[ansible][attitude-adjuster]
change
commit 5a0cd902df12d4d85b67225a21c0dd42f270f2da
author Alpha Chen <alpha@kejadlen.dev>
date
parent 1b3dbcc0
diff --git a/ansible/attitude-adjuster/Dockerfile.ddclient b/ansible/attitude-adjuster/Dockerfile.ddclient
new file mode 100644
index 00000000..4427102e
--- /dev/null
+++ b/ansible/attitude-adjuster/Dockerfile.ddclient
@@ -0,0 +1,44 @@
+# vim: ft=dockerfile
+FROM armhf/alpine
+
+# https://wiki.alpinelinux.org/wiki/Ddclient
+
+RUN apk update && apk upgrade && \
+    apk add \
+        curl \
+        make \
+        perl \
+        # perl-utils \
+        # perl-test-taint \
+        # perl-netaddr-ip \
+        # perl-net-ip \
+        # perl-yaml \
+        # perl-log-log4perl \
+        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
+
+# RUN apk del --purge \
+#         build-dependencies && \
+#     rm -rf /tmp/*
+
+# RUN apt-get update -y && \
+#     apt-get install -yq curl libdata-validate-ip-perl libio-socket-ip-perl libio-socket-ssl-perl libjson-any-perl perl && \
+#     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 && \
+#     rm ddclient-3.9.0.tar.gz && \
+#     cp ddclient-3.9.0/ddclient /usr/sbin && \
+#     rm -rf ddclient-3.9.0 && \
+#     mkdir /etc/ddclient && \
+#     mkdir -p /var/cache/ddclient
+
+VOLUME /etc/ddclient
+
+ENTRYPOINT [ "ddclient", "-daemon", "300", "-foreground", "-verbose" ]
diff --git a/ansible/attitude-adjuster/README.md b/ansible/attitude-adjuster/README.md
index a29b8767..1c08245a 100644
--- a/ansible/attitude-adjuster/README.md
+++ b/ansible/attitude-adjuster/README.md
@@ -12,9 +12,6 @@
   - `ansible-playbook --extra-vars "ansible_user=pi" --ask-pass attitude-adjuster/bootstrap.yml`
   - `ansible-playbook attitude-adjuster/main.yml`
 
-1. Install Pi-hole
-  - `curl -sSL https://install.pi-hole.net | bash`
-
 ## Optional
 
 1. Use `raspi-config` to set up the WiFi
