diff --git a/docker-lb.yml b/docker-lb.yml new file mode 100644 index 0000000..97995e5 --- /dev/null +++ b/docker-lb.yml @@ -0,0 +1,13 @@ +--- +- name: Set up reverse proxy for docker + hosts: docker_lb + gather_facts: yes + vars_files: + - secrets.yml + roles: + - role: common + tags: + - common + - role: reverse_proxy + tags: + - reverse_proxy diff --git a/group_vars/docker/vars.yml b/group_vars/docker/vars.yml index 28978a6..7d81f55 100644 --- a/group_vars/docker/vars.yml +++ b/group_vars/docker/vars.yml @@ -2,3 +2,78 @@ docker: url: "https://download.docker.com/linux" apt_release_channel: "stable" dirs: "/opt/docker" + +caddy: + admin_email: me+acme@tudattr.dev + +domain: "seyshiro.de" + +services: + - name: syncthing + vm: + - docker-host00 + port: 8384 + - name: status + vm: + - docker-host00 + port: 3001 + - name: plex + vm: + - docker-host00 + port: 32400 + - name: jellyfin + vm: + - docker-host02 + port: 8096 + - name: hass + vm: + - docker-host02 + port: 8123 + - name: ddns + vm: + - docker-host00 + port: 8123 + - name: sonarr + vm: + - docker-host00 + port: 8989 + - name: radarr + vm: + - docker-host00 + port: 7878 + - name: lidarr + vm: + - docker-host00 + port: 8686 + - name: prowlarr + vm: + - docker-host00 + port: 9696 + - name: qbit + vm: + - docker-host00 + port: 9696 + - name: tl + vm: + - docker-host00 + port: 9696 + - name: paperless + vm: + - docker-host00 + port: 8000 + - name: pdf + vm: + - docker-host00 + port: 8080 + - name: git + vm: + - docker-host02 + port: 3000 + - name: changedetection + vm: + - docker-host00 + port: 5000 + - name: calibre + vm: + - docker-host00 + port: 5000 diff --git a/host_vars/docker-host00.yml b/host_vars/docker-host00.yml index 271a4f2..154dc19 100644 --- a/host_vars/docker-host00.yml +++ b/host_vars/docker-host00.yml @@ -8,19 +8,3 @@ ansible_become_pass: "{{ vault.docker.host00.sudo }}" host: hostname: "docker-host00" ip: "{{ ansible_host }}" - -enable_nginx: true -enable_syncthing: true -enable_kuma: true -enable_plex: true -enable_arr: true -enable_prometheus: false -enable_grafana: false -enable_ddns_updater: true -enable_homeassistant: false -enable_stirling: true -enable_jellyfin: false -enable_paperless: true -enable_gitea: false -enable_changedetection: true -enable_calibre: false diff --git a/host_vars/docker-host01.yml b/host_vars/docker-host01.yml index a493c5e..bded570 100644 --- a/host_vars/docker-host01.yml +++ b/host_vars/docker-host01.yml @@ -8,3 +8,19 @@ ansible_become_pass: "{{ vault.docker.host01.sudo }}" host: hostname: "docker-host01" ip: "{{ ansible_host }}" + +enable_nginx: true +enable_syncthing: true +enable_kuma: true +enable_plex: true +enable_arr: true +enable_prometheus: false +enable_grafana: false +enable_ddns_updater: true +enable_homeassistant: false +enable_stirling: true +enable_jellyfin: false +enable_paperless: true +enable_gitea: false +enable_changedetection: true +enable_calibre: false diff --git a/host_vars/docker-host02.yml b/host_vars/docker-host02.yml index 89c2c48..9debe14 100644 --- a/host_vars/docker-host02.yml +++ b/host_vars/docker-host02.yml @@ -8,19 +8,3 @@ ansible_become_pass: "{{ vault.docker.host02.sudo }}" host: hostname: "docker-host02" ip: "{{ ansible_host }}" - -enable_nginx: true -enable_syncthing: false -enable_kuma: false -enable_plex: false -enable_arr: false -enable_prometheus: false -enable_grafana: false -enable_ddns_updater: false -enable_homeassistant: true -enable_stirling: false -enable_jellyfin: true -enable_paperless: false -enable_gitea: true -enable_changedetection: false -enable_calibre: false diff --git a/host_vars/docker-lb.yml b/host_vars/docker-lb.yml new file mode 100644 index 0000000..ab2b1ad --- /dev/null +++ b/host_vars/docker-lb.yml @@ -0,0 +1,10 @@ +--- +ansible_user: "{{ user }}" +ansible_host: 192.168.20.37 +ansible_port: 22 +ansible_ssh_private_key_file: "{{ pk_path }}" +ansible_become_pass: "{{ vault.docker.lb.sudo }}" + +host: + hostname: "docker-lb" + ip: "{{ ansible_host }}" diff --git a/production b/production index be4494f..af968f8 100644 --- a/production +++ b/production @@ -66,12 +66,18 @@ ansible_ssh_common_args='-o ProxyCommand="ssh -p 22 -W %h:%p -q aya01"' [docker] docker-host00 +docker-host01 docker-host02 +docker-lb [docker_host] docker-host00 +docker-host01 docker-host02 +[docker_lb] +docker-lb + [proxmox] aya01 lulu diff --git a/roles/common/files/bash/bashrc b/roles/common/files/bash/bashrc index a6458b9..bf6f7f6 100644 --- a/roles/common/files/bash/bashrc +++ b/roles/common/files/bash/bashrc @@ -50,3 +50,7 @@ if ! shopt -oq posix; then . /etc/bash_completion fi fi + +if [ -f /etc/profile ]; then + . /etc/profile +fi diff --git a/roles/docker_host/templates/compose.yaml.j2 b/roles/docker_host/templates/compose.yaml.j2 index 9989dc1..16a1b9c 100644 --- a/roles/docker_host/templates/compose.yaml.j2 +++ b/roles/docker_host/templates/compose.yaml.j2 @@ -1,5 +1,6 @@ services: -{% if enable_nginx %} +{% for service in services %} +{% if service.name == 'nginx' and inventory_hostname in service.vm %} nginx: container_name: "nginx" image: "jc21/nginx-proxy-manager:latest" @@ -16,7 +17,7 @@ services: - "/var/run/docker.sock:/var/run/docker.sock" {% endif %} -{% if enable_syncthing %} +{% if service.name == 'syncthing' and inventory_hostname in service.vm %} syncthing: image: syncthing/syncthing container_name: syncthing @@ -38,7 +39,7 @@ services: hostname: syncthing {% endif %} -{% if enable_kuma %} +{% if service.name == 'status' and inventory_hostname in service.vm %} kuma: container_name: kuma image: louislam/uptime-kuma:1 @@ -57,7 +58,7 @@ services: - "/opt/local/kuma/:/app/data" {% endif %} -{% if enable_plex %} +{% if service.name == 'plex' and inventory_hostname in service.vm %} plex: image: lscr.io/linuxserver/plex:latest container_name: plex @@ -89,7 +90,7 @@ services: - "/media/songs:/music:ro" {% endif %} -{% if enable_arr %} +{% if service.name == 'sonarr' and inventory_hostname in service.vm %} sonarr: image: lscr.io/linuxserver/sonarr:latest container_name: sonarr @@ -106,7 +107,9 @@ services: - /opt/local/sonarr/config:/config - /media/series:/tv #optional - /media/docker/data/arr_downloads/sonarr:/downloads #optional +{% endif %} +{% if service.name == 'radarr' and inventory_hostname in service.vm %} radarr: image: lscr.io/linuxserver/radarr:latest container_name: radarr @@ -123,7 +126,9 @@ services: - /opt/local/radarr/config:/config - /media/movies:/movies #optional - /media/docker/data/arr_downloads/radarr:/downloads #optional +{% endif %} +{% if service.name == 'lidarr' and inventory_hostname in service.vm %} lidarr: image: lscr.io/linuxserver/lidarr:latest container_name: lidarr @@ -140,7 +145,9 @@ services: - /opt/local/lidarr/config:/config - /media/songs:/music #optional - /media/docker/data/arr_downloads/lidarr:/downloads #optional +{% endif %} +{% if service.name == 'prowlarr' and inventory_hostname in service.vm %} prowlarr: image: lscr.io/linuxserver/prowlarr:latest container_name: prowlarr @@ -155,7 +162,9 @@ services: - TZ=Europe/Berlin volumes: - /opt/local/prowlarr/config:/config +{% endif %} +{% if service.name == 'tl' and inventory_hostname in service.vm %} gluetun: image: qmcgaw/gluetun container_name: gluetun @@ -181,7 +190,9 @@ services: - SERVER_COUNTRIES=Hungary - OPENVPN_USER=MfCOtzTIEsmu1wY-q2lAZ3X1+pmp - OPENVPN_PASSWORD=knCl1Zl5PHz4HMWVCGR77dYa +{% endif %} +{% if service.name == 'tl' and inventory_hostname in service.vm %} torrentleech: image: qbittorrentofficial/qbittorrent-nox container_name: torrentleech @@ -198,7 +209,9 @@ services: volumes: - /opt/docker/config/torrentleech/config:/config - /media/docker/data/arr_downloads:/downloads +{% endif %} +{% if service.name == 'qbit' and inventory_hostname in service.vm %} qbit: image: qbittorrentofficial/qbittorrent-nox container_name: qbit @@ -217,7 +230,7 @@ services: - /media/docker/data/arr_downloads:/downloads {% endif %} -{% if enable_prometheus %} +{% if service.name == 'prometheus' and inventory_hostname in service.vm %} prometheus: image: prom/prometheus container_name: prometheus @@ -235,7 +248,7 @@ services: - prometheus_data:/prometheus/ {% endif %} -{% if enable_grafana %} +{% if service.name == 'grafana' and inventory_hostname in service.vm %} grafana: image: grafana/grafana-oss container_name: grafana @@ -254,7 +267,7 @@ services: - /opt/docker/config/grafana/config/:/etc/grafana/ {% endif %} -{% if enable_ddns_updater %} +{% if service.name == 'ddns' and inventory_hostname in service.vm %} ddns-updater: container_name: ddns-updater image: "ghcr.io/qdm12/ddns-updater" @@ -267,7 +280,7 @@ services: - "/opt/docker/config/ddns-updater/data/:/updater/data/" {% endif %} -{% if enable_homeassistant %} +{% if service.name == 'hass' and inventory_hostname in service.vm %} homeassistant: container_name: homeassistant image: "ghcr.io/home-assistant/home-assistant:stable" @@ -287,7 +300,7 @@ services: - 5683:5683/udp {% endif %} -{% if enable_stirling %} +{% if service.name == 'pdf' and inventory_hostname in service.vm %} stirling: container_name: stirling image: frooodle/s-pdf:latest @@ -298,7 +311,7 @@ services: net: {} {% endif %} -{% if enable_jellyfin %} +{% if service.name == 'jellyfin' and inventory_hostname in service.vm %} jellyfin: container_name: jellyfin image: jellyfin/jellyfin @@ -319,7 +332,7 @@ services: - "8096:8096" {% endif %} -{% if enable_paperless %} +{% if service.name == 'paperless' and inventory_hostname in service.vm %} paperless-broker: container_name: paperless-broker image: docker.io/library/redis:7 @@ -378,7 +391,7 @@ services: - "PAPERLESS_OCR_LANGUAGE=deu" {% endif %} -{% if enable_gitea %} +{% if service.name == 'git' and inventory_hostname in service.vm %} git: container_name: git image: gitea/gitea:1.20.5-rootless @@ -400,7 +413,7 @@ services: - USER_GID=1000 {% endif %} -{% if enable_changedetection %} +{% if service.name == 'changedetection' and inventory_hostname in service.vm %} changedetection: container_name: changedetection image: dgtlmoon/changedetection.io @@ -413,7 +426,7 @@ services: - "/opt/docker/config/changedetection/data/:/datastore" {% endif %} -{% if enable_calibre %} +{% if service.name == 'calibre' and inventory_hostname in service.vm %} calibre: container_name: calibre image: lscr.io/linuxserver/calibre-web:latest @@ -431,6 +444,7 @@ services: - "/opt/local/calibre/:/config" - "/media/docker/data/calibre/:/books" {% endif %} +{% endfor %} networks: net: diff --git a/roles/reverse_proxy/defaults/main.yml b/roles/reverse_proxy/defaults/main.yml new file mode 100644 index 0000000..070e7e0 --- /dev/null +++ b/roles/reverse_proxy/defaults/main.yml @@ -0,0 +1,6 @@ +--- +caddy_version: latest +caddy_config_path: /etc/caddy/Caddyfile +caddy_binary: ./caddy + +go_version: 1.23.4 diff --git a/roles/reverse_proxy/handlers/main.yml b/roles/reverse_proxy/handlers/main.yml new file mode 100644 index 0000000..56a3042 --- /dev/null +++ b/roles/reverse_proxy/handlers/main.yml @@ -0,0 +1,4 @@ +--- +- name: Restart Caddy + ansible.builtin.command: "{{ caddy_binary }} reload --config {{ caddy_config_path }}" + become: true diff --git a/roles/reverse_proxy/tasks/configure.yml b/roles/reverse_proxy/tasks/configure.yml new file mode 100644 index 0000000..3ab93d4 --- /dev/null +++ b/roles/reverse_proxy/tasks/configure.yml @@ -0,0 +1,15 @@ +--- +- name: Ensure Caddy configuration directory exists + ansible.builtin.file: + path: /etc/caddy + state: directory + mode: "0755" + become: true + +- name: Deploy Caddy configuration file + ansible.builtin.template: + src: Caddyfile.j2 + dest: "{{ caddy_config_path }}" + mode: "0644" + become: true + notify: Restart Caddy diff --git a/roles/reverse_proxy/tasks/install.yml b/roles/reverse_proxy/tasks/install.yml new file mode 100644 index 0000000..ffd0b95 --- /dev/null +++ b/roles/reverse_proxy/tasks/install.yml @@ -0,0 +1,32 @@ +--- +- name: Download xCaddy GPG key + ansible.builtin.get_url: + url: "https://dl.cloudsmith.io/public/caddy/xcaddy/gpg.key" + dest: /etc/apt/keyrings/caddy-xcaddy.asc + mode: "0644" + become: true + +- name: Add xCaddy repository to apt sources + ansible.builtin.apt_repository: + repo: "deb [signed-by=/etc/apt/keyrings/caddy-xcaddy.asc] https://dl.cloudsmith.io/public/caddy/xcaddy/deb/debian any-version main" + state: present + update_cache: true + become: true + +- name: Update apt cache + ansible.builtin.apt: + update_cache: true + become: true + +- name: Install xCaddy + ansible.builtin.apt: + name: xcaddy + state: present + become: true + +- name: Install Caddy + ansible.builtin.command: xcaddy build --with github.com/caddy-dns/netcup + environment: + PATH: "{{ ansible_env.PATH }}:/usr/local/go/bin" + register: xcaddy_build + failed_when: xcaddy_build.rc != 0 diff --git a/roles/reverse_proxy/tasks/main.yml b/roles/reverse_proxy/tasks/main.yml new file mode 100644 index 0000000..4e07a90 --- /dev/null +++ b/roles/reverse_proxy/tasks/main.yml @@ -0,0 +1,9 @@ +--- +- name: Install Prerequisites + ansible.builtin.include_tasks: prereq.yml +- name: Install Caddy + ansible.builtin.include_tasks: install.yml +- name: Configure Caddy + ansible.builtin.include_tasks: configure.yml +- name: Start Caddy + ansible.builtin.include_tasks: start.yml diff --git a/roles/reverse_proxy/tasks/prereq.yml b/roles/reverse_proxy/tasks/prereq.yml new file mode 100644 index 0000000..250585f --- /dev/null +++ b/roles/reverse_proxy/tasks/prereq.yml @@ -0,0 +1,44 @@ +--- +- name: Install prerequisites for Caddy + ansible.builtin.apt: + name: + - debian-keyring + - debian-archive-keyring + - apt-transport-https + - curl + state: present + update_cache: true + become: true + +- name: Remove existing Go installation + ansible.builtin.file: + path: /usr/local/go + state: absent + become: true + +- name: Download Go tarball + ansible.builtin.get_url: + url: "https://go.dev/dl/go{{ go_version }}.linux-amd64.tar.gz" + dest: "/tmp/go{{ go_version }}.linux-amd64.tar.gz" + mode: "0755" + +- name: Extract Go tarball to /usr/local + ansible.builtin.unarchive: + src: /tmp/go1.23.4.linux-amd64.tar.gz + dest: /usr/local + remote_src: true + become: true + register: go_install + +- name: Ensure Go binary path is added to /etc/profile + ansible.builtin.lineinfile: + path: /etc/profile + line: "PATH=$PATH:/usr/local/go/bin" + state: present + regexp: "^PATH=.*:/usr/local/go/bin$" + become: true + +- name: Source /etc/profile to update PATH for the current session + ansible.builtin.shell: "source /etc/profile" + args: + executable: /bin/bash diff --git a/roles/reverse_proxy/tasks/start.yml b/roles/reverse_proxy/tasks/start.yml new file mode 100644 index 0000000..1626c20 --- /dev/null +++ b/roles/reverse_proxy/tasks/start.yml @@ -0,0 +1,4 @@ +--- +- name: Ensure Caddy service is running + ansible.builtin.command: "{{ caddy_binary }} start --config {{ caddy_config_path }}" + become: true diff --git a/roles/reverse_proxy/templates/Caddyfile.j2 b/roles/reverse_proxy/templates/Caddyfile.j2 new file mode 100644 index 0000000..d98c4cf --- /dev/null +++ b/roles/reverse_proxy/templates/Caddyfile.j2 @@ -0,0 +1,26 @@ +{ + email {{ caddy.admin_email | default('admin@example.com') }} + acme_ca {{ caddy.acme_ca | default('https://acme-v02.api.letsencrypt.org/directory') }} +} + +{% for service in services %} +{{ service.name }}.{{ domain }} { + {% for vm in service.vm %} + reverse_proxy {{ hostvars[vm].ansible_host }}:{{ service.port }} + {% endfor %} + log { + output file /var/log/caddy/{{ service.name }}.log + format json + } + tls { + dns netcup { + customer_number {{ vault.netcup.customer_number }} + api_key {{ vault.netcup.api_key}} + api_password {{ vault.netcup.api_password }} + } + propagation_timeout 900s + propagation_delay 600s + resolvers 1.1.1.1 + } +} +{% endfor %} diff --git a/secrets.yml.skeleton b/secrets.yml.skeleton index 06f5127..28efcbf 100644 --- a/secrets.yml.skeleton +++ b/secrets.yml.skeleton @@ -31,3 +31,9 @@ vault: sudo: ........ host02: sudo: ........ + lb: + sudo: ........ + netcup: + customer_number: ........ + api_key: ........ + api_password: ........