From ccb92cabe64cbadf4a04c516b33c7c95eeadf0aa Mon Sep 17 00:00:00 2001 From: Sam Al-Sapti Date: Thu, 22 Dec 2022 20:18:27 +0100 Subject: [PATCH] Add os_config role --- group_vars/all/secrets.yml | 55 ++++++++++++---------- playbook.yml | 10 +++- roles/docker/defaults/main.yml | 3 +- roles/docker/tasks/main.yml | 35 +++++++++----- roles/docker/tasks/services.yml | 8 ++++ roles/docker/tasks/services/caddy.yml | 3 ++ roles/docker/tasks/services/emby.yml | 3 +- roles/docker/tasks/services/monerod.yml | 9 ++-- roles/docker/tasks/services/nextcloud.yml | 19 ++++---- roles/docker/tasks/services/restic.yml | 7 +-- roles/docker/tasks/services/snowflake.yml | 1 + roles/docker/tasks/services/watchtower.yml | 1 + roles/docker/tasks/services/wireguard.yml | 1 + roles/docker/templates/Caddyfile.j2 | 18 ++----- roles/docker/templates/daemon.json.j2 | 11 +++++ roles/os_config/defaults/main.yml | 2 + roles/os_config/tasks/luks.yml | 3 ++ roles/os_config/tasks/main.yml | 16 +++++++ roles/os_config/tasks/pkgs.yml | 17 +++++++ roles/os_config/tasks/ssh.yml | 36 ++++++++++++++ roles/os_config/tasks/ufw.yml | 20 ++++++++ 21 files changed, 204 insertions(+), 74 deletions(-) create mode 100644 roles/docker/templates/daemon.json.j2 create mode 100644 roles/os_config/defaults/main.yml create mode 100644 roles/os_config/tasks/luks.yml create mode 100644 roles/os_config/tasks/main.yml create mode 100644 roles/os_config/tasks/pkgs.yml create mode 100644 roles/os_config/tasks/ssh.yml create mode 100644 roles/os_config/tasks/ufw.yml diff --git a/group_vars/all/secrets.yml b/group_vars/all/secrets.yml index af505c9..fe6d3c8 100644 --- a/group_vars/all/secrets.yml +++ b/group_vars/all/secrets.yml @@ -1,27 +1,30 @@ $ANSIBLE_VAULT;1.1;AES256 -65633564663165316234316164633133393062326534613461663237666537373861323162396136 -6665653130343264636466656566636132366439326330610a643735626235323335303937656333 -36326161666239323838373466373463383465396635393630663132356234353765653930643463 -3735383539303631300a633839373936663563363537656636633632323964393138333730303031 -61333535353132383562396136616138326265336235633665316164316234646533343232633938 -63343034363636343966363161376463383432643536633638663339323566396330306565356666 -33623936653635656265616364653831313866313931653662396463393430326662303164613838 -33306538366531656166306566353636366539636266346537353539643862663630663938663865 -62636531333566636438393831646466613465613537653162383030626564393464656132346661 -37383137633833366338383637333161303434363533363135376237636565373337396230663033 -38313838373131633035633761636134653263386430656535616339373336373538376364613563 -31323335343066346635393130623834313839303464313365633331616361393462373862306335 -31633266666630343637333936643633396463363336613332313736623466633733343164363631 -62303163616563393735633438633739333732656161653337343439313265656166613731356162 -66313433306338643533373265613637336232623732643734646233666266663666623565636631 -61336635326465333232616134666635363234396535386265373533363138363366303631616630 -61393837313139313531336631333734633039363034396165643733653132623136363137343232 -39633839303536613636313661363831343831303562383832313166316164346231626565323961 -31336334393965346466386564393961383734393663636139313964653163666235323538626362 -34373263383463376130323562386561376262666539663233346431623263376532643737633830 -62626136336632663030383136383364343332323732306539306663613161646535656531383561 -32353436386163626436356632386631653666343931663063373462613134613039386266636366 -36646538633166383830643466383936613565613031313936316539313434333839363764636438 -62616261613936663762373764656466623666373034303662306265636431333663376230393634 -64323032323439363265623938323237626538653534633364623730613836373336363862613334 -3566396264653531386637613639373638393633363639613566 +66653666613865393239313165343731323338616237653731343964373065386138666161653164 +3031306366373335323239396631633034363332306434380a613331613239663035313235383137 +62356463323933303336383363363962643963623934663363636364363034323465326562616463 +3236396135396566300a396162623864346162383338343132353331623664643065303634616664 +65623037636561313237376233623137616537346535333536396662343164633737313938313637 +64313366303264336464653231333562363835383036663864323764636565646137353265363566 +63333332373562663866393139643465346164316464373132636166363562643564343935383737 +61653332326162316532656262666233393132386238653032353435306464343138326236386330 +36316263383863393866616562306365643132633939373836353236666432373662386632323234 +64326132616433643132633035306266623235316137396362306132636437646430323663653233 +62393165333537383232643132353431373338633261323739616565306634306263346163353938 +30393766323339616238613361313834636534623265346237383730386163346562666234303832 +35613236626465393031663833336238323832646261333731393365373539393231366134613866 +36376135396335333437383864613634383635663834393138376635613633333062343338643965 +65343335643435303765666530346431313632353434343735383065346132653035316239353566 +32616536626138653939306137636136396330613964393833616536636464326538396634323037 +63366364393061353638633663343263666132336330306136663662366132343265653361356161 +39666138616331323336313438343763666331363238396364353664383533393632646665326337 +37613034633939356134366639663239653031323037623364633838303734336532626536356365 +66613061653833646231666564316632346166313461636333393965386162626232626465376437 +63396464346137666537626333643564316461316536323236643132346462353133653739363330 +33376137633336616663373633303964323661353636373631633465663566383834373932306330 +38626465353265306431386563343638363064623164393563376365353534343036356331393435 +64613331366234343261343463366330316566313431653632653339386631363966663634656434 +64666161313264386165373231666665303435373138633536616535373132353966636662666561 +35343966373330323231346637363563343063373639326134636364626462663061343231363631 +31303937373261623362323833613837336631346137633831356165313864383364613431646333 +61316636396236633164336563306534626162326263643230303839373761633739366165396331 +33326332393935313262663631386631353936626161623238343335383764343131 diff --git a/playbook.yml b/playbook.yml index e19bdb3..c354ffa 100644 --- a/playbook.yml +++ b/playbook.yml @@ -1,10 +1,18 @@ --- -- name: Deploy homeserver +- name: Deploy self-hosted services hosts: all gather_facts: false become: true + vars: + hdd_mount_point: /opt/storage + ssd_mount_point: /opt/pi-ssd + + timezone: Europe/Copenhagen tasks: + - name: Run OS configuration role + import_role: + name: os_config - name: Run Docker role import_role: name: docker diff --git a/roles/docker/defaults/main.yml b/roles/docker/defaults/main.yml index f095654..775533f 100644 --- a/roles/docker/defaults/main.yml +++ b/roles/docker/defaults/main.yml @@ -1,7 +1,6 @@ --- base_domain: sapti.me -base_volume: /opt/storage/apps -timezone: Europe/Copenhagen +base_volume: "{{ hdd_mount_point }}/apps" services: caddy: diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml index b0ca16c..08bd93b 100644 --- a/roles/docker/tasks/main.yml +++ b/roles/docker/tasks/main.yml @@ -13,11 +13,12 @@ - name: Install Docker apt: - name: "{{ item }}" + name: "{{ pkgs }}" state: present - loop: - - docker-ce - - docker-compose-plugin + vars: + pkgs: + - docker-ce + - docker-compose-plugin - name: Create docker-compose symlink file: @@ -25,18 +26,26 @@ name: /usr/local/bin/docker-compose state: link -- name: Install Python bindings for Docker Compose +- name: Install Python bindings for Docker pip: - executable: pip3 - name: docker-compose + name: "{{ pkgs }}" state: present + executable: pip3 + vars: + pkgs: + - docker + - docker-compose -- name: Create base directory for Docker volumes - file: - name: "{{ base_volume }}" - owner: "{{ ansible_user }}" - mode: u=rwx,g=rx,o=rx - state: directory +- name: Start but disable Docker daemon + service: + name: "{{ unit }}" + enabled: false + state: started + loop: + - docker.socket + - docker.service + loop_control: + loop_var: unit - name: Set up Docker services import_tasks: services.yml diff --git a/roles/docker/tasks/services.yml b/roles/docker/tasks/services.yml index d5dd6fc..768b0e9 100644 --- a/roles/docker/tasks/services.yml +++ b/roles/docker/tasks/services.yml @@ -1,4 +1,12 @@ +# vim: ft=yaml.ansible --- +- name: Create base directory for Docker volumes + file: + name: "{{ base_volume }}" + owner: "{{ ansible_user }}" + mode: u=rwx,g=rx,o=rx + state: directory + - name: Deploy services include_tasks: "services/{{ item.service.file }}" loop: "{{ services | dict2items(value_name='service') }}" diff --git a/roles/docker/tasks/services/caddy.yml b/roles/docker/tasks/services/caddy.yml index 27d2f2d..7beca44 100644 --- a/roles/docker/tasks/services/caddy.yml +++ b/roles/docker/tasks/services/caddy.yml @@ -1,3 +1,4 @@ +# vim: ft=yaml.ansible --- - name: Create Caddy volume directories file: @@ -31,3 +32,5 @@ - dac_override cap_drop: - all + +# vim: ft=yaml.ansible diff --git a/roles/docker/tasks/services/emby.yml b/roles/docker/tasks/services/emby.yml index 9b4868f..3244983 100644 --- a/roles/docker/tasks/services/emby.yml +++ b/roles/docker/tasks/services/emby.yml @@ -1,3 +1,4 @@ +# vim: ft=yaml.ansible --- - name: Create Emby volume directories file: @@ -25,6 +26,6 @@ - "{{ services.emby.volume }}/tvshows:/mnt/share1:rw" - "{{ services.emby.volume }}/movies:/mnt/share2:rw" published_ports: - - "8096:8096" + - '8096:8096' devices: - /dev/vchiq:/dev/vchiq # MMAL/OMX on Raspberry Pi diff --git a/roles/docker/tasks/services/monerod.yml b/roles/docker/tasks/services/monerod.yml index 179e790..6cfb3ca 100644 --- a/roles/docker/tasks/services/monerod.yml +++ b/roles/docker/tasks/services/monerod.yml @@ -1,3 +1,4 @@ +# vim: ft=yaml.ansible --- - name: Create Docker volume for Monero blockchain data docker_volume: @@ -11,11 +12,11 @@ volumes: - monerod_data:/home/monero/.bitmonero:rw command: - - --rpc-restricted-bind-ip=0.0.0.0 - - --rpc-restricted-bind-port=18089 + - '--rpc-restricted-bind-ip=0.0.0.0' + - '--rpc-restricted-bind-port=18089' - --no-igd - --no-zmq - --enable-dns-blocklist published_ports: - - "18080:18080" - - "127.0.0.1:18081:18089" + - '18080:18080' + - '127.0.0.1:18081:18089' diff --git a/roles/docker/tasks/services/nextcloud.yml b/roles/docker/tasks/services/nextcloud.yml index 2f87ed6..a441e76 100644 --- a/roles/docker/tasks/services/nextcloud.yml +++ b/roles/docker/tasks/services/nextcloud.yml @@ -1,3 +1,4 @@ +# vim: ft=yaml.ansible --- - name: Create Nextcloud volume directories file: @@ -34,22 +35,22 @@ image: "mariadb:{{ services.nextcloud.mariadb_version }}" restart: unless-stopped command: - - --transaction-isolation=READ-COMMITTED + - '--transaction-isolation=READ-COMMITTED' - --log-bin - - --binlog-format=ROW - - --innodb_read_only_compressed=OFF + - '--binlog-format=ROW' + - '--innodb_read_only_compressed=OFF' environment: MYSQL_DATABASE: nextcloud MYSQL_USER: nextcloud - MYSQL_PASSWORD: "{{ secrets.nextcloud.mysql.pw }}" - MYSQL_ROOT_PASSWORD: "{{ secrets.nextcloud.mysql.pw }}" + MYSQL_PASSWORD: "{{ secrets.nextcloud.mysql_pw }}" + MYSQL_ROOT_PASSWORD: "{{ secrets.nextcloud.mysql_pw }}" volumes: - "{{ services.nextcloud.volume }}/db:/var/lib/mysql:rw" redis: image: "redis:{{ services.nextcloud.redis_version }}" restart: unless-stopped - command: "redis-server --requirepass={{ secrets.nextcloud.redis.pw }}" + command: "redis-server --requirepass={{ secrets.nextcloud.redis_pw }}" tmpfs: - /var/lib/redis @@ -70,9 +71,9 @@ MYSQL_HOST: mysql MYSQL_DATABASE: nextcloud MYSQL_USER: nextcloud - MYSQL_PASSWORD: "{{ secrets.nextcloud.mysql.pw }}" + MYSQL_PASSWORD: "{{ secrets.nextcloud.mysql_pw }}" REDIS_HOST: redis - REDIS_HOST_PASSWORD: "{{ secrets.nextcloud.redis.pw }}" + REDIS_HOST_PASSWORD: "{{ secrets.nextcloud.redis_pw }}" PHP_MEMORY_LIMIT: 2G PHP_UPLOAD_LIMIT: 16G volumes: @@ -80,7 +81,7 @@ - "{{ services.nextcloud.volume }}/apache2/apache2.conf:/etc/apache2/apache2.conf:ro" - "{{ services.nextcloud.volume }}/apache2/remoteip.conf:/etc/apache2/conf-enabled/remoteip.conf:ro" ports: - - "127.0.0.1:8080:80" + - '127.0.0.1:8080:80' depends_on: - mysql - redis diff --git a/roles/docker/tasks/services/restic.yml b/roles/docker/tasks/services/restic.yml index 538b3bb..465d3a9 100644 --- a/roles/docker/tasks/services/restic.yml +++ b/roles/docker/tasks/services/restic.yml @@ -1,3 +1,4 @@ +# vim: ft=yaml.ansible --- - name: Deploy Restic with Docker Compose docker_compose: @@ -14,7 +15,7 @@ RUN_ON_STARTUP: false BACKUP_CRON: '0 30 3 * * *' RESTIC_REPOSITORY: "b2:{{ secrets.restic.b2.bucket }}:{{ services.restic.repo }}" - RESTIC_PASSWORD: "{{ secrets.restic.pw }}" + RESTIC_PASSWORD: "{{ secrets.restic.repo_pw }}" RESTIC_BACKUP_SOURCES: /mnt/volumes RESTIC_BACKUP_ARGS: >- --tag docker-volumes @@ -40,7 +41,7 @@ RUN_ON_STARTUP: false PRUNE_CRON: '0 0 4 * * *' RESTIC_REPOSITORY: "b2:{{ secrets.restic.b2.bucket }}:{{ services.restic.repo }}" - RESTIC_PASSWORD: "{{ secrets.restic.pw }}" + RESTIC_PASSWORD: "{{ secrets.restic.repo_pw }}" RESTIC_PRUNE_ARGS: >- --verbose B2_ACCOUNT_ID: "{{ secrets.restic.b2.id }}" @@ -54,7 +55,7 @@ RUN_ON_STARTUP: false CHECK_CRON: '0 30 4 * * *' RESTIC_REPOSITORY: "b2:{{ secrets.restic.b2.bucket }}:{{ services.restic.repo }}" - RESTIC_PASSWORD: "{{ secrets.restic.pw }}" + RESTIC_PASSWORD: "{{ secrets.restic.repo_pw }}" RESTIC_CHECK_ARGS: >- --verbose B2_ACCOUNT_ID: "{{ secrets.restic.b2.id }}" diff --git a/roles/docker/tasks/services/snowflake.yml b/roles/docker/tasks/services/snowflake.yml index d86cdd1..928a5c1 100644 --- a/roles/docker/tasks/services/snowflake.yml +++ b/roles/docker/tasks/services/snowflake.yml @@ -1,3 +1,4 @@ +# vim: ft=yaml.ansible --- - name: Deploy snowflake-proxy Docker container docker_container: diff --git a/roles/docker/tasks/services/watchtower.yml b/roles/docker/tasks/services/watchtower.yml index ef79d97..2cec897 100644 --- a/roles/docker/tasks/services/watchtower.yml +++ b/roles/docker/tasks/services/watchtower.yml @@ -1,3 +1,4 @@ +# vim: ft=yaml.ansible --- - name: Deploy Watchtower Docker container docker_container: diff --git a/roles/docker/tasks/services/wireguard.yml b/roles/docker/tasks/services/wireguard.yml index d721554..bb03079 100644 --- a/roles/docker/tasks/services/wireguard.yml +++ b/roles/docker/tasks/services/wireguard.yml @@ -1,3 +1,4 @@ +# vim: ft=yaml.ansible --- - name: Create Wireguard volume directory file: diff --git a/roles/docker/templates/Caddyfile.j2 b/roles/docker/templates/Caddyfile.j2 index fcb6b7c..43d0493 100644 --- a/roles/docker/templates/Caddyfile.j2 +++ b/roles/docker/templates/Caddyfile.j2 @@ -3,7 +3,7 @@ } {{ services.nextcloud.domain }} { - tls {{ secrets.tls.email }} + tls {{ secrets.tls_email }} rewrite /.well-known/caldav /remote.php/dav rewrite /.well-known/carddav /remote.php/dav @@ -13,39 +13,27 @@ -Server } - log { - output discard - } - reverse_proxy localhost:8080 } {{ services.emby.domain }} { - tls {{ secrets.tls.email }} + tls {{ secrets.tls_email }} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" -Server } - log { - output discard - } - reverse_proxy localhost:8096 } {{ services.monerod.domain }}:18089 { - tls {{ secrets.tls.email }} + tls {{ secrets.tls_email }} header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" -Server } - log { - output discard - } - reverse_proxy localhost:18081 } diff --git a/roles/docker/templates/daemon.json.j2 b/roles/docker/templates/daemon.json.j2 new file mode 100644 index 0000000..ce3b976 --- /dev/null +++ b/roles/docker/templates/daemon.json.j2 @@ -0,0 +1,11 @@ +{ + "data-root": "{{ ssd_mount_point }}/docker-runtime", + "default-address-pools": [ + { + "base": "172.17.0.0/16", + "size": 24 + } + ], + "ipv6": true, + "fixed-cidr-v6": "fd00::/80" +} diff --git a/roles/os_config/defaults/main.yml b/roles/os_config/defaults/main.yml new file mode 100644 index 0000000..141f8ae --- /dev/null +++ b/roles/os_config/defaults/main.yml @@ -0,0 +1,2 @@ +--- +ssh_key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPd/4fQV7CL8/KVwbo/phiV5UdXFBIDlkZ+ps8C7FeRf diff --git a/roles/os_config/tasks/luks.yml b/roles/os_config/tasks/luks.yml new file mode 100644 index 0000000..a00885c --- /dev/null +++ b/roles/os_config/tasks/luks.yml @@ -0,0 +1,3 @@ +# vim: ft=yaml.ansible +--- + diff --git a/roles/os_config/tasks/main.yml b/roles/os_config/tasks/main.yml new file mode 100644 index 0000000..736fe61 --- /dev/null +++ b/roles/os_config/tasks/main.yml @@ -0,0 +1,16 @@ +--- +- name: Configure system packages + import_tasks: + - pkgs.yml + +- name: Configure firewall + import_tasks: + - ufw.yml + +- name: Configure disk encryption + import_tasks: + - luks.yml + +- name: Configure SSH + import_tasks: + - ssh.yml diff --git a/roles/os_config/tasks/pkgs.yml b/roles/os_config/tasks/pkgs.yml new file mode 100644 index 0000000..c3859ab --- /dev/null +++ b/roles/os_config/tasks/pkgs.yml @@ -0,0 +1,17 @@ +# vim: ft=yaml.ansible +--- +- name: Upgrade system packages + apt: + update_cache: true + upgrade: full + +- name: Install packages via apt + apt: + name: "{{ pkgs }}" + state: present + vars: + pkgs: + - python3-pip + - apparmor + - haveged + - ufw diff --git a/roles/os_config/tasks/ssh.yml b/roles/os_config/tasks/ssh.yml new file mode 100644 index 0000000..5662da6 --- /dev/null +++ b/roles/os_config/tasks/ssh.yml @@ -0,0 +1,36 @@ +# vim: ft=yaml.ansible +--- +- name: Add public SSH key to default user + authorized_key: + user: "{{ ansible_user }}" + key: "{{ ssh_key }}" + exclusive: true + +- name: Allow SSH login with public keys + lineinfile: + regexp: '^#?PubkeyAuthentication ' + line: PubkeyAuthentication yes + dest: /etc/ssh/sshd_config + register: ssh_pubkey + +- name: Disallow SSH login with password + lineinfile: + regexp: '^#?PasswordAuthentication ' + line: PasswordAuthentication no + dest: /etc/ssh/sshd_config + register: ssh_pw + +- name: Disallow root login over SSH + lineinfile: + regexp: '^#?PermitRootLogin ' + line: PermitRootLogin no + dest: /etc/ssh/sshd_config + register: ssh_root + +- name: Restart sshd + service: + name: sshd + state: restarted + when: (ssh_pubkey is defined and ssh_pubkey.changed) or + (ssh_pw is defined and ssh_pw.changed) or + (ssh_root is defined and ssh_root.changed) diff --git a/roles/os_config/tasks/ufw.yml b/roles/os_config/tasks/ufw.yml new file mode 100644 index 0000000..2c2dba9 --- /dev/null +++ b/roles/os_config/tasks/ufw.yml @@ -0,0 +1,20 @@ +# vim: ft=yaml.ansible +--- +- name: Allow necessary ports in UFW + community.general.ufw: + rule: allow + port: "{{ item.port }}" + proto: "{{ item.proto | default('tcp') }}" + loop: + - port: 22 # SSH + - port: 80 # HTTP + - port: 443 # HTTPS + - port: 18080 # monerod P2P + - port: 18089 # monerod RPC + - port: 51820 # Wireguard + proto: udp + +- name: Enable UFW + community.general.ufw: + state: enabled + policy: deny