[ansible] on-fire-within setup
change
commit ddfe75225be2b79121fb3f0b5655f5051e31b666
author Alpha Chen <alpha@kejadlen.dev>
date
parent 02e5f4a9
diff --git a/ansible/.gitignore b/ansible/.gitignore
new file mode 100644
index 00000000..fe756772
--- /dev/null
+++ b/ansible/.gitignore
@@ -0,0 +1,2 @@
+hosts.yml
+host_vars
diff --git a/ansible/README.md b/ansible/README.md
new file mode 100644
index 00000000..362fa086
--- /dev/null
+++ b/ansible/README.md
@@ -0,0 +1,26 @@
+# ansible
+
+## Raspberry Pi
+
+1. [Download Raspberian Stretch Lite](https://www.raspberrypi.org/downloads/raspbian/)
+1. Install Etcher: `brew cask install balenaetcher`
+1. [Flash the SD card](https://www.raspberrypi.org/documentation/installation/installing-images/README.md)
+
+1. [Enable SSH](https://www.raspberrypi.org/documentation/remote-access/ssh/)
+
+1. Run the Ansible playbooks
+  1. Add the host to the `pi_bootstrap` group and set the `password` variable
+     on the host
+  1. Connect to the host via SSH (so Ansible can piggyback off the existing
+     connection and not have to deal with password shenanigans)
+  1. `ansible-playbook playbooks/pi/bootstrap.yml`
+  1. `ansible-playbook {{ host }}/main.yml`
+
+### etc
+
+1. Use `raspi-config` to set up the WiFi
+  - [Additional setup](https://www.raspberrypi.org/documentation/configuration/wireless/wireless-cli.md)
+  - Add `scan_ssid=1` to `/etc/wpa_supplicant/wpa_supplicant.conf`
+  - Restart WiFi: `wpa_cli -i wlan0 reconfigure`
+1. Use `raspi-config` to set the keyboard layout
+
diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg
index 37366f16..d2e82a16 100644
--- a/ansible/ansible.cfg
+++ b/ansible/ansible.cfg
@@ -1,5 +1,5 @@
 [defaults]
-inventory = hosts.private
+inventory = hosts.yml
 retry_files_enabled = False
 pipelining = True
 host_key_checking = False
diff --git a/ansible/ddclient/Dockerfile b/ansible/ddclient/Dockerfile
new file mode 100644
index 00000000..44d5a1da
--- /dev/null
+++ b/ansible/ddclient/Dockerfile
@@ -0,0 +1,22 @@
+FROM armhf/alpine
+
+# https://wiki.alpinelinux.org/wiki/Ddclient
+
+RUN apk update && apk upgrade && \
+    apk add \
+        curl \
+        make \
+        perl \
+        perl-io-socket-ssl
+RUN cpan install Test::Requires && cpan Data::Validate::IP
+
+RUN cd tmp && \
+    curl --location https://github.com/ddclient/ddclient/archive/v3.9.0.tar.gz > ddclient-3.9.0.tar.gz && \
+    tar xvz < ddclient-3.9.0.tar.gz && \
+    cp ddclient-3.9.0/ddclient /usr/sbin && \
+    mkdir /etc/ddclient && \
+    mkdir -p /var/cache/ddclient
+
+VOLUME /etc/ddclient
+
+ENTRYPOINT [ "ddclient", "-foreground" ]
diff --git a/ansible/on-fire-within/README.md b/ansible/on-fire-within/README.md
new file mode 100644
index 00000000..31c91bf0
--- /dev/null
+++ b/ansible/on-fire-within/README.md
@@ -0,0 +1,16 @@
+# On Fire Within
+
+## Setup
+
+- [Installing Hass.io](https://www.home-assistant.io/hassio/installation/)
+
+1. `ansible-playbook playbooks/pi/bootstrap.yml`
+1. `ansible-playbook on-fire-within/bootstrap.yml`
+1. `curl -fsSL get.docker.com | sh`
+  - `sudo usermod -aG docker alpha`
+1. `curl -sL "https://raw.githubusercontent.com/home-assistant/hassio-installer/master/hassio_install.sh" | bash -s -- -m raspberrypi4`
+1. `ansible-playbook on-fire-within/main.yml`
+
+## Notes
+
+- `/usr/share/hassio`
diff --git a/ansible/on-fire-within/hass-io.yml b/ansible/on-fire-within/hass-io.yml
new file mode 100644
index 00000000..30070c19
--- /dev/null
+++ b/ansible/on-fire-within/hass-io.yml
@@ -0,0 +1,29 @@
+# https://www.home-assistant.io/hassio/installation/
+
+- hosts: on_fire_within
+  become: yes
+  tasks:
+    # Forgot what I need this for...
+    - name: install software-properties-common
+      apt: name=software-properties-common
+
+    - name: install other dependencies for hass.io
+      apt:
+        name:
+          - apparmor-utils
+          - apt-transport-https
+          - avahi-daemon
+          - ca-certificates
+          - curl
+          - dbus
+          - jq
+          - network-manager
+          - socat
+        update_cache: yes
+    - service:
+        name: ModemManager
+        enabled: false
+
+    # homekit
+    - name: install dependenies for homekit
+      apt: name=libavahi-compat-libdnssd-dev
diff --git a/ansible/on-fire-within/main.yml b/ansible/on-fire-within/main.yml
new file mode 100644
index 00000000..774ac079
--- /dev/null
+++ b/ansible/on-fire-within/main.yml
@@ -0,0 +1,166 @@
+- import_playbook: ../playbooks/pi/security.yml
+- import_playbook: hass-io.yml
+
+- hosts: on_fire_within
+  become: yes
+  tasks:
+    - name: install dependencies
+      apt:
+        name:
+          - git
+          - vim
+
+          # Needed for Docker stuff
+          - docker-compose
+          - python-pip
+          - python-backports-shutil-get-terminal-size
+          - python-backports.ssl-match-hostname
+
+    - name: install python docker packages
+      pip:
+        name:
+          - docker
+          - docker-compose
+        state: latest
+
+    - name: create conf dirs
+      file:
+        path: "{{ item }}"
+        state: directory
+      with_items:
+        - /etc/ddclient
+        - /etc/traefik
+
+    - name: configure ddclient
+      copy:
+        content: |
+          daemon=300
+
+          use=web
+          ssl=yes
+          protocol=googledomains
+
+          {% for host in hosts %}
+          login={{ host.login }}, password={{ host.password }} {{ host.host }}
+          {% endfor %}
+        dest: /etc/ddclient/ddclient.conf
+        mode: 0600
+      vars:
+        hosts: "{{ ddclient.hosts }}"
+      notify: restart ddclient
+
+    - name: traefik static configuration
+      copy:
+        content: |
+          [providers]
+            [providers.docker]
+              exposedByDefault = false
+            [providers.file]
+              filename = "/etc/traefik/dynamic_conf.toml"
+              watch = true
+
+          [entryPoints]
+            [entryPoints.http]
+              address = ":80"
+
+            [entryPoints.https]
+              address = ":443"
+
+          [certificatesResolvers.le.acme]
+            email = "{{ email }}"
+            storage = "/etc/traefik/acme.json"
+            [certificatesResolvers.le.acme.httpChallenge]
+              entryPoint = "http"
+
+          [api]
+            insecure = true
+        dest: /etc/traefik/traefik.toml
+        mode: 0600
+
+    - name: docker all the things!
+      docker_compose:
+        project_name: on-fire-within
+        definition:
+          version: '2'
+          services:
+            ddclient:
+              image: kejadlen/ddclient:latest
+              container_name: ddclient
+              volumes:
+                - /etc/ddclient:/etc/ddclient
+              restart: always
+            pihole:
+              image: pihole/pihole:latest
+              container_name: pihole
+              ports:
+                - 53:53/tcp
+                - 53:53/udp
+              environment:
+                TZ: America/Los_Angeles
+                VIRTUAL_HOST: "{{ pihole.host }}"
+                WEBPASSWORD: "{{ pihole.password }}"
+              volumes:
+                - /etc/pihole:/etc/pihole
+                - /etc/dnsmasq.d:/etc/dnsmasq.d
+              dns:
+                - 127.0.0.1
+                - 1.1.1.1
+              labels:
+                - traefik.enable=true
+                - traefik.http.routers.pihole.rule=Host(`{{ traefik.host_rules.pihole }}`)
+                - traefik.http.routers.pihole.tls=true
+                - traefik.http.routers.pihole.tls.certresolver=le
+                - traefik.http.services.pihole.loadbalancer.server.port=80
+              restart: always
+            traefik:
+              image: traefik:latest
+              container_name: traefik
+              ports:
+                - 80:80
+                - 8080:8080
+                - 443:443
+              volumes:
+                - /var/run/docker.sock:/var/run/docker.sock
+                - /etc/traefik:/etc/traefik
+              labels:
+                - traefik.enable=true
+                - traefik.http.middlewares.auth.basicauth.users=alpha:{{ traefik.password | password_hash("md5") | replace("$", "$$") }}
+                - traefik.http.routers.traefik.rule=Host(`{{ traefik.host_rules.traefik }}`)
+                - traefik.http.routers.traefik.tls=true
+                - traefik.http.routers.traefik.tls.certresolver=le
+                - traefik.http.routers.traefik.middlewares=auth
+                - traefik.http.routers.traefik.service=api@internal
+              restart: always
+
+    - name: get docker ip
+      block:
+        # - shell: ip -4 addr show docker0 | grep -Po 'inet \K[\d.]+' | head -n 1
+        - shell: docker network inspect on-fire-within_default | jq --raw-output .[0].IPAM.Config[0].Gateway
+          register: docker_ip_result
+        - set_fact:
+            docker_ip: "{{ docker_ip_result.stdout | trim }}"
+
+    - name: traefik dynamic configuration
+      copy:
+        content: |
+          [http.routers]
+            [http.routers.hassio]
+              rule = "Host(`{{ traefik.host_rules.hassio }}`)"
+              service = "hassio"
+              [http.routers.hassio.tls]
+                certResolver = "le"
+
+          [http.services]
+            [http.services.hassio.loadBalancer]
+              [[http.services.hassio.loadBalancer.servers]]
+                url = "http://{{ docker_ip }}:8123/"
+        dest: /etc/traefik/dynamic_conf.toml
+        mode: 0600
+
+  handlers:
+    - name: restart ddclient
+      docker_container:
+        name: ddclient
+        restart: yes
+      ignore_errors: yes
+
diff --git a/ansible/playbooks/bootstrap.yml b/ansible/playbooks/bootstrap.yml
deleted file mode 100644
index 86d529a1..00000000
--- a/ansible/playbooks/bootstrap.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-- hosts: linux
-  become: yes
-  tasks:
-
-    - name: create user
-      user:
-        name: alpha
-        password: "{{ password | password_hash('sha512') }}"
-        shell: /bin/bash
-
-    - name: give user sudo access
-      lineinfile:
-        path: /etc/sudoers.d/alpha
-        line: "alpha ALL=(ALL) NOPASSWD: ALL"
-        create: yes
-        validate: visudo -cf %s
-
-    - name: add ssh keys
-      authorized_key:
-        user: alpha
-        key: https://github.com/kejadlen.keys
-
diff --git a/ansible/playbooks/pi/bootstrap.yml b/ansible/playbooks/pi/bootstrap.yml
new file mode 100644
index 00000000..5b0b5739
--- /dev/null
+++ b/ansible/playbooks/pi/bootstrap.yml
@@ -0,0 +1,44 @@
+- hosts: pi_bootstrap
+  become: yes
+  tasks:
+
+    - name: change hostname to {{ hostname }}
+      hostname:
+        name: "{{ hostname }}"
+      notify: reboot
+
+    - name: update hostname in /etc/hosts
+      replace:
+        path: /etc/hosts
+        regexp: '(\s+)raspberrypi(\s+.*)?$'
+        replace: '\1{{ hostname }}\2'
+      notify: reboot
+
+    - name: disable wifi
+      lineinfile:
+        path: /boot/config.txt
+        insertafter: dtoverlay
+        line: dtoverlay=pi3-disable-wifi
+      notify: reboot
+
+    - name: create user
+      user:
+        name: alpha
+        password: "{{ password | password_hash('sha512') }}"
+        shell: /bin/bash
+
+    - name: give user sudo access
+      lineinfile:
+        path: /etc/sudoers.d/alpha
+        line: "alpha ALL=(ALL) NOPASSWD: ALL"
+        create: yes
+        validate: visudo -cf %s
+
+    - name: add ssh keys
+      authorized_key:
+        user: alpha
+        key: https://github.com/kejadlen.keys
+
+  handlers:
+    - name: reboot
+      reboot:
diff --git a/ansible/playbooks/pi/security.yml b/ansible/playbooks/pi/security.yml
new file mode 100644
index 00000000..76d1e31e
--- /dev/null
+++ b/ansible/playbooks/pi/security.yml
@@ -0,0 +1,44 @@
+# https://www.raspberrypi.org/documentation/configuration/security.md
+
+- hosts: pi
+  become: yes
+  tasks:
+    - name: disable ssh password logins
+      lineinfile:
+        path: /etc/ssh/sshd_config
+        regexp: '^(#\s*)?{{ item }} '
+        line: "{{ item }} no"
+      notify: reload ssh
+      with_items:
+        - ChallengeResponseAuthentication
+        - PasswordAuthentication
+        - UsePAM
+
+    - name: disable pi user
+      user:
+        name: pi
+        password: !
+
+    - name: install fail2ban
+      package:
+        name: fail2ban
+        state: present
+
+    - name: create jail.local
+      copy:
+        content: |
+          [sshd]
+          enabled = true
+        dest: /etc/fail2ban/jail.local
+      notify: reload fail2ban
+
+  handlers:
+    - name: reload ssh
+      service:
+        name: ssh
+        state: reloaded
+
+    - name: reload fail2ban
+      service:
+        name: fail2ban
+        state: reloaded