diff options
31 files changed, 412 insertions, 99 deletions
@@ -59,3 +59,6 @@ - name: ci ansible.builtin.import_playbook: playbooks/ci.yml + +- name: oci + ansible.builtin.import_playbook: playbooks/oci.yml diff --git a/group_vars/all.yml b/group_vars/all.yml index f6747d0..717a983 100644 --- a/group_vars/all.yml +++ b/group_vars/all.yml @@ -22,6 +22,8 @@ domain: "liz.coffee" idm_domain: "idm.{{ domain }}" headscale_host: "vpn.{{ domain }}" mail_domain: "mail.{{ domain }}" +oci_domain: "oci.{{ domain }}" +passwd_domain: "passwd.{{ domain }}" info_mail_user: "info" info_mail: "{{ info_mail_user }}@{{ domain }}" diff --git a/group_vars/labdns.yml b/group_vars/labdns.yml index c1985c9..d0b0c6a 100644 --- a/group_vars/labdns.yml +++ b/group_vars/labdns.yml @@ -3,6 +3,7 @@ labdns_base: "{{ swarm_base }}/labdns" internal_services: + - oci.{{ domain }} - ci.{{ domain }} - test.{{ domain }} - bin.{{ domain }} diff --git a/group_vars/mail.yml b/group_vars/mail.yml index 1114ca8..0a964e2 100644 --- a/group_vars/mail.yml +++ b/group_vars/mail.yml @@ -16,15 +16,16 @@ ldap_server_host: "ldaps://{{ ldap_server }}:3636" ldap_search_base: "{{ 'dc=' ~ idm_domain | regex_replace('\\.', ',dc=') }}" ldap_bind_dn: "dn=token" -ldap_query_filter_user: "(&(class=account)(emailprimary=%s))" -ldap_query_filter_group: "(&(class=group)(mail=%s))" -ldap_query_filter_alias: "(&(class=account)(emailalternative=%s))" +ldap_memberof_query: "(memberof=mail)" +ldap_query_filter_user: "(&(class=account)(emailprimary=%s){{ ldap_memberof_query }})" +ldap_query_filter_group: "(&(class=group)(mail=%s){{ ldap_memberof_query }})" +ldap_query_filter_alias: "(&(class=account)(emailalternative=%s)(memberof=mail){{ ldap_memberof_query }})" ldap_query_filter_domain: "(mail=*@%s)" -ldap_query_filter_senders: "(&(class=account)(|(emailprimary=%s)(emailalternative=%s)))" +ldap_query_filter_senders: "(&(class=account)(|(emailprimary=%s)(emailalternative=%s)){{ ldap_memberof_query }})" dovecot_user_filter: > - (&(class=account)(name=%u) - (memberOf=cn=mail,{{ ldap_search_base }})) + (&(class=account)(name=%u){{ ldap_memberof_query }}) + dovecot_auth_bind_userdn: "name=%u,{{ ldap_search_base }}" roundcube_default_host: "ssl://{{ mail_domain }}" diff --git a/group_vars/oci.yml b/group_vars/oci.yml new file mode 100644 index 0000000..7bc2db0 --- /dev/null +++ b/group_vars/oci.yml @@ -0,0 +1,3 @@ +--- + +oci_base: "{{ swarm_base }}/oci" diff --git a/group_vars/swarm_cluster.yml b/group_vars/swarm_cluster.yml index bf0744d..25324ae 100644 --- a/group_vars/swarm_cluster.yml +++ b/group_vars/swarm_cluster.yml @@ -1,3 +1,6 @@ --- swarm_base: "{{ ceph_base }}/docker" + +oci_username: "readonly" +oci_password: "readonly" @@ -68,3 +68,6 @@ swarm-one ansible_host=10.128.0.201 ansible_user=serve ansible_connectio [ci] swarm-one ansible_host=10.128.0.201 ansible_user=serve ansible_connection=ssh ansible_become_password='{{ swarm_become_password }}' +[oci] +swarm-one ansible_host=10.128.0.201 ansible_user=serve ansible_connection=ssh ansible_become_password='{{ swarm_become_password }}' + diff --git a/playbooks/oci.yml b/playbooks/oci.yml new file mode 100644 index 0000000..be8a007 --- /dev/null +++ b/playbooks/oci.yml @@ -0,0 +1,7 @@ +--- + +- name: oci setup + hosts: oci + become: true + roles: + - oci diff --git a/playbooks/roles/ci/templates/stacks/docker-compose.yml b/playbooks/roles/ci/templates/stacks/docker-compose.yml index e2358e5..38e1b1c 100644 --- a/playbooks/roles/ci/templates/stacks/docker-compose.yml +++ b/playbooks/roles/ci/templates/stacks/docker-compose.yml @@ -1,69 +1,26 @@ -services: - db: - image: postgres - environment: - POSTGRES_DB: concourse - POSTGRES_PASSWORD: concourse_pass - POSTGRES_USER: concourse_user - PGDATA: /database - POSTGRES_HOST_AUTH_METHOD: trust - healthcheck: - test: ["CMD-SHELL", "pg_isready -U concourse_user -d concourse"] - interval: 3s - timeout: 3s - retries: 5 - networks: - - ci - - worker: - image: concourse/concourse - command: worker - privileged: true - depends_on: - web: - condition: service_healthy - volumes: - - {{ ci_base }}/volumes/keys/worker:/concourse-keys - networks: - - ci - stop_signal: SIGUSR2 - environment: - CONCOURSE_TSA_HOST: web:2222 - CONCOURSE_GARDEN_DNS_PROXY_ENABLE: "true" +--- - web: - image: concourse - depends_on: - db: - condition: service_healthy +services: + laminard: + image: oci.liz.coffee/img/laminar-ciworker:latest volumes: - - {{ ci_base }}/volumes/keys/web:/concourse-keys + - {{ ci_base }}/volumes/laminar:/var/lib/laminar + - /var/run/docker.sock:/var/run/docker.sock + healthcheck: + test: ["CMD-SHELL", "/usr/bin/laminarc show-jobs"] + timeout: 15s + interval: 30s + retries: 3 + start_period: 5s environment: + - BW_CLIENTID={{ vaultwarden_client_id }} + - BW_CLIENTSECRET={{ vaultwarden_client_secret }} + - BW_PASSWORD={{ vaultwarden_master_password }} - TZ={{ timezone }} - DEPLOYMENT_TIME={{ deployment_time }} - - CONCOURSE_POSTGRES_HOST: db - - CONCOURSE_POSTGRES_USER: concourse_user - - CONCOURSE_POSTGRES_PASSWORD: concourse_pass - - CONCOURSE_POSTGRES_DATABASE: concourse - - CONCOURSE_EXTERNAL_URL: https://{{ ci_domain }} - - - # instead of relying on the default "detect" - - CONCOURSE_WORKER_BAGGAGECLAIM_DRIVER=overlay - - CONCOURSE_CLUSTER_NAME={{ ci_domain }} - - - CONCOURSE_OIDC_DISPLAY_NAME={{ domain }} <3 - - CONCOURSE_OIDC_CLIENT_ID=concourse - - CONCOURSE_OIDC_CLIENT_SECRET={{ concourse_secret_key }} - - CONCOURSE_OID_ISSUER=https://{{ idm_domain }}/oauth2/openid/concourse/ networks: - ci - proxy - healthcheck: - test: ["CMD-SHELL", "curl", "--fail", "http://localhost:8080"] - timeout: 15s - interval: 30s - retries: 3 - start_period: 5s deploy: mode: replicated update_config: @@ -84,5 +41,7 @@ services: networks: ci: + driver: overlay + attachable: true proxy: external: true diff --git a/playbooks/roles/ci/templates/volumes/laminar/.gitkeep b/playbooks/roles/ci/templates/volumes/laminar/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/playbooks/roles/ci/templates/volumes/laminar/.gitkeep diff --git a/playbooks/roles/ci/templates/volumes/laminar/jobs/build_image.run b/playbooks/roles/ci/templates/volumes/laminar/jobs/build_image.run new file mode 100755 index 0000000..ed7bf21 --- /dev/null +++ b/playbooks/roles/ci/templates/volumes/laminar/jobs/build_image.run @@ -0,0 +1,36 @@ +#!/bin/bash +# usage: laminarc queue build_publish_image registry="oci.liz.coffee" \ +# repo="src/cgit" tag="latest" remote="ssh://src.liz.coffee:2222/cgit" \ +# rev="<sha>" image_file="Dockerfile" + +set -e + +declare -a args=("$registry" "$repo" "$tag" "$remote" "$rev" "$image_file") +for arg in "${args[@]}" +do + if [[ ! "$arg" =~ ^[[:alnum:]:_\.\/\-]*$ ]]; then + echo "Invalid argument format. Don't be sneaky snek (-_-)." + exit 1 + fi +done + +log "Logging into registry $registry" +registry_username="$(get_secret $registry | jq -r ".login.username")" +get_secret $registry | jq -r ".login.password" \ + | docker login --username "$registry_username" --password-stdin "$registry" + +log "Cloning remote $remote" +r=$(echo "build-$(date --iso-8601=seconds)") +git clone "$remote" "$r" && cd "$r" +git checkout "$rev" + +image_tag="$registry/$repo:$tag" +log "Building image $image_tag" +env -i HOME="$HOME" bash -l -c "docker build . -t '$image_tag' -f '$image_file'" + +log "Pushing $image_tag" +docker push "$image_tag" + +cd - +rm -rf "$r" +docker logout "$registry" diff --git a/playbooks/roles/ci/templates/volumes/laminar/jobs/playbook.run b/playbooks/roles/ci/templates/volumes/laminar/jobs/playbook.run new file mode 100755 index 0000000..181a050 --- /dev/null +++ b/playbooks/roles/ci/templates/volumes/laminar/jobs/playbook.run @@ -0,0 +1,25 @@ +#!/bin/bash +# usage: laminarc queue playbook remote="ssh://src.liz.coffee:2222/infra" playbooks="deploy.yml playbooks/labdns.yml" + +set -e + +declare -a args=("$remote" "$playbooks") +for arg in "${args[@]}" +do + if [[ ! "$arg" =~ ^[[:alnum:]:_\ \.\/\-]*$ ]]; then + echo "Invalid argument format. Don't be sneaky snek (-_-)." + exit 1 + fi +done + +log "Cloning remote $remote" +r=$(echo "ansible-$(date --iso-8601=seconds)") +git clone "$remote" "$r" && cd "$r" + +get_secret "ansible_secrets" | jq -r '.notes' > secrets.yml +private_key=$(get_secret "ssh_key" | jq -r '.notes') + +env -i HOME="$HOME" ssh-agent bash -c "ssh-add <(echo \"$private_key\") && ansible-playbook -e @secrets.yml $playbooks" + +cd - +rm -rf "$r" diff --git a/playbooks/roles/ci/templates/volumes/laminar/scripts/get_secret b/playbooks/roles/ci/templates/volumes/laminar/scripts/get_secret new file mode 100755 index 0000000..2774651 --- /dev/null +++ b/playbooks/roles/ci/templates/volumes/laminar/scripts/get_secret @@ -0,0 +1,35 @@ +#!/bin/bash + +bw config server "https://{{ passwd_domain }}" +bw login --apikey --quiet +bw unlock --passwordenv BW_PASSWORD --quiet + +# https://github.com/bitwarden/clients/issues/3366 +function bw_get() { + local pwd + local count + local organisation=${2:-notnull} + + count=$(bw list items --pretty --organizationid ${organisation} | jq -r '[.[] | select(.name=="'$1'")] | length') + + if [[ "$count" -gt 1 ]]; then + echo "Multiple items found" + return 1 + fi + + if [[ "$count" -lt 1 ]]; then + echo "No items found" + return 1 + fi + + pwd=$(bw list items --pretty --organizationid ${organisation} | jq -r '.[] | select(.name=="'$1'")') + if [[ -z "$pwd" ]]; then + echo "Password not found" + return 1 + fi + + echo "$pwd" +} + +bw_get $@ +bw --quiet lock diff --git a/playbooks/roles/ci/templates/volumes/laminar/scripts/log b/playbooks/roles/ci/templates/volumes/laminar/scripts/log new file mode 100755 index 0000000..180fa33 --- /dev/null +++ b/playbooks/roles/ci/templates/volumes/laminar/scripts/log @@ -0,0 +1,3 @@ +#!/bin/bash + +echo `date +"%d-%m-%Y %H:%M:%S"` " - " "${@}" diff --git a/playbooks/roles/labdns/templates/volumes/unbound/a-records.conf b/playbooks/roles/labdns/templates/volumes/unbound/a-records.conf index 5eefeb5..d0c9517 100644 --- a/playbooks/roles/labdns/templates/volumes/unbound/a-records.conf +++ b/playbooks/roles/labdns/templates/volumes/unbound/a-records.conf @@ -3,6 +3,6 @@ local-data: "{{ service }}. A {{ loadbalancer_ip }}" {% endfor %} -# lucina.cloud +# TODO: for lucina.cloud, for now... local-zone: "lucina.cloud." redirect local-data: "lucina.cloud. A 10.128.0.44" diff --git a/playbooks/roles/mail/templates/stacks/docker-compose.yml b/playbooks/roles/mail/templates/stacks/docker-compose.yml index 38e63cb..d7f8984 100644 --- a/playbooks/roles/mail/templates/stacks/docker-compose.yml +++ b/playbooks/roles/mail/templates/stacks/docker-compose.yml @@ -1,7 +1,6 @@ services: roundcube: image: roundcube/roundcubemail:latest - restart: always volumes: - {{ mail_base }}/volumes/data/roundcube/db:/var/roundcube/db - {{ mail_base }}/volumes/data/roundcube/config:/var/roundcube/config/ @@ -38,9 +37,9 @@ services: mailserver: image: ghcr.io/docker-mailserver/docker-mailserver:latest hostname: {{ mail_domain }} +{% if homelab_build %} command: - /scripts/wait-for-cert.sh -{% if homelab_build %} healthcheck: disable: true {% else %} @@ -59,7 +58,6 @@ services: - '4190:4190' - '110:110' - '995:995' - stop_grace_period: 30s deploy: mode: replicated replicas: 1 @@ -104,8 +102,6 @@ services: - SASLAUTHD_MECHANISMS=rimap - SASLAUTHD_MECH_OPTIONS=127.0.0.1 - - DOVECOT_USER_FILTER={{ dovecot_user_filter }} - - ENABLE_OAUTH2=1 - OAUTH2_INTROSPECTION_URL={{ roundcube_oauth2_user_uri }} diff --git a/playbooks/roles/mail/templates/volumes/data/dms/config/dovecot-ldap.conf b/playbooks/roles/mail/templates/volumes/data/dms/config/dovecot-ldap.conf index 6a14553..e55a861 100644 --- a/playbooks/roles/mail/templates/volumes/data/dms/config/dovecot-ldap.conf +++ b/playbooks/roles/mail/templates/volumes/data/dms/config/dovecot-ldap.conf @@ -8,3 +8,5 @@ dnpass = {{ email_ldap_api_token }} auth_bind = yes auth_bind_userdn = {{ dovecot_auth_bind_userdn }} +user_filter = {{ dovecot_user_filter }} +pass_filter = {{ dovecot_user_filter }} diff --git a/playbooks/roles/oci/tasks/main.yml b/playbooks/roles/oci/tasks/main.yml new file mode 100644 index 0000000..d9c3b56 --- /dev/null +++ b/playbooks/roles/oci/tasks/main.yml @@ -0,0 +1,8 @@ +--- + +- name: Deploy oci + ansible.builtin.import_tasks: manage-docker-swarm-service.yml + vars: + service_name: oci + template_render_dir: "../templates" + service_destination_dir: "{{ oci_base }}" diff --git a/playbooks/roles/oci/templates/stacks/docker-compose.yml b/playbooks/roles/oci/templates/stacks/docker-compose.yml new file mode 100644 index 0000000..8b40356 --- /dev/null +++ b/playbooks/roles/oci/templates/stacks/docker-compose.yml @@ -0,0 +1,48 @@ +services: + valkey: + image: valkey/valkey:8.0.2 + networks: + - oci + + oci: + image: ghcr.io/simple-registry/simple-registry:main + command: "server" + volumes: + - {{ oci_base }}/volumes/config.toml:/config.toml + - {{ oci_base }}/volumes/images:/images + environment: + - TZ={{ timezone }} + - DEPLOYMENT_TIME={{ deployment_time }} + - RUST_LOG=info + networks: + - proxy + - oci + healthcheck: + test: ["CMD", "/simple-registry", "scrub"] + timeout: 10s + interval: 30s + retries: 2 + start_period: 5s + deploy: + mode: replicated + update_config: + parallelism: 1 + failure_action: rollback + order: start-first + monitor: 5s + replicas: 1 + labels: + - traefik.enable=true + - traefik.swarm.network=proxy + - traefik.http.routers.oci.tls=true + - traefik.http.routers.oci.tls.certResolver=letsencrypt + - traefik.http.routers.oci.rule=Host(`{{ oci_domain }}`) + - traefik.http.routers.oci.entrypoints=websecure + - traefik.http.services.oci.loadbalancer.server.port=8000 + +networks: + oci: + attachable: true + driver: overlay + proxy: + external: true diff --git a/playbooks/roles/oci/templates/volumes/config.toml b/playbooks/roles/oci/templates/volumes/config.toml new file mode 100644 index 0000000..6d2f199 --- /dev/null +++ b/playbooks/roles/oci/templates/volumes/config.toml @@ -0,0 +1,35 @@ +[server] +bind_address = "0.0.0.0" +port = 8000 +streaming_chunk_size = "5MiB" + +[lock_store.redis] +url = "redis://valkey:6379" +ttl = 5 + +[cache_store.redis] +url = "redis://valkey:6379" +ttl = 5 + +[storage.fs] +root_dir = "/images" + +[observability.tracing] +endpoint = "http://127.0.0.1:4317" +sampling_rate = 1.0 + +[identity.ci] +username = "ci" +password = "{{ simple_registry_password_argon_encoded }}" + +[identity.readonly] +username = "readonly" +password = "$argon2i$v=19$m=16,t=2,p=1$TjJyTEdIZUJ6dFZkdlZvSg$qf8vG09O93Z/9vUMCgWNtA" # readonly + +[repository."img"] + +[repository."img".access_policy] +default_allow = false +rules = [ + 'request.action.startsWith("get-") || request.action.startsWith("list-") || identity.id == "ci"' +] diff --git a/playbooks/roles/oci/templates/volumes/images/.gitkeep b/playbooks/roles/oci/templates/volumes/images/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/playbooks/roles/oci/templates/volumes/images/.gitkeep diff --git a/playbooks/roles/outbound/templates/headscale/config/acl.json b/playbooks/roles/outbound/templates/headscale/config/acl.json index 449207d..242d01e 100644 --- a/playbooks/roles/outbound/templates/headscale/config/acl.json +++ b/playbooks/roles/outbound/templates/headscale/config/acl.json @@ -1,17 +1,24 @@ { "groups": { - "group:internal": ["liz{{ oauth_user_suffix }}", "lucina{{ oauth_user_suffix }}", "riley{{ oauth_user_suffix }}"], + "group:coffee_admins": ["liz{{ oauth_user_suffix }}", "lucina{{ oauth_user_suffix }}"], }, "acls": [ +{% for user in ["liz", "lucina", "riley"] %} + { + "action": "accept", + "src": ["{{ user }}{{ oauth_user_suffix }}"], + "dst": ["{{ user }}{{ oauth_user_suffix }}:*"] + }, +{% endfor %} { "action": "accept", "src": ["{{ auth_key_user }}"], - "dst": ["{{ auth_key_user }}:*", "10.0.0.0/8:*"] + "dst": ["{{ auth_key_user }}:*", "{{ loadbalancer_ip }}/32:*"] }, { "action": "accept", - "src": ["group:internal"], - "dst": ["10.0.0.0/8:*"] + "src": ["group:coffee_admins"], + "dst": ["{{ loadbalancer_ip }}/32:*"] } ] } diff --git a/playbooks/roles/outbound/templates/proxy/nginx/conf.d/oci.conf b/playbooks/roles/outbound/templates/proxy/nginx/conf.d/oci.conf new file mode 100644 index 0000000..5e68fc3 --- /dev/null +++ b/playbooks/roles/outbound/templates/proxy/nginx/conf.d/oci.conf @@ -0,0 +1,19 @@ +server { + listen 80; + server_name oci.liz.coffee; + + real_ip_header X-Forwarded-For; + real_ip_recursive on; + set_real_ip_from {{ docker_network }}; + + location / { + proxy_pass https://{{ loadbalancer_ip }}; + proxy_ssl_verify off; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } +} diff --git a/playbooks/roles/src/templates/stacks/docker-compose.yml b/playbooks/roles/src/templates/stacks/docker-compose.yml index 46f253d..547020d 100644 --- a/playbooks/roles/src/templates/stacks/docker-compose.yml +++ b/playbooks/roles/src/templates/stacks/docker-compose.yml @@ -1,4 +1,40 @@ services: + frontend: + image: emarcs/nginx-cgit + volumes: + - {{ src_base }}/volumes/data/repos:/srv/git:ro + - {{ src_base }}/volumes/cgit/:/usr/share/cgit:ro + - {{ src_base }}/volumes/cgit.nginx.conf:/etc/nginx/sites-available/default + environment: + CGIT_TITLE: '{{ src_domain }}' + CGIT_DESC: '<3 {{ domain }}' + CGIT_VROOT: '/cgit' + CGIT_SECTION_FROM_STARTPATH: 1 + CGIT_MAX_REPO_COUNT: 100 + networks: + - proxy + healthcheck: + test: ["CMD-SHELL", "curl --fail http://localhost"] + timeout: 15s + interval: 30s + retries: 3 + start_period: 5s + deploy: + mode: replicated + update_config: + parallelism: 1 + failure_action: rollback + order: start-first + monitor: 10s + labels: + - traefik.enable=true + - traefik.swarm.network=proxy + - traefik.http.routers.src.tls=true + - traefik.http.routers.src.tls.certResolver=letsencrypt + - traefik.http.routers.src.rule=Host(`{{ src_domain }}`) + - traefik.http.routers.src.entrypoints=websecure + - traefik.http.services.src.loadbalancer.server.port=80 + src: image: charmcli/soft-serve volumes: @@ -17,8 +53,6 @@ services: - SOFT_SERVE_INITIAL_ADMIN_KEYS={{ src_admin_keys }} - SOFT_SERVE_GIT_MAX_CONNECTIONS=20 - SOFT_SERVE_LOG_FORMAT=json - networks: - - proxy healthcheck: test: ["CMD-SHELL", "netstat -tuln | grep 2222"] timeout: 15s @@ -33,14 +67,6 @@ services: order: stop-first monitor: 10s replicas: 1 - labels: - - traefik.enable=true - - traefik.swarm.network=proxy - - traefik.http.routers.src.tls=true - - traefik.http.routers.src.tls.certResolver=letsencrypt - - traefik.http.routers.src.rule=Host(`{{ src_domain }}`) - - traefik.http.routers.src.entrypoints=websecure - - traefik.http.services.src.loadbalancer.server.port=8000 networks: proxy: diff --git a/playbooks/roles/src/templates/volumes/cgit.nginx.conf b/playbooks/roles/src/templates/volumes/cgit.nginx.conf new file mode 100644 index 0000000..5abe189 --- /dev/null +++ b/playbooks/roles/src/templates/volumes/cgit.nginx.conf @@ -0,0 +1,49 @@ +server { + listen 80; + server_name localhost; + + #charset koi8-r; + #access_log /var/log/nginx/log/host.access.log main; + + location / { + root /usr/share/cgit/; + } + + location /cgit { + try_files $uri @cgit; + } + + location @cgit { + fastcgi_param SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi; + + fastcgi_param HTTP_HOST $server_name; + fastcgi_split_path_info ^(/cgit/?)(.+)$; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param QUERY_INFO $uri; + + include fastcgi_params; + + fastcgi_pass unix:/var/run/fcgiwrap.socket; + } + + location /cgit-css/ { + rewrite ^/cgit-css(/.*)$ $1 break; + root /usr/share/cgit/cgit-css/; + } + + error_page 404 /404.html; + error_page 401 /401.html; + + # redirect server error pages to the static page /50x.html + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + + # deny access to .htaccess files, if Apache's document root + # concurs with nginx's one + # + location ~ /\.ht { + deny all; + } +} diff --git a/playbooks/roles/src/templates/volumes/cgit/cgit-css/cgit.png b/playbooks/roles/src/templates/volumes/cgit/cgit-css/cgit.png Binary files differnew file mode 100644 index 0000000..bb7ffa1 --- /dev/null +++ b/playbooks/roles/src/templates/volumes/cgit/cgit-css/cgit.png diff --git a/playbooks/roles/src/templates/volumes/cgit/index.html b/playbooks/roles/src/templates/volumes/cgit/index.html new file mode 100644 index 0000000..4b06983 --- /dev/null +++ b/playbooks/roles/src/templates/volumes/cgit/index.html @@ -0,0 +1 @@ +<h1>hai</h1> diff --git a/playbooks/roles/swarm_cluster/tasks/main.yml b/playbooks/roles/swarm_cluster/tasks/main.yml index d2507af..961d6f5 100644 --- a/playbooks/roles/swarm_cluster/tasks/main.yml +++ b/playbooks/roles/swarm_cluster/tasks/main.yml @@ -18,3 +18,7 @@ when: ansible_hostname != swarm_initializer_host ansible.builtin.import_tasks: swarm_join/tasks/main.yml +- name: Login to OCI + when: not homelab_build + ansible.builtin.command: "docker login {{ oci_domain }} --username {{ oci_username }} --password {{ oci_password }}" + diff --git a/secrets.txt b/secrets.txt index 4fd2647..e23eeba 100644 --- a/secrets.txt +++ b/secrets.txt @@ -17,4 +17,7 @@ roundcube_oauth2_client_basic_secret info_mail_password yubico_client_id yubico_secret_key -concourse_secret_key +simple_registry_password_argon_encoded +vaultwarden_client_id +vaultwarden_client_secret +vaultwarden_master_password diff --git a/tasks/copy-rendered-templates-recursive.yml b/tasks/copy-rendered-templates-recursive.yml index 0733493..2b83834 100644 --- a/tasks/copy-rendered-templates-recursive.yml +++ b/tasks/copy-rendered-templates-recursive.yml @@ -7,7 +7,7 @@ state: directory register: tempdir -- name: Ensure parent directories exist for rendered templates +- name: Ensure parent directories exist for all files delegate_to: localhost become: false ansible.builtin.file: @@ -17,36 +17,69 @@ with_filetree: "{{ render_dir }}" when: item.state == "file" -- name: Recursively render templates +- name: Check if each file is text or binary + delegate_to: localhost + become: false + ansible.builtin.command: + cmd: file --mime-type "{{ item.src }}" + register: file_type_check + changed_when: false + with_filetree: "{{ render_dir }}" + when: item.state == "file" + loop_control: + label: "{{ item.path }}" + +- name: Separate text and binary files + set_fact: + text_files: >- + {{ + file_type_check.results + | selectattr('stdout', 'defined') + | selectattr('stdout', 'search', '^.*: text/') + | map(attribute='item') + | list + }} + binary_files: >- + {{ + file_type_check.results + | selectattr('stdout', 'defined') + | rejectattr('stdout', 'search', '^.*: text/') + | map(attribute='item') + | list + }} + +- name: Render templates (text files only) delegate_to: localhost become: false ansible.builtin.template: src: "{{ item.src }}" dest: "{{ tempdir.path }}/{{ item.path }}" mode: "{{ mode | default('0755') }}" - with_filetree: "{{ render_dir }}" - when: item.state == "file" + loop: "{{ text_files }}" -- name: Sync rendered templates to remote host +- name: Copy binary files directly delegate_to: localhost become: false - ansible.builtin.synchronize: - src: "{{ tempdir.path }}/" - dest: "{{ tempdir.path }}/" + ansible.builtin.copy: + src: "{{ item.src }}" + dest: "{{ tempdir.path }}/{{ item.path }}" + mode: "{{ mode | default('0644') }}" + loop: "{{ binary_files }}" -- name: Remove local temporary directory +- name: Sync rendered and copied files to remote host delegate_to: localhost become: false - ansible.builtin.file: - path: "{{ tempdir.path }}" - state: absent + ansible.builtin.synchronize: + src: "{{ tempdir.path }}/" + dest: "{{ tempdir.path }}/" + recursive: true - name: Ensure destination exists ansible.builtin.file: path: "{{ destination_dir }}" state: directory -- name: Update remote files +- name: Copy files to final destination ansible.builtin.command: cmd: bash -c 'cp -r {{ tempdir.path }}/* {{ destination_dir }}/' @@ -61,3 +94,4 @@ ansible.builtin.file: path: "{{ tempdir.path }}" state: absent + diff --git a/tasks/manage-docker-swarm-service.yml b/tasks/manage-docker-swarm-service.yml index 5332a3f..3d01e1c 100644 --- a/tasks/manage-docker-swarm-service.yml +++ b/tasks/manage-docker-swarm-service.yml @@ -9,7 +9,7 @@ - name: "Deploy stack for {{ service_name }}" ansible.builtin.command: - cmd: "docker stack deploy --resolve-image changed --detach=false --compose-file {{ stack_file | default(service_destination_dir + '/stacks/docker-compose.yml') }} {{ service_name }}" + cmd: "docker stack deploy --with-registry-auth --prune --detach=false --resolve-image=always --compose-file {{ stack_file | default(service_destination_dir + '/stacks/docker-compose.yml') }} {{ service_name }}" register: stack_result changed_when: true failed_when: stack_result.rc != 0 |