Merge pull request #16 from realraum/openwrt-image
authorChristian Pointner <equinox@spreadspace.org>
Mon, 21 May 2018 00:11:06 +0000 (02:11 +0200)
committerGitHub <noreply@github.com>
Mon, 21 May 2018 00:11:06 +0000 (02:11 +0200)
Role for building OpenWrt images

16 files changed:
ansible/.gitignore
ansible/files/tuer/authorized_keys.sh [new file with mode: 0755]
ansible/files/tuer/post-receive [new file with mode: 0755]
ansible/files/tuer/update-keys-from-stdin.sh [new file with mode: 0644]
ansible/group_vars/openwrt/main.yml [new file with mode: 0644]
ansible/host_vars/torwaechter/main.yml [new file with mode: 0644]
ansible/hosts.ini
ansible/roles/openwrt-image/README.md [new file with mode: 0644]
ansible/roles/openwrt-image/defaults/main.yml [new file with mode: 0644]
ansible/roles/openwrt-image/group.j2 [new file with mode: 0644]
ansible/roles/openwrt-image/passwd.j2 [new file with mode: 0644]
ansible/roles/openwrt-image/tasks/fetch.yml [new file with mode: 0644]
ansible/roles/openwrt-image/tasks/main.yml [new file with mode: 0644]
ansible/roles/openwrt-image/tasks/prepare.yml [new file with mode: 0644]
ansible/roles/openwrt-image/uci.j2 [new file with mode: 0644]
ansible/tuer.yml [new file with mode: 0644]

index 808abb8..f525999 100644 (file)
@@ -4,3 +4,4 @@
 *.retry
 .*.sw?
 /.cache/