diff --git a/ansible/attitude-adjuster/config.json b/ansible/attitude-adjuster/config.json
deleted file mode 100644
index e56d0cd2..00000000
--- a/ansible/attitude-adjuster/config.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-  "bridge": {
-    "name": "Homebridge",
-    "username": "67:9E:AC:02:F4:51",
-    "port": 59377,
-    "pin": "617-05-928"
-  },
-  "platforms": [
-    {
-      "platform": "SmartThings",
-      "name": "SmartThings",
-      "app_url": "https://graph-na04-useast2.api.smartthings.com:443/api/smartapps/installations/",
-      "access_token": "d03f2c6e-36e4-4069-8b7a-a2fcb30c1d68",
-      "app_id": "d0549bc7-23cc-4355-b261-6bfe9e5e48b6"
-    }
-  ]
-}
diff --git a/ansible/attitude-adjuster/ddclient.conf b/ansible/attitude-adjuster/ddclient.conf
deleted file mode 100644
index 9cd553d8..00000000
--- a/ansible/attitude-adjuster/ddclient.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-use=web
-ssl=yes
-protocol=googledomains
-login={{ ddclient_login }}
-password={{ ddclient_password }}
-{{ ddclient_host }}
diff --git a/ansible/attitude-adjuster/ddclient.yml b/ansible/attitude-adjuster/ddclient.yml
new file mode 100644
index 00000000..fc11b0f4
--- /dev/null
+++ b/ansible/attitude-adjuster/ddclient.yml
@@ -0,0 +1,66 @@
+- hosts: attitude-adjuster
+  vars_files:
+    - vars.yml.private
+  become: yes
+  tasks:
+
+    - name: configure ddclient
+      copy:
+        content: |
+          use=web
+          ssl=yes
+          protocol=googledomains
+          login={{ home.ddclient.login }}
+          password={{ home.ddclient.password }}
+          {{ home.host }}
+
+          use=web
+          ssl=yes
+          protocol=googledomains
+          login={{ home_assistant.ddclient.login }}
+          password={{ home_assistant.ddclient.password }}
+          {{ home_assistant.host }}
+
+          use=web
+          ssl=yes
+          protocol=googledomains
+          login={{ homebridge.ddclient.login }}
+          password={{ homebridge.ddclient.password }}
+          {{ homebridge.host }}
+
+          use=web
+          ssl=yes
+          protocol=googledomains
+          login={{ pihole.ddclient.login }}
+          password={{ pihole.ddclient.password }}
+          {{ pihole.host }}
+
+          use=web
+          ssl=yes
+          protocol=googledomains
+          login={{ traefik.ddclient.login }}
+          password={{ traefik.ddclient.password }}
+          {{ traefik.host }}
+        dest: /etc/ddclient/ddclient.conf
+        mode: 0600
+      notify: restart ddclient
+
+    - name: build docker image
+      docker_image:
+        build:
+          path: .
+          dockerfile: Dockerfile.ddclient
+          pull: yes
+        name: kejadlen/ddclient
+        repository: kejadlen/ddclient
+        push: yes
+        source: build
+
+  handlers:
+
+    - name: restart ddclient
+      docker_container:
+        name: ddclient
+        restart: yes
+      ignore_errors: yes
+
diff --git a/ansible/attitude-adjuster/docker.yml b/ansible/attitude-adjuster/docker.yml
new file mode 100644
index 00000000..28cd18b3
--- /dev/null
+++ b/ansible/attitude-adjuster/docker.yml
@@ -0,0 +1,142 @@
+# https://docs.docker.com/install/linux/docker-ce/debian/#install-using-the-repository
+
+- hosts: attitude-adjuster
+  vars_files:
+    - vars.yml.private
+  become: yes
+  tasks:
+
+    - name: install dependencies
+      package:
+        name: "{{ item }}"
+        state: present
+      with_items:
+        - apt-transport-https
+        - ca-certificates
+        - gnupg2
+
+    - name: add docker apt key
+      apt_key:
+        url: https://download.docker.com/linux/debian/gpg
+
+    # https://github.com/moby/moby/issues/31405#issuecomment-501494874
+    - name: add docker apt repository
+      apt_repository:
+        repo: deb [arch=armhf] https://download.docker.com/linux/raspbian stretch stable
+        state: present
+        filename: docker
+
+    - name: install docker
+      package:
+        name: "{{ item }}"
+        state: present
+      with_items:
+        - docker-ce
+        - docker-ce-cli
+        - containerd.io
+
+    - name: add myself to docker group
+      user:
+        name: alpha
+        groups: docker
+        append: yes
+
+    - name: install dependencies
+      package:
+        name: "{{ item }}"
+        state: present
+      with_items:
+        - docker-compose
+        - python-docker
+
+    - name: docker compose
+      docker_compose:
+        project_name: attitude-adjuster
+        definition:
+          version: '2'
+          services:
+            ddclient:
+              image: kejadlen/ddclient
+              container_name: ddclient
+              environment:
+                TZ: America/Los_Angeles
+              volumes:
+                - /etc/ddclient:/etc/ddclient
+              networks:
+                - bridge
+              restart: always
+
+            home_assistant:
+              container_name: home-assistant
+              image: homeassistant/raspberrypi3-homeassistant
+              ports:
+                - 5353:5353/udp
+                - 51827:51827/tcp
+              environment:
+                TZ: America/Los_Angeles
+              volumes:
+                - /etc/homeassistant:/config
+              networks:
+                - bridge
+              restart: always
+              labels:
+                - traefik.enable=true
+                - traefik.frontend.rule=Host:{{ home_assistant.host }}
+                - traefik.basic.port=8123
+                - traefik.basic.protocol=http
+
+            pihole:
+              container_name: pihole
+              image: pihole/pihole:latest
+              ports:
+                - 53:53/tcp
+                - 53:53/udp
+              environment:
+                ServerIP: 192.168.1.101
+                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
+              cap_add:
+                - NET_ADMIN
+              networks:
+                - bridge
+              restart: always
+              labels:
+                - traefik.enable=true
+                - traefik.frontend.rule=Host:{{ pihole.host }}
+                - traefik.basic.port=80
+                - traefik.basic.protocol=http
+
+            traefik:
+              container_name: traefik
+              image: traefik
+              command: --api --docker
+              ports:
+                - 80:80
+                - 8080:8080
+                - 443:443
+              volumes:
+                - /var/run/docker.sock:/var/run/docker.sock
+                - /opt/traefik/traefik.toml:/traefik.toml
+                - /opt/traefik/acme.json:/acme.json
+              networks:
+                - bridge
+              restart: always
+              labels:
+                - traefik.enable=true
+                - traefik.frontend.rule=Host:{{ traefik.host }}
+                - traefik.frontend.auth.basic.users=alpha:{{ traefik.password | password_hash('md5') | replace('$', '$$') }}
+                - traefik.basic.port=8080
+                - traefik.basic.protocol=http
+
+          networks:
+            bridge:
+        state: present
+        pull: yes
+
diff --git a/ansible/attitude-adjuster/external.conf b/ansible/attitude-adjuster/external.conf
deleted file mode 100644
index 2a1fcc8d..00000000
--- a/ansible/attitude-adjuster/external.conf
+++ /dev/null
@@ -1,22 +0,0 @@
-$HTTP["host"] == "{{ fqdn }}" {
-  # Ensure the Pi-hole Block Page knows that this is not a blocked domain
-  setenv.add-environment = ("fqdn" => "true")
-
-  # Enable the SSL engine with a LE cert, only for this specific host
-  $SERVER["socket"] == ":443" {
-    ssl.engine = "enable"
-    ssl.pemfile = "/etc/letsencrypt/live/{{ fqdn }}/combined.pem"
-    ssl.ca-file =  "/etc/letsencrypt/live/{{ fqdn }}/fullchain.pem"
-    ssl.honor-cipher-order = "enable"
-    ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"
-    ssl.use-sslv2 = "disable"
-    ssl.use-sslv3 = "disable"
-  }
-
-  # Redirect HTTP to HTTPS
-  $HTTP["scheme"] == "http" {
-    $HTTP["host"] =~ ".*" {
-      url.redirect = (".*" => "https://%0$0")
-    }
-  }
-}
diff --git a/ansible/attitude-adjuster/home-assistant.yml b/ansible/attitude-adjuster/home-assistant.yml
new file mode 100644
index 00000000..5d48cf49
--- /dev/null
+++ b/ansible/attitude-adjuster/home-assistant.yml
@@ -0,0 +1,39 @@
+- hosts: attitude-adjuster
+  vars_files:
+    - vars.yml.private
+  become: yes
+  tasks:
+
+    - name: configure home assistant
+      copy:
+        content: |
+          default_config:
+          http:
+            base_url: https://{{ home_assistant.host }}
+          tts: # Text to speech
+            - platform: google_translate
+          homekit:
+          group: !include groups.yaml
+          automation: !include automations.yaml
+          script: !include scripts.yaml
+        dest: /etc/homeassistant/configuration.yaml
+      notify: restart home assistant
+
+    - name: allow access to homekit
+      ufw:
+        rule: allow
+        proto: "{{ item.proto }}"
+        port: "{{ item.port }}"
+        state: reloaded
+      with_items:
+        - { proto: tcp, port: 51827 }
+        - { proto: udp, port: 5353 }
+
+  handlers:
+
+    - name: restart home assistant
+      docker_container:
+        name: home-assistant
+        restart: yes
+      ignore_errors: yes
+
diff --git a/ansible/attitude-adjuster/homebridge/config.json b/ansible/attitude-adjuster/homebridge/config.json
new file mode 100644
index 00000000..543db761
--- /dev/null
+++ b/ansible/attitude-adjuster/homebridge/config.json
@@ -0,0 +1,16 @@
+{
+  "bridge": {
+    "name": "Homebridge",
+    "username": "{{ homebridge.username }}",
+    "port": 51826,
+    "pin": "{{ homebridge.pin }}"
+  },
+  "platforms": [
+    {
+      "platform": "SmartThings Cloud",
+      "name": "SmartThings Cloud",
+      "port": 3000,
+      "publicKey": "{{ homebridge.smartthings_public_key | b64encode }}"
+    }
+  ]
+}
diff --git a/ansible/attitude-adjuster/homebridge/homebridge b/ansible/attitude-adjuster/homebridge/homebridge
new file mode 100644
index 00000000..f5c2eb9d
--- /dev/null
+++ b/ansible/attitude-adjuster/homebridge/homebridge
@@ -0,0 +1,7 @@
+# Defaults / Configuration options for homebridge
+# The following settings tells homebridge where to find the config.json file and where to persist the data (i.e. pairing and others)
+HOMEBRIDGE_OPTS=-U /var/lib/homebridge
+
+# If you uncomment the following line, homebridge will log more 
+# You can display this via systemd's journalctl: journalctl -f -u homebridge
+# DEBUG=*
\ No newline at end of file
diff --git a/ansible/attitude-adjuster/homebridge/homebridge.service b/ansible/attitude-adjuster/homebridge/homebridge.service
new file mode 100644
index 00000000..41ab653f
--- /dev/null
+++ b/ansible/attitude-adjuster/homebridge/homebridge.service
@@ -0,0 +1,17 @@
+[Unit]
+Description=Node.js HomeKit Server 
+After=syslog.target network-online.target
+
+[Service]
+Type=simple
+User=homebridge
+EnvironmentFile=/etc/default/homebridge
+# Adapt this to your specific setup (could be /usr/bin/homebridge)
+# See comments below for more information
+ExecStart=/usr/local/bin/homebridge $HOMEBRIDGE_OPTS
+Restart=on-failure
+RestartSec=10
+KillMode=process
+
+[Install]
+WantedBy=multi-user.target
\ No newline at end of file
diff --git a/ansible/attitude-adjuster/homebridge/main.yml b/ansible/attitude-adjuster/homebridge/main.yml
new file mode 100644
index 00000000..443c4624
--- /dev/null
+++ b/ansible/attitude-adjuster/homebridge/main.yml
@@ -0,0 +1,83 @@
+# https://github.com/nfarina/homebridge/wiki/Running-HomeBridge-on-a-Raspberry-Pi#install-avahi-and-other-dependencies
+
+- hosts: pi
+  vars_files:
+    - vars.yml.private
+  become: yes
+  tasks:
+
+    - name: update apt to get a modern version of node
+      block:
+        - apt_key:
+            url: https://deb.nodesource.com/gpgkey/nodesource.gpg.key
+        - copy:
+            src: nodesource.list
+            dest: /etc/apt/sources.list.d/nodesource.list
+
+    - name: update apt
+      apt:
+        update_cache: yes
+
+    - name: install dependencies
+      package:
+        name: "{{ item }}"
+        state: present
+      with_items:
+        - libavahi-compat-libdnssd-dev
+        - nodejs
+
+    - name: install homebridge
+      npm:
+        name: "{{ item }}"
+        global: yes
+      with_items:
+        - homebridge
+        - homebridge-smartthings-cloud
+
+    - name: copy systemd config
+      copy:
+        src: homebridge/{{ item.key }}
+        dest: "{{ item.value }}"
+      with_dict:
+        homebridge: /etc/default
+        homebridge.service: /etc/systemd/system
+      notify:
+        - reload systemd daemon
+        - enable and restart homebridge service
+
+    - name: create homebridge user
+      user:
+        name: homebridge
+        password: !
+
+    - name: create homebridge dir
+      file:
+        path: /var/lib/homebridge
+        state: directory
+        owner: homebridge
+
+    - name: copy homebridge config
+      template:
+        src: homebridge/config.json
+        dest: /var/lib/homebridge
+        owner: homebridge
+      notify: enable and restart homebridge service
+
+    - name: allow access to homebridge
+      ufw:
+        rule: allow
+        proto: tcp
+        port: 51826
+        state: reloaded
+
+  handlers:
+
+    - name: reload systemd daemon
+      systemd:
+        daemon_reload: yes
+
+    - name: enable and restart homebridge service
+      service:
+        name: homebridge
+        enabled: yes
+        state: restarted
diff --git a/ansible/attitude-adjuster/homebridge/nodesource.list b/ansible/attitude-adjuster/homebridge/nodesource.list
new file mode 100644
index 00000000..3e984aa6
--- /dev/null
+++ b/ansible/attitude-adjuster/homebridge/nodesource.list
@@ -0,0 +1,2 @@
+deb https://deb.nodesource.com/node_12.x stretch main
+deb-src https://deb.nodesource.com/node_12.x stretch main
diff --git a/ansible/attitude-adjuster/jail.local b/ansible/attitude-adjuster/jail.local
deleted file mode 100644
index 9eb356c8..00000000
--- a/ansible/attitude-adjuster/jail.local
+++ /dev/null
@@ -1,2 +0,0 @@
-[sshd]
-enabled = true
diff --git a/ansible/attitude-adjuster/lighttpd/main.yml b/ansible/attitude-adjuster/lighttpd/main.yml
new file mode 100644
index 00000000..420213e1
--- /dev/null
+++ b/ansible/attitude-adjuster/lighttpd/main.yml
@@ -0,0 +1,69 @@
+# https://discourse.pi-hole.net/t/enabling-https-for-your-pi-hole-web-interface/5771
+
+- hosts: attitude-adjuster
+  vars_files:
+    - vars.yml.private
+  become: yes
+  tasks:
+
+    - name: install certbot
+      package:
+        name: certbot
+        state: present
+
+    - name: obtain ssl certificates
+      command: >-
+        certbot certonly
+        --webroot --webroot-path /var/www/html
+        -d {{ item.fqdn }}
+        -m alpha+lets.encrypt@kejadlen.dev
+        --agree-tos
+        --non-interactive
+      args:
+        creates: /etc/letsencrypt/live/{{ item.fqdn }}
+      with_items:
+        - "{{ home }}"
+        - "{{ home_assistant }}"
+        - "{{ homebridge }}"
+        - "{{ pihole }}"
+
+    - name: create combined pemfile
+      shell: >-
+        cat /etc/letsencrypt/live/{{ item.fqdn }}/privkey.pem
+        /etc/letsencrypt/live/{{ item.fqdn }}/cert.pem >
+        /etc/letsencrypt/live/{{ item.fqdn }}/combined.pem
+      args:
+        creates: /etc/letsencrypt/live/{{ item.fqdn }}/combined.pem
+      with_items:
+        - "{{ home }}"
+        - "{{ home_assistant }}"
+        - "{{ homebridge }}"
+        - "{{ pihole }}"
+
+    - name: let lighttpd read the pemfile
+      file:
+        path: /etc/letsencrypt/live/{{ item.fqdn }}
+        state: directory
+        recurse: yes
+        owner: www-data
+      with_items:
+        - "{{ home }}"
+        - "{{ home_assistant }}"
+        - "{{ homebridge }}"
+        - "{{ pihole }}"
+
+    - name: enable mod_proxy
+      command: lighttpd-enable-mod proxy
+      ignore_errors: yes
+
+    - name: update lighttpd configuration
+      template:
+        src: external.conf.private
+        dest: /etc/lighttpd/external.conf
+      notify: restart lighttpd
+
+  handlers:
+    - name: restart lighttpd
+      service:
+        name: lighttpd
+        state: restarted
diff --git a/ansible/attitude-adjuster/main.yml b/ansible/attitude-adjuster/main.yml
index 89056368..ad795b11 100644
--- a/ansible/attitude-adjuster/main.yml
+++ b/ansible/attitude-adjuster/main.yml
@@ -1,4 +1,5 @@
 - hosts: attitude-adjuster
