diff --git a/group_vars/appservers/vars.yml b/group_vars/appservers/vars.yml index 093e53b..55acae4 100644 --- a/group_vars/appservers/vars.yml +++ b/group_vars/appservers/vars.yml @@ -1,6 +1,16 @@ # vim: ft=yaml.ansible # code: language=ansible --- +apps_include: + - nginx + - postfix + - ipfs + - monerod + - nextcloud + - snowflake + - restic + - watchtower + redis_passwords: nextcloud: "{{ vault_redis_passwords.nextcloud }}" diff --git a/group_vars/cloud/vars.yml b/group_vars/cloud/vars.yml new file mode 100644 index 0000000..bda5152 --- /dev/null +++ b/group_vars/cloud/vars.yml @@ -0,0 +1,6 @@ +# vim: ft=yaml.ansible +# code: language=ansible +--- +base_domain: sapti.me +internal_subnet: 10.2.3.0/24 +tls_email: "{{ vault_tls_email }}" diff --git a/group_vars/cloud/vault.yml b/group_vars/cloud/vault.yml new file mode 100644 index 0000000..3f0fc00 --- /dev/null +++ b/group_vars/cloud/vault.yml @@ -0,0 +1,9 @@ +$ANSIBLE_VAULT;1.1;AES256 +30383865626464383438663561653566646634313937376332336630616566663434373638366337 +6632323765633062373438643335656530366537626662370a376165366165303064353863306331 +32333430303239643435343865376434623038303238313962303161633338373261663761633361 +3361623463346563630a323064333831383535336335363930333235623966663663633265636463 +32393139373931336565323838626361616264376333613939626263393837323462373435303731 +37353866623639343634393566336131343564616237383762306436616535373138376561613230 +63623866323265356338613232626432666335323462653938646562353866366463353965623066 +63313138663861626634 diff --git a/group_vars/production/vars.yml b/group_vars/production/vars.yml index ba34061..afa3824 100644 --- a/group_vars/production/vars.yml +++ b/group_vars/production/vars.yml @@ -1,17 +1,17 @@ # vim: ft=yaml.ansible # code: language=ansible --- -apps_base_domain: sapti.me +base_domain: sapti.me internal_subnet: 10.2.16.0/24 postgresql_version: 14 -databases: - nextcloud: - username: nextcloud - password: "{{ vault_db_passwords.nextcloud }}" - db_inventory_hostname: sapt-labp-db01 db_host: "{{ hostvars[db_inventory_hostname].internal_ipv4 }}" proxy_inventory_hostname: sapt-labr-prx01 proxy_host: "{{ hostvars[proxy_inventory_hostname].internal_ipv4 }}" + +databases: + nextcloud: + username: nextcloud + password: "{{ vault_db_passwords.nextcloud }}" diff --git a/group_vars/publicservers/vars.yml b/group_vars/publicservers/vars.yml index 6a04d69..7cb216d 100644 --- a/group_vars/publicservers/vars.yml +++ b/group_vars/publicservers/vars.yml @@ -1,4 +1,9 @@ # vim: ft=yaml.ansible # code: language=ansible --- -apps_base_domain: sapti.me +apps_include: + - caddy + - searxng + - watchtower + +searxng_secret_key: "{{ vault_searxng_secret_key }}" diff --git a/group_vars/publicservers/vault.yml b/group_vars/publicservers/vault.yml new file mode 100644 index 0000000..088fabf --- /dev/null +++ b/group_vars/publicservers/vault.yml @@ -0,0 +1,11 @@ +$ANSIBLE_VAULT;1.1;AES256 +61623537323039313538373562663036346638653365326439373333333236613163633764343665 +3434613163333131343732316662303065646462343135300a613630313234316663336437643662 +61323861313833383830303732306433653339326231313466643131616438353836666661306564 +6535383837633264650a393133636536643434326537636633366665313164373463633862343034 +36613030393538373464353166616164363430663361343534623135376563303663633266666332 +32383336326563333535646265643638376661356631356434303963646532356133306266353736 +37363639613166353038383736633034656637623638656662393539633538663432346665316136 +63653130303762323562663562623065326263356561626330636337366164353634323062303062 +66356531636261313462656265343731396333393263653733333530386439356665323765393030 +3231663733393164383865336531333932393863666636336539 diff --git a/group_vars/staging/vars.yml b/group_vars/staging/vars.yml index 12b00b4..e59eba5 100644 --- a/group_vars/staging/vars.yml +++ b/group_vars/staging/vars.yml @@ -1,17 +1,17 @@ # vim: ft=yaml.ansible # code: language=ansible --- -apps_base_domain: staging.sapti.me +base_domain: staging.sapti.me internal_subnet: 10.2.19.0/24 postgresql_version: 14 -databases: - nextcloud: - username: nextcloud - password: "{{ vault_db_passwords.nextcloud }}" - db_inventory_hostname: sapt-labs-db01 db_host: "{{ hostvars[db_inventory_hostname].internal_ipv4 }}" proxy_inventory_hostname: sapt-labr-prx01 proxy_host: "{{ hostvars[proxy_inventory_hostname].internal_ipv4 }}" + +databases: + nextcloud: + username: nextcloud + password: "{{ vault_db_passwords.nextcloud }}" diff --git a/roles/apps/defaults/main.yml b/roles/apps/defaults/main.yml index 3c0895e..96015e4 100644 --- a/roles/apps/defaults/main.yml +++ b/roles/apps/defaults/main.yml @@ -2,11 +2,19 @@ # code: language=ansible --- apps_data_root: "{{ data_fs }}/apps" +apps_base_domain: "{{ base_domain }}" apps_local_domain: local.{{ apps_base_domain }} apps_shared_docker_network: apps_network apps_postfix_docker_network: postfix_network apps_vars: + caddy: + backup: false + sender: false + extra_tasks: true + docker_ipv4: 172.17.2.48 + version: 2.7.6-alpine + nginx: backup: false sender: false @@ -44,6 +52,14 @@ apps_vars: version: 28-apache redis_version: 7-alpine + searxng: + backup: false + sender: false + extra_tasks: true + domain: search.{{ apps_base_domain }} + version: latest + redis_version: 7-alpine + snowflake: backup: false sender: false diff --git a/roles/apps/tasks/extra_tasks/caddy.yml b/roles/apps/tasks/extra_tasks/caddy.yml new file mode 100644 index 0000000..02502ba --- /dev/null +++ b/roles/apps/tasks/extra_tasks/caddy.yml @@ -0,0 +1,21 @@ +# vim: ft=yaml.ansible +# code: language=ansible +--- +- name: Create subdirectories for Caddy data + ansible.builtin.file: + path: "{{ apps_data_root }}/caddy/data/caddy-{{ item }}" + owner: root + group: root + mode: u=rwx,go= + state: directory + loop: + - config + - data + +- name: Copy Caddyfile + ansible.builtin.template: + src: caddy/Caddyfile.j2 + dest: "{{ apps_data_root }}/caddy/data/Caddyfile" + owner: root + group: root + mode: u=rw,g=r,o=r diff --git a/roles/apps/tasks/extra_tasks/searxng.yml b/roles/apps/tasks/extra_tasks/searxng.yml new file mode 100644 index 0000000..958da54 --- /dev/null +++ b/roles/apps/tasks/extra_tasks/searxng.yml @@ -0,0 +1,18 @@ +# vim: ft=yaml.ansible +# code: language=ansible +--- +- name: Create subdirectory for SearXNG Redis data + ansible.builtin.file: + path: "{{ apps_data_root }}/searxng/data/redis" + owner: '999' + group: '1000' + mode: u=rwx,g=rx,o=rx + state: directory + +- name: Copy SearXNG config + ansible.builtin.template: + src: searxng/settings.yml.j2 + dest: "{{ apps_data_root }}/searxng/data/settings.yml" + owner: root + group: root + mode: u=rw,g=r,o=r diff --git a/roles/apps/templates/caddy/Caddyfile.j2 b/roles/apps/templates/caddy/Caddyfile.j2 new file mode 100644 index 0000000..7b55d33 --- /dev/null +++ b/roles/apps/templates/caddy/Caddyfile.j2 @@ -0,0 +1,75 @@ +{# code: language=ansible-jinja #} +# THIS FILE IS MANAGED BY ANSIBLE + +{{ apps_vars.searxng.domain }} { + tls {{ tls_email }} + + log { + output discard + } + + @api { + path /config + path /healthz + path /stats/errors + path /stats/checker + } + + @static { + path /static/* + } + + @notstatic { + not path /static/* + } + + @imageproxy { + path /image_proxy + } + + @notimageproxy { + not path /image_proxy + } + + header { + Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" + X-XSS-Protection "1; mode=block" + X-Content-Type-Options "nosniff" + Permissions-Policy "accelerometer=(),ambient-light-sensor=(),autoplay=(),camera=(),encrypted-media=(),focus-without-user-activation=(),geolocation=(),gyroscope=(),magnetometer=(),microphone=(),midi=(),payment=(),picture-in-picture=(),speaker=(),sync-xhr=(),usb=(),vr=(),interest-cohort=()" + Referrer-Policy "no-referrer" + X-Robots-Tag "noindex, noarchive, nofollow" + -Server + } + + header @api { + Access-Control-Allow-Methods "GET, OPTIONS" + Access-Control-Allow-Origin "*" + } + + header @static { + Cache-Control "public, max-age=31536000" + defer + } + + header @notstatic { + Cache-Control "no-cache, no-store" + Pragma "no-cache" + } + + header @imageproxy { + Content-Security-Policy "default-src 'none'; img-src 'self' data:" + } + + header @notimageproxy { + Content-Security-Policy "upgrade-insecure-requests; default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; form-action 'self' https://github.com/searxng/searxng/issues/new; font-src 'self'; frame-ancestors 'self'; base-uri 'self'; connect-src 'self' https://overpass-api.de; img-src 'self' data: https://*.tile.openstreetmap.org; frame-src https://www.youtube-nocookie.com https://player.vimeo.com https://www.dailymotion.com https://www.deezer.com https://www.mixcloud.com https://w.soundcloud.com https://embed.spotify.com" + } + + handle { + encode zstd gzip + + reverse_proxy searxng:8080 { + header_up X-Forwarded-Port {http.request.port} + header_up X-Forwarded-Proto {http.request.scheme} + } + } +} diff --git a/roles/apps/templates/compose-files/caddy.yml.j2 b/roles/apps/templates/compose-files/caddy.yml.j2 new file mode 100644 index 0000000..5fa900e --- /dev/null +++ b/roles/apps/templates/compose-files/caddy.yml.j2 @@ -0,0 +1,29 @@ +{# code: language=ansible-jinja #} +# THIS FILE IS MANAGED BY ANSIBLE + +version: "3.8" + +services: + web: + image: caddy:{{ apps_vars.caddy.version }} + restart: always + networks: + {{ apps_shared_docker_network }}: + ipv4_address: {{ apps_vars.caddy.docker_ipv4 }} + ports: + - 80:80/tcp + - 443:443/tcp + - 443:443/udp + volumes: + - "./data/Caddyfile:/etc/caddy/Caddyfile:ro" + - "./data/caddy-config:/config:rw" + - "./data/caddy-data:/data:rw" + cap_add: + - net_bind_service + - dac_override + cap_drop: + - all + +networks: + {{ apps_shared_docker_network }}: + external: true diff --git a/roles/apps/templates/compose-files/nextcloud.yml.j2 b/roles/apps/templates/compose-files/nextcloud.yml.j2 index 0b46631..9d300f8 100644 --- a/roles/apps/templates/compose-files/nextcloud.yml.j2 +++ b/roles/apps/templates/compose-files/nextcloud.yml.j2 @@ -56,7 +56,9 @@ services: - app networks: - {{ apps_postfix_docker_network }}: - external: true {{ apps_shared_docker_network }}: external: true +{% if 'postfix' in apps_include %} + {{ apps_postfix_docker_network }}: + external: true +{% endif %} diff --git a/roles/apps/templates/compose-files/searxng.yml.j2 b/roles/apps/templates/compose-files/searxng.yml.j2 new file mode 100644 index 0000000..b8c3ece --- /dev/null +++ b/roles/apps/templates/compose-files/searxng.yml.j2 @@ -0,0 +1,44 @@ +{# code: language=ansible-jinja #} +# THIS FILE IS MANAGED BY ANSIBLE + +version: "3.8" + +services: + redis: + image: redis:{{ apps_vars.searxng.redis_version }} + restart: always + command: redis-server --save 60 1 --appendonly no + volumes: + - "./data/redis:/data:rw" + cap_add: + - dac_override + - setuid + - setgid + cap_drop: + - all + + app: + image: searxng/searxng:{{ apps_vars.searxng.version }} + restart: always + environment: + SEARXNG_BASE_URL: https://{{ apps_vars.searxng.domain }} + networks: + default: + {{ apps_shared_docker_network }}: + aliases: + - searxng + volumes: + - "./data/settings.yml:/etc/searxng/settings.yml:ro" + cap_add: + - chown + - dac_override + - setuid + - setgid + cap_drop: + - all + depends_on: + - redis + +networks: + {{ apps_shared_docker_network }}: + external: true diff --git a/roles/apps/templates/scripts/deploy.sh.j2 b/roles/apps/templates/scripts/deploy.sh.j2 index dd7fe4f..3f18826 100644 --- a/roles/apps/templates/scripts/deploy.sh.j2 +++ b/roles/apps/templates/scripts/deploy.sh.j2 @@ -1,6 +1,5 @@ +{# code: language=ansible-jinja #} # THIS FILE IS MANAGED BY ANSIBLE -# vim: ft=bash -# code: language=bash #!/usr/bin/env bash ARG="$1" @@ -22,10 +21,19 @@ restart) docker compose -f $app/docker-compose.yml restart done ;; +{% if 'caddy' in apps_include %} +reload-proxy) + CADDYFILE="/etc/caddy/Caddyfile" + docker compose -f $APPS_DIR/caddy/docker-compose.yml exec web \ + sh -c "caddy validate -c $CADDYFILE && caddy reload -c $CADDYFILE" \ + 2>/dev/null + ;; +{% elif 'nginx' in apps_include %} reload-proxy) docker compose -f $APPS_DIR/nginx/docker-compose.yml exec web \ sh -c "nginx -t && nginx -s reload" ;; +{% endif %} *) echo "Unrecognized argument" exit 1 diff --git a/roles/apps/templates/searxng/settings.yml.j2 b/roles/apps/templates/searxng/settings.yml.j2 new file mode 100644 index 0000000..95259fc --- /dev/null +++ b/roles/apps/templates/searxng/settings.yml.j2 @@ -0,0 +1,99 @@ +# THIS FILE IS MANAGED BY ANSIBLE +# vim: ft=yaml +# code: language=yaml +--- +use_default_settings: true + +general: + debug: false + instance_name: Sam's SearXNG + privacypolicy_url: https://samsapti.dev/privacy + contact_url: https://samsapti.dev/contact + enable_metrics: true + +server: + secret_key: "{{ searxng_secret_key }}" + image_proxy: true + http_protocol_version: '1.1' + method: GET + limiter: true + public_instance: true + +ui: + results_on_new_tab: false + center_alignment: true + theme_args: + simple_style: auto + +redis: + url: redis://redis:6379/0 + +search: + formats: + - html + safe_search: 2 + suspended_times: + SearxEngineAccessDenied: 0 + SearxEngineCaptcha: 600 + SearxEngineTooManyRequests: 600 + cf_SearxEngineCaptcha: 600 + cf_SearxEngineAccessDenied: 1200 + recaptcha_SearxEngineCaptcha: 600 + +outgoing: + enable_http2: true + source_ips: + - 0.0.0.0 + +enabled_plugins: + - 'Hash plugin' + - 'Self Informations' + - 'Tracker URL remover' + - 'Hostname replace' + +hostname_replace: + '^(.*\.)?youtube\.com$': 'yewtu.be' + '^(.*\.)?youtu\.be$': 'yewtu.be' + '^(.*\.)?youtube-noocookie\.com$': 'yewtu.be' + '^(www\.)?twitter\.com$': 'nitter.net' + '^(.*\.)?(m\.)?wiktionary\.org$': '\1m.wiktionary.org' + +engines: + - name: bing + disabled: false + + - name: brave + disabled: true + + - name: ddg definitions + disabled: false + + - name: duckduckgo + disabled: false + + - name: duckduckgo images + disabled: false + + - name: google + disabled: false + + - name: mojeek + disabled: true + + - name: qwant + disabled: true + + - name: qwant images + disabled: false + + - name: startpage + disabled: true + + - name: wikidata + disabled: true + + - name: wikinews + disabled: true + + - name: yahoo + disabled: false diff --git a/roles/proxy/templates/caddy/Caddyfile.j2 b/roles/proxy/templates/caddy/Caddyfile.j2 index 7032dbd..0337b61 100644 --- a/roles/proxy/templates/caddy/Caddyfile.j2 +++ b/roles/proxy/templates/caddy/Caddyfile.j2 @@ -4,7 +4,7 @@ {% for env in proxy_environments %} # BEGIN Environment: {{ env }} -cloud.{{ proxy_vars[env].app01.apps_base_domain }} { +cloud.{{ proxy_vars[env].app01.base_domain }} { tls {{ tls_email }} { dns njalla {{ njalla_api_token }} } @@ -29,9 +29,9 @@ cloud.{{ proxy_vars[env].app01.apps_base_domain }} { {% endif %} } -ipfs-gateway.{{ proxy_vars[env].app01.apps_base_domain }}, -*.ipfs.ipfs-gateway.{{ proxy_vars[env].app01.apps_base_domain }}, -*.ipns.ipfs-gateway.{{ proxy_vars[env].app01.apps_base_domain }} { +ipfs-gateway.{{ proxy_vars[env].app01.base_domain }}, +*.ipfs.ipfs-gateway.{{ proxy_vars[env].app01.base_domain }}, +*.ipns.ipfs-gateway.{{ proxy_vars[env].app01.base_domain }} { tls {{ tls_email }} { dns njalla {{ njalla_api_token }} } @@ -56,7 +56,7 @@ ipfs-gateway.{{ proxy_vars[env].app01.apps_base_domain }}, {% endif %} } -ipfs.local.{{ proxy_vars[env].app01.apps_base_domain }} { +ipfs.local.{{ proxy_vars[env].app01.base_domain }} { tls {{ tls_email }} { dns njalla {{ njalla_api_token }} } @@ -77,7 +77,7 @@ ipfs.local.{{ proxy_vars[env].app01.apps_base_domain }} { respond 403 } -xmr.local.{{ proxy_vars[env].app01.apps_base_domain }} { +xmr.local.{{ proxy_vars[env].app01.base_domain }} { tls {{ tls_email }} { dns njalla {{ njalla_api_token }} } diff --git a/roles/proxy/templates/scripts/deploy.sh.j2 b/roles/proxy/templates/scripts/deploy.sh.j2 index f09bd13..5ad0cdd 100644 --- a/roles/proxy/templates/scripts/deploy.sh.j2 +++ b/roles/proxy/templates/scripts/deploy.sh.j2 @@ -1,6 +1,5 @@ +{# code: language=ansible-jinja #} # THIS FILE IS MANAGED BY ANSIBLE -# vim: ft=bash -# code: language=bash #!/usr/bin/env bash ARG="$1"