+/files/openwrt/
diff --git a/ansible/files/tuer/authorized_keys.sh b/ansible/files/tuer/authorized_keys.sh
new file mode 100755 (executable)
index 0000000..79ed2b5
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+# Copyright © 2018 nicoo <nicoo@realraum.at>
+# Distributed under the WTFPL v2
+#
+#         DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+#                     Version 2, December 2004
+#
+#  Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
+#
+#  Everyone is permitted to copy and distribute verbatim or modified
+#  copies of this license document, and changing it is allowed as long
+#  as the name is changed.
+#
+#             DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+#    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+#
+#   0. You just DO WHAT THE FUCK YOU WANT TO.
+
+# This script processes the tuergit repository located at KEYS_DIR
+# and outputs authorized_keys data for sshd.
+# It is meant to be used as an AuthorizedKeysCommand
+
+set -e
+
+KEYS_DIR=${KEYS_DIR:-'/home/tuergit/keys.git'}
+KEYS_OPTIONS='no-port-forwarding'
+
+cd "${KEYS_DIR}"
+if git config hooks.keys_branch 2>/dev/null; then
+    KEYS_BRANCH="$(git config hooks.keys_branch)"
+else
+    KEYS_BRANCH="master"
+fi
+
+git show "${KEYS_BRANCH}:ssh/" |
+    while read user; do
+        [ -n "$user" ] || continue
+        git show "${KEYS_BRANCH}:ssh/${user}" |
+            while read key; do
+                echo "command=\"${user}\",${KEYS_OPTIONS}" "${key}"
+            done
+    done
diff --git a/ansible/files/tuer/post-receive b/ansible/files/tuer/post-receive
new file mode 100755 (executable)
index 0000000..57f7b2c
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/sh
+## (c) Bernhard Tittelbach 2017-10-28, Tschunk-License
+## (c) nicoo               2018-05-20, Tschunk-License
+## this is the post-recieve hook installed in /home/tuergit/keys.git/hooks/
+
+set -eu
+
+keys_branch=$(git config hooks.keys_branch 2>/dev/null)
+keys_file=$(git config hooks.keys_file 2>/dev/null)
+keys_pipe_to=$(git config hooks.keys_pipe_to 2>/dev/null)
+
+keys_branch=${keys_branch:-master}
+keys_file=${keys_file:-keys}
+keys_pipe_to=${keys_pipe_to:-/usr/local/bin/update-keys-from-stdin.sh}
+
+changedcommits=$( grep "refs/heads/${keys_branch}" | sed 's/\([0-9a-f]\+\)\s\+\([0-9a-f]\+\)\s\+.*/\1..\2/;' )
+
+catgitkeyfile() {
+       git show "${keys_branch}:${keys_file}"
+}
+
+if git whatchanged --oneline $changedcommits | grep -qe "^:.*${keys_file}\$"; then
+  echo "./$keys_file changed in pushed commits. Updating keys in firmware!"
+  ## update door
+  catgitkeyfile | ${keys_pipe_to}
+else
+  echo "./$keys_file not changed, not updating keys in firmware"
+fi
diff --git a/ansible/files/tuer/update-keys-from-stdin.sh b/ansible/files/tuer/update-keys-from-stdin.sh
new file mode 100644 (file)
index 0000000..2a11eff
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/sh
+set -eu
+
+## this script takes keys on STDIN and programs teenstep eeprom
+
+MONIT_STOP="/etc/init.d/monit stop"
+MONIT_START="/etc/init.d/monit start"
+TUERDAEMON_STOP="/etc/init.d/tuer_core stop"
+TUERDAEMON_START="/etc/init.d/tuer_core start"
+UPDATE_KEYS_TOOL="/flash/tuer/update-keys /dev/door"
+
+## stop monit. it monit not installed or error. don't start monit again later
+${MONIT_STOP} || MONIT_START=""
+## stop door daemon.
+${TUERDAEMON_STOP}
+## give daemons time to stop
+sleep 1
+# pipe me keys to program plz
+${UPDATE_KEYS_TOOL}
+## start daemon again
+${TUERDAEMON_START}
+${MONIT_START}
+
diff --git a/ansible/group_vars/openwrt/main.yml b/ansible/group_vars/openwrt/main.yml
new file mode 100644 (file)
index 0000000..b93d046
--- /dev/null
@@ -0,0 +1,15 @@
+---
+openwrt_packages_remove:
+  - ppp
+  - ppp-mod-pppoe
+  - dnsmasq
+  - firewall
+  - odhcpd
+openwrt_packages_add:
+  - haveged
+  - htop
+  - hwclock
+  - ip
+  - less
+  - nano
+  - tcpdump
diff --git a/ansible/host_vars/torwaechter/main.yml b/ansible/host_vars/torwaechter/main.yml
new file mode 100644 (file)
index 0000000..86575c9
--- /dev/null
@@ -0,0 +1,127 @@
+---
+openwrt_arch: x86
+openwrt_target: geode
+openwrt_output_image_suffixes:
+  - combined-ext4.img.gz
+  - combined-squashfs.img
+
+openwrt_packages_extra:
+  - "-dropbear"
+  - flashrom
+  - git
+  - kmod-usb-acm
+  - openssh-server
+  - openssh-sftp-server
+  - screen
+  - sudo
+  - usbutils
+
+openwrt_mixin:
+  # Go binaries
+  /usr/local/bin/door_client:
+    mode: '0755'
+    file: "{{ playbook_dir }}/.cache/openwrt/tuer/door_and_sensors/door_client/door_client"
+  /usr/local/bin/door_daemon:
+    mode: '0755'
+    file: "{{ playbook_dir }}/.cache/openwrt/tuer/door_and_sensors/door_daemon/door_daemon"
+  /usr/local/bin/update-keys:
+    mode: '0755'
+    file: "{{ playbook_dir }}/.cache/openwrt/tuer/door_and_sensors/update-keys/update-keys"
+
+  /usr/local/bin/authorized_keys.sh:
+    mode: '0755'
+    file: "{{ playbook_dir }}/files/tuer/authorized_keys.sh"
+
+  /usr/local/bin/update-keys-from-stdin.sh:
+    mode: '0755'
+    file: "{{ playbook_dir }}/files/tuer/update-keys-from-stdin.sh"
+
+  /etc/ssh/sshd_config:
+    content: |
+      Port 22000
+
+      AllowUsers root tuerctl tuergit
+      AuthenticationMethods publickey
+      AuthorizedKeysFile /etc/ssh/authorized_keys.d/%u
+
+      AllowAgentForwarding no
+      AllowTcpForwarding no
+      X11Forwarding no
+      UsePrivilegeSeparation sandbox
+
+      Subsystem sftp /usr/libexec/sftp-server
+
+      Match User tuerctl
+        AuthorizedKeysFile /dev/null
+        AuthorizedKeysCommand /usr/local/bin/authorized_keys.sh
+        AuthorizedKeysCommandUser tuergit
+
+  /etc/ssh/authorized_keys.d/root:
+    content: |-
+      {% for key in noc_ssh_keys %}
+      {{ key }}
+      {% endfor %}
+
+  /etc/ssh/authorized_keys.d/tuergit:
+    content: |-
+      {% for key in noc_ssh_keys %}
+      {{ key }}
+      {% endfor %}
+
+openwrt_uci:
+  system:
+    - name: system
+      options:
+        hostname: '{{ inventory_hostname }}'
+        timezone: 'CET-1CEST,M3.5.0,M10.5.0/3'
+        ttylogin: '0'
+        log_size: '64'
+        urandom_seed: '0'
+
+    - name: timeserver 'ntp'
+      options:
+        enabled: '1'
+        enable_server: '0'
+        server:
+          - '0.lede.pool.ntp.org'
+          - '1.lede.pool.ntp.org'
+          - '2.lede.pool.ntp.org'
+          - '3.lede.pool.ntp.org'
+
+  network:
+    - name: globals 'globals'
+      options:
+        ula_prefix: fdc9:e01f:83db::/48
+
+    - name: interface 'loopback'
+      options:
+        ifname: lo
+        proto: static
+        ipaddr: 127.0.0.1
+        netmask: 255.0.0.0
+
+    - name: interface 'lan'
+      options:
+        ifname: eth0
+        accept_ra: 0
+        proto: static
+        ipaddr: 192.168.33.7
+        netmask: 255.255.255.0
+        gateway: 192.168.33.1
+        dns: 192.168.33.1
+        dns_search: realraum.at
+
+
+openwrt_mounts:
+  - path: /run
+    src: none
+    fstype: tmpfs
+    opts: nosuid,nodev,noexec,noatime
+
+openwrt_users:
+  tuerd: {}
+  tuergit:
+    home:  /home/tuergit
+    shell: /usr/bin/git-shell
+  tuerctl:
+    shell: /bin/false # TODO fixme
index 519fe6a..d67f69e 100644 (file)
@@ -4,7 +4,7 @@ ansible_host={{ inventory_hostname }}.{{ host_domain }}
 ansible_user=root
 
 [net-zone-mgmt]