+  become: yes
   tasks:
 
     - name: install dependencies
@@ -8,148 +9,9 @@
       with_items:
         - git
         - vim
-      become: yes
 
-# Security
-#
-# https://www.raspberrypi.org/documentation/configuration/security.md
+- import_playbook: security.yml
 
-- hosts: attitude-adjuster
-  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
-      become: yes
-
-    - name: disable pi user
-      user:
-        name: pi
-        password: !
-      become: yes
-
-    - name: install ufw and fail2ban
-      package:
-        name: "{{ item }}"
-        state: present
-      become: yes
-      with_items:
-        - ufw
-        - fail2ban
-
-    - name: allow access to dns, http, and https
-      ufw:
-        rule: allow
-        name: "{{ item }}"
-      with_items:
-        - DNS
-        - WWW Full
-      become: yes
-
-    - name: allow access to dhcp
-      ufw:
-        rule: allow
-        proto: udp
-        port: 67
-      become: yes
-
-    - name: limit ssh access
-      ufw:
-        rule: limit
-        name: OpenSSH
-      become: yes
-
-    - name: enable ufw
-      ufw:
-        state: enabled
-      become: yes
-
-    - name: create jail.local
-      copy:
-        src: jail.local
-        dest: /etc/fail2ban/
-      become: yes
-      notify: reload fail2ban
-
-  handlers:
-    - name: reload ssh
-      service:
-        name: ssh
-        state: reloaded
-    - name: reload fail2ban
-      service:
-        name: fail2ban
-        state: reloaded
-
-- import_playbook: pihole.yml
-
-# ddclient
-
-- hosts: attitude-adjuster
-  vars_files:
-    - vars.yml.private
-  tasks:
-
-    - name: install ddclient
-      package:
-        name: ddclient
-        state: present
-      become: yes
-
-    - name: configure ddclient
-      template:
-        src: ddclient.conf
-        dest: /etc/ddclient.conf
-        mode: go-r
-      become: yes
-      notify: restart ddclient
-
-    - name: enable ddclient
-      service:
-        name: ddclient
-        enabled: yes
-      become: yes
-
-  handlers:
-    - name: restart ddclient
-      service:
-        name: ddclient
-        state: restarted
-      become: yes
-
-# Homebridge
-
-- hosts: attitude-adjuster
-  tasks:
-
-    - name: install npm
-      package:
-        name: npm
-        state: present
-      become: yes
-
-    - name: install homebridge
-      npm:
-        name: "{{ item }}"
-        global: yes
-      with_items:
-        - homebridge
-        - homebridge-smartthings-tonesto7
-      become: yes
-
-    - name: create ~/.homebridge
-      file:
-        path: ~/.homebridge
-        state: directory
-
-    - name: copy homebridge config
-      copy:
-        src: config.json
-        dest: ~/.homebridge/config.json
+- import_playbook: ddclient.yml
+- import_playbook: home-assistant.yml
+- import_playbook: docker.yml
diff --git a/ansible/attitude-adjuster/nginx.yml b/ansible/attitude-adjuster/nginx.yml
new file mode 100644
index 00000000..2ff78563
--- /dev/null
+++ b/ansible/attitude-adjuster/nginx.yml
@@ -0,0 +1,72 @@
+- hosts: attitude-adjuster
+  vars_files:
+    - vars.yml.private
+  become: yes
+  tasks:
+
+    - name: install dependencies
+      package:
+        name: "{{ item }}"
+        state: present
+      with_items:
+        - certbot
+        - nginx
+        - python3-certbot-nginx
+
+    - name: allow for long domain names
+      lineinfile:
+        path: /etc/nginx/nginx.conf
+        regexp: '^(\s*)# server_names_hash_bucket_size 64;$'
+        line: '\1server_names_hash_bucket_size 64;'
+        backrefs: yes
+
+    - name: obtain ssl certs
+      command: >-
+        certbot certonly --nginx
+        -d {{ item }}
+        -m alpha+lets.encrypt@kejadlen.dev
+        --agree-tos
+        --non-interactive
+      args:
+        creates: /etc/letsencrypt/live/{{ item }}
+      with_items:
+        - "{{ home.fqdn }}"
+        - "{{ home_assistant.fqdn }}"
+        - "{{ pihole.fqdn }}"
+
+    - name: configure nginx
+      copy:
+        content: |
+          server {
+
+              server_name {{ fqdn }};
+
+              # Redirect non-https traffic to https
+              if ($scheme != "https") {
+                  return 301 https://$host$request_uri;
+              }
+
+              location / {
+                  proxy_pass http://{{ proxy }}/;
+                  proxy_set_header Host $host;
+                  proxy_set_header X-Real-IP $remote_addr;
+                  proxy_set_header X-Forwarded-For nginx;
+              }
+
+              listen 80;
+
+              listen 443 ssl;
+              ssl_certificate /etc/letsencrypt/live/{{ fqdn }}/fullchain.pem;
+              ssl_certificate_key /etc/letsencrypt/live/{{ fqdn }}/privkey.pem;
+              include /etc/letsencrypt/options-ssl-nginx.conf;
+              ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
+
+          }
+        dest: /etc/nginx/conf.d/{{ fqdn }}.conf
+      vars:
+        fqdn: "{{ item.fqdn }}"
+        proxy: "{{ item.nginx.proxy }}"
+      with_items:
+        - "{{ home }}"
+        - "{{ home_assistant }}"
+        - "{{ pihole }}"
diff --git a/ansible/attitude-adjuster/pihole.yml b/ansible/attitude-adjuster/pihole.yml
deleted file mode 100644
index b844f4e9..00000000
--- a/ansible/attitude-adjuster/pihole.yml
+++ /dev/null
@@ -1,64 +0,0 @@
-# https://discourse.pi-hole.net/t/enabling-https-for-your-pi-hole-web-interface/5771
-
-- hosts: attitude-adjuster
-  vars_files:
-    - vars.yml.private
-  tasks:
-
-    - name: install certbot
-      package:
-        name: certbot
-        state: present
-      become: yes
-
-    - name: clone the pi-hole repo
-      git:
-        repo: https://github.com/pi-hole/pi-hole.git
-        dest: ~/src/pi-hole
-        depth: 1
-
-    - name: obtain ssl certificates
-      command: >-
-        certbot certonly
-        --webroot --webroot-path /var/www/html
-        -d {{ fqdn }}
-        -m alpha+lets.encrypt@kejadlen.dev
-        --agree-tos
-        --non-interactive
-      args:
-        creates: /etc/letsencrypt/live/{{ fqdn }}
-      become: yes
-
-    - name: create combined pemfile
-      shell: >-
-        cat /etc/letsencrypt/live/{{ fqdn }}/privkey.pem
-        /etc/letsencrypt/live/{{ fqdn }}/cert.pem >
-        /etc/letsencrypt/live/{{ fqdn }}/combined.pem
-      args:
-        creates: /etc/letsencrypt/live/{{ fqdn }}/combined.pem
-      become: yes
-
-    - name: let lighttpd read the pemfile
-      file:
-        path: /etc/letsencrypt/live/{{ fqdn }}
-        state: directory
-        recurse: yes
-        owner: www-data
-      become: yes
-
-    - name: update lighttpd configuration
-      template:
-        src: external.conf
-        dest: /etc/lighttpd/external.conf
-      become: yes
-      notify: restart lighttpd
-
-  handlers:
-    - name: restart lighttpd
-      service:
-        name: lighttpd
-        state: restarted
-      become: yes
-
-
-
diff --git a/ansible/attitude-adjuster/security.yml b/ansible/attitude-adjuster/security.yml
new file mode 100644
index 00000000..becd9213
--- /dev/null
+++ b/ansible/attitude-adjuster/security.yml
@@ -0,0 +1,66 @@
+# https://www.raspberrypi.org/documentation/configuration/security.md
+
+- hosts: attitude-adjuster
+  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 ufw and fail2ban
+      package:
+        name: "{{ item }}"
+        state: present
+      with_items:
+        - ufw
+        - fail2ban
+
+    - name: allow access to dns, http, and https
+      ufw:
+        rule: allow
+        name: "{{ item }}"
+      with_items:
+        - DNS
+        - WWW Full
+
+    - name: limit ssh access
+      ufw:
+        rule: limit
+        name: OpenSSH
+
+    - name: enable ufw
+      ufw:
+        state: enabled
+
+    - 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
diff --git a/ansible/attitude-adjuster/traefik.yml b/ansible/attitude-adjuster/traefik.yml
new file mode 100644
index 00000000..9345288e
--- /dev/null
+++ b/ansible/attitude-adjuster/traefik.yml
@@ -0,0 +1,49 @@
+# https://docs.traefik.io/user-guide/docker-and-lets-encrypt/
+
+- hosts: attitude-adjuster
+  vars_files:
+    - vars.yml.private
+  become: yes
+  tasks:
+
+    - name: traefik.toml
+      copy:
+        content: |
+          debug = false
+          insecureSkipVerify = true
+
+          logLevel = "ERROR"
+          defaultEntryPoints = ["https","http"]
+
+          [entryPoints]
+            [entryPoints.http]
+            address = ":80"
+              [entryPoints.http.redirect]
+              entryPoint = "https"
+            [entryPoints.https]
+            address = ":443"
+            [entryPoints.https.tls]
+
+          [retry]
+
+          [docker]
+          endpoint = "unix:///var/run/docker.sock"
+          domain = "{{ traefik.domain }}"
+          watch = true
+          exposedByDefault = false
+          network = "attitudeadjuster_bridge"
+
+          [acme]
+          email = "{{ traefik.email }}"
+          storage = "acme.json"
+          entryPoint = "https"
+          onHostRule = true
+          [acme.httpChallenge]
+          entryPoint = "http"
+        dest: /opt/traefik/traefik.toml
+
+    - name: acme.json
+      file:
+        name: /opt/traefik/acme.json
+        mode: 0600
+        state: touch