-#torwaechter
+torwaechter
 alfred
 calendar
 galley
@@ -48,6 +48,13 @@ virtualservers
 wuerfel
 
 
+[openwrt]
+torwaechter
+
+[openwrt:vars]
+localconfig_ssh_config_user=root
+
+
 #[alix]
 #gw
 #torwaechter
diff --git a/ansible/roles/openwrt-image/README.md b/ansible/roles/openwrt-image/README.md
new file mode 100644 (file)
index 0000000..d56affe
--- /dev/null
@@ -0,0 +1,3 @@
+# Build OpenWRT images with Ansible
+
+## Configuration
diff --git a/ansible/roles/openwrt-image/defaults/main.yml b/ansible/roles/openwrt-image/defaults/main.yml
new file mode 100644 (file)
index 0000000..92932fc
--- /dev/null
@@ -0,0 +1,17 @@
+---
+openwrt_variant: lede
+openwrt_release: 17.01.4
+openwrt_download_dir: .cache/openwrt
+openwrt_tarball_basename: "{{ openwrt_variant }}-imagebuilder-{{ openwrt_release }}-{{ openwrt_arch }}{% if openwrt_target != 'generic' %}-{{ openwrt_target }}{% endif %}.Linux-x86_64"
+openwrt_tarball_name: "{{ openwrt_tarball_basename }}.tar.xz"
+openwrt_target: generic
+
+openwrt_output_dir: files/openwrt/{{ inventory_hostname }}
+openwrt_output_image_name_base: "{{ openwrt_variant }}-{{ openwrt_release }}-{{ openwrt_arch }}{% if openwrt_target != 'generic' %}-{{ openwrt_target }}{% endif %}"
+openwrt_output_image_suffixes:
+  - squashfs-sysupgrade.bin
+  - squashfs-factory.bin
+
+openwrt_packages_remove: []
+openwrt_packages_add: []
+openwrt_packages_extra: []
diff --git a/ansible/roles/openwrt-image/group.j2 b/ansible/roles/openwrt-image/group.j2
new file mode 100644 (file)
index 0000000..cb433b8
--- /dev/null
@@ -0,0 +1,21 @@
+{{ ansible_managed | comment }}
+root:x:0:
+daemon:x:1:
+adm:x:4:
+mail:x:8:
+audio:x:29:
+www-data:x:33:
+ftp:x:55:
+users:x:100:
+network:x:101:
+{% for name, opt in openwrt_users.items() %}
+{% if 'group_id' not in opt %}
+{{ name }}:x:{{ opt.id | default(loop.index + 110) }}:
+{% endif %}
+{% endfor %}
+{% if openwrt_groups is defined %}
+{% for name, opt in openwrt_groups.items() %}
+{{ name }}:x:{{ opt.id | default(loop.index + 200) }}:
+{% endfor %}
+{% endif %}
+nogroup:x:65534:
diff --git a/ansible/roles/openwrt-image/passwd.j2 b/ansible/roles/openwrt-image/passwd.j2
new file mode 100644 (file)
index 0000000..9beaeb6
--- /dev/null
@@ -0,0 +1,9 @@
+{{ ansible_managed | comment }}
+root:x:0:0:root:/root:/bin/ash
+daemon:*:1:1:daemon:/var:/bin/false
+ftp:*:55:55:ftp:/home/ftp:/bin/false
+network:*:101:101:network:/var:/bin/false
+{% for name, opt in openwrt_users.items() %}
+{{ name }}:*:{{ opt.id | default(loop.index + 110) }}:{{ opt.gid | default(loop.index + 110) }}:{{ name }}:{{ opt.home | default('/nonexistent') }}:{{ opt.shell | default('/bin/false') }}
+{% endfor %}
+nobody:*:65534:65534:nobody:/var:/bin/false
diff --git a/ansible/roles/openwrt-image/tasks/fetch.yml b/ansible/roles/openwrt-image/tasks/fetch.yml
new file mode 100644 (file)
index 0000000..4b5b1c8
--- /dev/null
@@ -0,0 +1,48 @@
+---
+- name: Create download directory
+  file:
+    dest: "{{ openwrt_download_dir }}"
+    state: directory
+
+- block:
+    - name: Generate OpenWrt download URLs
+      set_fact:
+        openwrt_url:
+          https://downloads.openwrt.org/releases/{{ openwrt_release }}/targets/{{ openwrt_arch | mandatory }}/{{ openwrt_target }}
+
+    - name: Download sha256sums
+      get_url:
+        url: "{{ openwrt_url }}/sha256sums"
+        dest: "{{ openwrt_download_dir }}/{{ openwrt_tarball_basename }}.sha256"
+
+    - name: Download sha256sums.asc
+      get_url:
+        url: "{{ openwrt_url }}/sha256sums.asc"
+        dest: "{{ openwrt_download_dir }}/{{ openwrt_tarball_basename }}.sha256.asc"
+
+    - name: Check OpenPGP signature
+      command: gpg --verify "{{ openwrt_download_dir }}/{{ openwrt_tarball_basename }}.sha256.asc"
+      changed_when: False
+
+    - name: Extract SHA256 hash of the imagebuilder archive
+      command: grep '{{ openwrt_tarball_name }}' "{{ openwrt_download_dir }}/{{ openwrt_tarball_basename }}.sha256"
+      register: sha256
+      changed_when: False
+
+    - name: Download imagebuilder
+      get_url:
+        url: "{{ openwrt_url }}/{{ openwrt_tarball_name }}"
+        dest: "{{ openwrt_download_dir }}/{{ openwrt_tarball_name }}"
+        checksum: sha256:{{ sha256.stdout.split(' ') | first }}
+
+  rescue:
+    - name: Delete downloaded artifacts
+      file:
+        path: "{{ item }}"
+        state: absent
+      with_items:
+        - "{{ openwrt_download_dir }}/{{ openwrt_tarball_basename }}.sha256"
+        - "{{ openwrt_download_dir }}/{{ openwrt_tarball_basename }}.sha256.asc"
+        - "{{ openwrt_download_dir }}/{{ openwrt_tarball_name }}"
+    - fail:
+        msg: Something borked
diff --git a/ansible/roles/openwrt-image/tasks/main.yml b/ansible/roles/openwrt-image/tasks/main.yml
new file mode 100644 (file)
index 0000000..5f9cc52
--- /dev/null
@@ -0,0 +1,36 @@
+---
+- include: fetch.yml
+  when: openwrt_imgbuilder_tarball is not defined
+
+- block:
+    - include: prepare.yml
+
+    - name: Create the output directory for built images
+      file:
+        path: "{{ openwrt_output_dir }}"
+        state: directory
+
+    - set_fact:
+        openwrt_packages: >-
+          {{ openwrt_packages_remove | map('regex_replace', '^', '-') | join(' ') }}
+          {{ openwrt_packages_add   | join(' ') }}
+          {{ openwrt_packages_extra | join(' ') }}
+
+    - name: Build the OpenWrt image
+      command: >-
+        make -C {{ openwrt_imgbuilder_dir }}/{{ openwrt_tarball_basename }} image
+          FILES="{{ openwrt_imgbuilder_files }}"
+          PACKAGES="{{ openwrt_packages }}"
+          {% if openwrt_extra_name is defined %} EXTRA_IMAGE_NAME="{{ openwrt_extra_name }}" {% endif %}
+
+    - name: Copy newly built OpenWrt image
+      with_items: "{{ openwrt_output_image_suffixes }}"
+      copy:
+        src: "{{ openwrt_imgbuilder_dir }}/{{ openwrt_tarball_basename }}/bin/targets/{{ openwrt_arch }}/{{ openwrt_target }}/{{ openwrt_output_image_name_base }}-{{ item }}"
+        dest: "{{ openwrt_output_dir }}"
+
+  always:
+    - name: Delete the temporary build directory
+      file:
+        path: "{{ openwrt_imgbuilder_dir }}"
+        state: absent
diff --git a/ansible/roles/openwrt-image/tasks/prepare.yml b/ansible/roles/openwrt-image/tasks/prepare.yml
new file mode 100644 (file)
index 0000000..b0847ec
--- /dev/null
@@ -0,0 +1,94 @@
+---
+- name: Create temporary build directory
+  command: mktemp --tmpdir -d openwrt-{{ inventory_hostname }}.XXXXXX
+  register: tmpdir
+
+- set_fact:
+    openwrt_imgbuilder_dir:   "{{ tmpdir.stdout }}"
+    openwrt_imgbuilder_files: "{{ tmpdir.stdout }}/files"
+
+- name: Create the directories for mixins
+  file:
+    path: "{{ item }}"
+    state: directory
+    mode: '0755'
+  with_items:
+    - "{{ openwrt_download_dir }}/dl"
+    - "{{ openwrt_imgbuilder_files }}/etc/config"
+    - "{{ openwrt_mixin | map('dirname') | map('regex_replace', '^', openwrt_imgbuilder_files) | unique | list }}"
+
+
+- name: Copy mixins in place [1/2]
+  copy:
+    src: "{{ item.value.file }}"
+    dest: "{{ openwrt_imgbuilder_files }}/{{ item.key }}"
+    mode: "{{ item.value.mode | default('0644') }}"
+  with_dict: "{{ openwrt_mixin }}"
+  when: '"file" in item.value'
+  loop_control:
+    label: "{{ item.key }}"
+
+- name: Copy mixins in place [2/2]
+  copy:
+    content: "{{ item.value.content }}"
+    dest: "{{ openwrt_imgbuilder_files }}/{{ item.key }}"
+    mode: "{{ item.value.mode | default('0644') }}"
+  with_dict: "{{ openwrt_mixin }}"
+  when: '"content" in item.value'
+  loop_control:
+    label: "{{ item.key }}"
+
+- name: Generate /etc/fstab
+  mount:
+    fstab: "{{ openwrt_imgbuilder_files }}/etc/fstab"
+    state: present
+  args:
+    "{{ item }}"
+  when: openwrt_mounts is defined
+  with_items: "{{ openwrt_mounts }}"
+  loop_control:
+    label: "{{ item.path }}"
+
+- name: Create UCI configuration files
+  template:
+    src: uci.j2
+    dest: "{{ openwrt_imgbuilder_files }}/etc/config/{{ item.key }}"
+    mode: 0644
+    trim_blocks: yes
+#   force: no  ## TODO: fail when overwriting a file
+  with_dict: "{{ openwrt_uci }}"
+  loop_control:
+    label: "{{ item.key }}"
+
+- name: Create /etc/passwd
+  template:
+    src: passwd.j2
+    dest: "{{ openwrt_imgbuilder_files }}/etc/passwd"
+    mode: 0644
+    trim_blocks: yes
+  when: openwrt_users is defined
+
+- name: Create /etc/group
+  template:
+    src: group.j2
+    dest: "{{ openwrt_imgbuilder_files }}/etc/group"
+    mode: 0644
+    trim_blocks: yes
+  when: openwrt_groups is defined or openwrt_users is defined
+
+### TODO: this just hangs?
+# - unarchive:
+#     copy: False
+#     src:  "{{ openwrt_download_dir }}/{{ openwrt_tarball_name }}"
+#     dest: "{{ openwrt_imgbuilder_dir }}"
+
+- name: Decompress the OpenWrt image builder
+  command: >-
+    tar -xf  "{{ openwrt_download_dir }}/{{ openwrt_tarball_name }}"
+    -C   "{{ openwrt_imgbuilder_dir     }}"
+
+- name: Symlink the cache repository
+  file:
+    state: link
+    src: "{{ playbook_dir }}/{{ openwrt_download_dir }}/dl"
+    path: "{{ openwrt_imgbuilder_dir }}/{{ openwrt_tarball_basename }}/dl"
diff --git a/ansible/roles/openwrt-image/uci.j2 b/ansible/roles/openwrt-image/uci.j2
new file mode 100644 (file)
index 0000000..3cc480b
--- /dev/null
@@ -0,0 +1,15 @@
+{{ ansible_managed | comment }}
+
+{% for section in item.value %}
+config {{ section.name }}
+{% for option, value in section.options.items() %}
+{%   if value is iterable and value is not string %}
+{%      for v in value %}
+  list {{ option }} '{{ v }}'
+{%      endfor %}
+{%   else %}
+  option {{ option }} '{{ value }}'
+{%   endif %}
+{% endfor %}
+
+{% endfor %}
diff --git a/ansible/tuer.yml b/ansible/tuer.yml
new file mode 100644 (file)
index 0000000..0d26eb3
--- /dev/null
@@ -0,0 +1,42 @@
+---
+- hosts: torwaechter
+  connection: local
+  pre_tasks:
+    - name: Create go directories
+      file:
+        path: .cache/openwrt/tuer/{{ item }}
+        state: directory
+      with_items: [ gopath, gocache ]
+
+    - name: Clone necessary git repositories
+      git:
+        repo: https://github.com/realraum/{{ item }}.git
+        dest: .cache/openwrt/tuer/{{ item }}
+        update: True
+      with_items: [ door_and_sensors ]
+
+    - name: Download dependencies
+      command: go get -d ./...
+      args:
+        chdir: .cache/openwrt/tuer/door_and_sensors/{{ item }}
+      environment:
+        GOCACHE: "{{ playbook_dir }}/.cache/openwrt/tuer/gocache"
+        GOPATH:  "{{ playbook_dir }}/.cache/openwrt/tuer/gopath"
+      with_items: [ door_client, door_daemon, update-keys ]
+
+    - name: Cross-compile Go binaries
+      command: go build -ldflags "-s"
+      args:
+        chdir: .cache/openwrt/tuer/door_and_sensors/{{ item }}
+      environment:
+        GOCACHE: "{{ playbook_dir }}/.cache/openwrt/tuer/gocache"
+        GOPATH:  "{{ playbook_dir }}/.cache/openwrt/tuer/gopath"
+        GO386: 387
+        CGO_ENABLED: 0
+        GOOS: linux
+        GOARCH: 386
+      with_items: [ door_client, door_daemon, update-keys ]
+
+  roles:
+    - role: openwrt-image
+      delegate_to: localhost