diff options
Diffstat (limited to 'roles')
25 files changed, 412 insertions, 4 deletions
| diff --git a/roles/certbot/README.md b/roles/certbot/README.md new file mode 100644 index 0000000..b9d3e36 --- /dev/null +++ b/roles/certbot/README.md @@ -0,0 +1,3 @@ +# Role "certbot" + +Installs certbot and the Cloudflare DNS plugin for certbot to provision and deploy TLS certificates for web properties. diff --git a/roles/certbot/files/rsync.sh b/roles/certbot/files/rsync.sh new file mode 100644 index 0000000..fa9b27b --- /dev/null +++ b/roles/certbot/files/rsync.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +# Start the rsync server and perform the transfer +rrsync -wo /etc/letsencrypt/live + +# Reload NGINX +systemctl reload nginx diff --git a/roles/certbot/tasks/main.yml b/roles/certbot/tasks/main.yml new file mode 100644 index 0000000..abe22cc --- /dev/null +++ b/roles/certbot/tasks/main.yml @@ -0,0 +1,110 @@ +--- +- name: Install certbot and certbot Cloudflare plugin +  when: inventory_hostname == ansible_play_hosts_all[0] +  package: +    name: +      - python3-certbot +      - python3-certbot-dns-cloudflare +    state: present +  tags: +    - role::certbot + +- name: Install rsync on certbot hosts +  package: +    name: rsync +    state: present +  tags: +    - role::certbot + +- name: Generate Cloudflare credentials file on designated leader +  when: inventory_hostname == ansible_play_hosts_all[0] +  copy: +    content: | +      # This file is managed by Ansible +      dns_cloudflare_api_token = {{ certbot_cloudflare_token }} +    dest: /etc/letsencrypt/cloudflare.ini +    owner: root +    group: root +    mode: 0400 +  tags: +    - role::certbot + +- name: Generate SSH key for certificate distribution +  when: inventory_hostname == ansible_play_hosts_all[0] +  community.crypto.openssh_keypair: +    path: /root/.ssh/cert_{{ item }}_key_ed25519 +    type: ed25519 +    state: present +    comment: certificate distribution key for {{ item }} +  with_items: +    - "{{ ansible_play_hosts | reject('in', [inventory_hostname]) }}" +  tags: +    - role::certbot +  register: generated_keys + +- name: Create certificate directories on replica certificate hosts +  when: inventory_hostname != ansible_play_hosts[0] +  file: +    path: /etc/letsencrypt/live +    recurse: true +    state: directory +    owner: root +    group: root +    mode: 0700 +  tags: +    - role::certbot + +- name: Install rsync + nginx reload script to replica servers +  when: inventory_hostname != ansible_play_hosts[0] +  copy: +    src: rsync.sh +    dest: /opt/cert_rsync.sh +    owner: root +    group: root +    mode: 0500 +  tags: +    - role::certbot + +- name: Install certificate distribution keys to other NGINX nodes +  when: inventory_hostname != ansible_play_hosts[0] +  ansible.posix.authorized_key: +    user: root +    state: present +    key: | +      {{ hostvars[ansible_play_hosts_all[0]]['generated_keys']['results'] +      | selectattr('item', 'equalto', inventory_hostname) +      | map(attribute='public_key') +      | first }} +    comment: "certificate distribution key" +    key_options: 'from="{{ hostvars[ansible_play_hosts_all[0]]["wireguard_subnet"] }}",restrict,command="/opt/cert_rsync.sh"' +  tags: +    - role::certbot + +- name: Create renewal hook to synchronize certificates +  when: inventory_hostname == ansible_play_hosts_all[0] +  template: +    src: renewal-hook.sh.j2 +    dest: /etc/letsencrypt/renewal-hooks/deploy/distribute-certs +    owner: root +    group: root +    mode: 0700 +  tags: +    - role::certbot + +- name: Request certificates for configured domains +  when: inventory_hostname == ansible_play_hosts_all[0] +  command: | +    certbot certonly +    --agree-tos +    --non-interactive +    --email {{ certbot_email }} +    --dns-cloudflare +    --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini +    --deploy-hook /etc/letsencrypt/renewal-hooks/deploy/distribute-certs +    -d {{ item }} -d *.{{ item }} -d cloud.native.is.fun.and.easy.pydis.wtf +  args: +    creates: "/etc/letsencrypt/live/{{ item }}/fullchain.pem" +  with_items: +    - "{{ certbot_domains }}" +  tags: +    - role::certbot diff --git a/roles/certbot/templates/renewal-hook.sh.j2 b/roles/certbot/templates/renewal-hook.sh.j2 new file mode 100644 index 0000000..7fa7252 --- /dev/null +++ b/roles/certbot/templates/renewal-hook.sh.j2 @@ -0,0 +1,6 @@ +#!/bin/sh +set -ex + +{% for host in ansible_play_hosts if host != inventory_hostname %} +rsync --copy-links --delete --recursive -e "ssh -i /root/.ssh/cert_{{ host }}_key_ed25519 -o StrictHostKeyChecking=accept-new" /etc/letsencrypt/live/* root@{{ hostvars[host]['wireguard_subnet'] | split("/") | first }}:/etc/letsencrypt/live +{% endfor %} diff --git a/roles/certbot/vars/main/main.yml b/roles/certbot/vars/main/main.yml new file mode 100644 index 0000000..fdfc7b1 --- /dev/null +++ b/roles/certbot/vars/main/main.yml @@ -0,0 +1,6 @@ +--- +certbot_cloudflare_token: "{{ encrypted_cloudflare_token }}" +certbot_email: "[email protected]" +certbot_domains: +  - pydis.wtf +  - pythondiscord.com diff --git a/roles/certbot/vars/main/vault.yml b/roles/certbot/vars/main/vault.yml new file mode 100644 index 0000000..c669b69 --- /dev/null +++ b/roles/certbot/vars/main/vault.yml @@ -0,0 +1,9 @@ +$ANSIBLE_VAULT;1.1;AES256 +66336535306366333038666137306135663438346366643735383962623339636236343438633766 +6565343931306531623330373936313730353539303264390a333031363634663236636232386461 +34353239643364653464373531653236383963303137326438343239313136376537336636326162 +3537383737323732310a623836363138646434636165643130366362656661393937346534313632 +37663966613031363036623838326666636231313462363831396366363837343632646131303863 +35363032386463346164623733656463633735376161653361343231326166313466643236623762 +31343562323362353238663666303435353138643463656531373466336639316464376632623731 +32646464393438656134 diff --git a/roles/nginx-cloudflare-mtls/README.md b/roles/nginx-cloudflare-mtls/README.md new file mode 100644 index 0000000..081cacb --- /dev/null +++ b/roles/nginx-cloudflare-mtls/README.md @@ -0,0 +1,16 @@ +# Role "nginx-cloudflare-mtls" + +Installs the certificate required for performing mutual TLS authentication +between NGINX and Cloudflare. + +To use mutual TLS in your NGINX virtual hosts, add this configuration snippet: + +```nginx +ssl_client_certificate {{ nginx_cloudflare_mtls_certificate_path }}; +ssl_verify_client on; +``` + + +## Variables + +See [role defaults](./defaults/main.yml) for an annotated overview. diff --git a/roles/nginx-cloudflare-mtls/defaults/main.yml b/roles/nginx-cloudflare-mtls/defaults/main.yml new file mode 100644 index 0000000..ff1c667 --- /dev/null +++ b/roles/nginx-cloudflare-mtls/defaults/main.yml @@ -0,0 +1,3 @@ +--- +# The path at which to install the certificate. +nginx_cloudflare_mtls_certificate_path: /etc/nginx/certs/cloudflare.crt diff --git a/roles/nginx-cloudflare-mtls/files/cloudflare.crt b/roles/nginx-cloudflare-mtls/files/cloudflare.crt new file mode 100644 index 0000000..965f0bf --- /dev/null +++ b/roles/nginx-cloudflare-mtls/files/cloudflare.crt @@ -0,0 +1,35 @@ +-----BEGIN CERTIFICATE----- +MIIGCjCCA/KgAwIBAgIIV5G6lVbCLmEwDQYJKoZIhvcNAQENBQAwgZAxCzAJBgNV +BAYTAlVTMRkwFwYDVQQKExBDbG91ZEZsYXJlLCBJbmMuMRQwEgYDVQQLEwtPcmln +aW4gUHVsbDEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZv +cm5pYTEjMCEGA1UEAxMab3JpZ2luLXB1bGwuY2xvdWRmbGFyZS5uZXQwHhcNMTkx +MDEwMTg0NTAwWhcNMjkxMTAxMTcwMDAwWjCBkDELMAkGA1UEBhMCVVMxGTAXBgNV +BAoTEENsb3VkRmxhcmUsIEluYy4xFDASBgNVBAsTC09yaWdpbiBQdWxsMRYwFAYD +VQQHEw1TYW4gRnJhbmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMSMwIQYDVQQD +ExpvcmlnaW4tcHVsbC5jbG91ZGZsYXJlLm5ldDCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAN2y2zojYfl0bKfhp0AJBFeV+jQqbCw3sHmvEPwLmqDLqynI +42tZXR5y914ZB9ZrwbL/K5O46exd/LujJnV2b3dzcx5rtiQzso0xzljqbnbQT20e +ihx/WrF4OkZKydZzsdaJsWAPuplDH5P7J82q3re88jQdgE5hqjqFZ3clCG7lxoBw +hLaazm3NJJlUfzdk97ouRvnFGAuXd5cQVx8jYOOeU60sWqmMe4QHdOvpqB91bJoY +QSKVFjUgHeTpN8tNpKJfb9LIn3pun3bC9NKNHtRKMNX3Kl/sAPq7q/AlndvA2Kw3 +Dkum2mHQUGdzVHqcOgea9BGjLK2h7SuX93zTWL02u799dr6Xkrad/WShHchfjjRn +aL35niJUDr02YJtPgxWObsrfOU63B8juLUphW/4BOjjJyAG5l9j1//aUGEi/sEe5 +lqVv0P78QrxoxR+MMXiJwQab5FB8TG/ac6mRHgF9CmkX90uaRh+OC07XjTdfSKGR +PpM9hB2ZhLol/nf8qmoLdoD5HvODZuKu2+muKeVHXgw2/A6wM7OwrinxZiyBk5Hh +CvaADH7PZpU6z/zv5NU5HSvXiKtCzFuDu4/Zfi34RfHXeCUfHAb4KfNRXJwMsxUa ++4ZpSAX2G6RnGU5meuXpU5/V+DQJp/e69XyyY6RXDoMywaEFlIlXBqjRRA2pAgMB +AAGjZjBkMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgECMB0GA1Ud +DgQWBBRDWUsraYuA4REzalfNVzjann3F6zAfBgNVHSMEGDAWgBRDWUsraYuA4REz +alfNVzjann3F6zANBgkqhkiG9w0BAQ0FAAOCAgEAkQ+T9nqcSlAuW/90DeYmQOW1 +QhqOor5psBEGvxbNGV2hdLJY8h6QUq48BCevcMChg/L1CkznBNI40i3/6heDn3IS +zVEwXKf34pPFCACWVMZxbQjkNRTiH8iRur9EsaNQ5oXCPJkhwg2+IFyoPAAYURoX +VcI9SCDUa45clmYHJ/XYwV1icGVI8/9b2JUqklnOTa5tugwIUi5sTfipNcJXHhgz +6BKYDl0/UP0lLKbsUETXeTGDiDpxZYIgbcFrRDDkHC6BSvdWVEiH5b9mH2BON60z +0O0j8EEKTwi9jnafVtZQXP/D8yoVowdFDjXcKkOPF/1gIh9qrFR6GdoPVgB3SkLc +5ulBqZaCHm563jsvWb/kXJnlFxW+1bsO9BDD6DweBcGdNurgmH625wBXksSdD7y/ +fakk8DagjbjKShYlPEFOAqEcliwjF45eabL0t27MJV61O/jHzHL3dknXeE4BDa2j +bA+JbyJeUMtU7KMsxvx82RmhqBEJJDBCJ3scVptvhDMRrtqDBW5JShxoAOcpFQGm +iYWicn46nPDjgTU0bX1ZPpTpryXbvciVL5RkVBuyX2ntcOLDPlZWgxZCBp96x07F +AnOzKgZk4RzZPNAxCXERVxajn/FLcOhglVAKo5H0ac+AitlQ0ip55D2/mf8o72tM +fVQ6VpyjEXdiIXWUq/o= +-----END CERTIFICATE----- diff --git a/roles/nginx-cloudflare-mtls/meta/main.yml b/roles/nginx-cloudflare-mtls/meta/main.yml new file mode 100644 index 0000000..72b1bd7 --- /dev/null +++ b/roles/nginx-cloudflare-mtls/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: +  - nginx diff --git a/roles/nginx-cloudflare-mtls/tasks/main.yml b/roles/nginx-cloudflare-mtls/tasks/main.yml new file mode 100644 index 0000000..d5ef7f4 --- /dev/null +++ b/roles/nginx-cloudflare-mtls/tasks/main.yml @@ -0,0 +1,20 @@ +--- +- name: Create nginx certificates directory +  file: +    path: /etc/nginx/certs +    state: directory +    owner: root +    group: root +    mode: 0444 +  tags: +    - role::nginx-cloudflare-mtls + +- name: Copy the cloudflare mutual TLS certificate +  copy: +    src: cloudflare.crt +    dest: /etc/nginx/certs/cloudflare.crt +    owner: root +    group: root +    mode: 0444 +  tags: +    - role::nginx-cloudflare-mtls diff --git a/roles/nginx-geoip/meta/main.yml b/roles/nginx-geoip/meta/main.yml new file mode 100644 index 0000000..72b1bd7 --- /dev/null +++ b/roles/nginx-geoip/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: +  - nginx diff --git a/roles/nginx-geoip/tasks/main.yml b/roles/nginx-geoip/tasks/main.yml new file mode 100644 index 0000000..f772278 --- /dev/null +++ b/roles/nginx-geoip/tasks/main.yml @@ -0,0 +1,13 @@ +--- +- name: Configure the geoip module +  copy: +    # ref https://nginx.org/en/docs/http/ngx_http_geoip_module.html +    content: geoip_country /usr/share/GeoIP/GeoIP.dat; +    dest: /etc/nginx/conf.d/geoip.conf +    owner: root +    group: root +    mode: 0444 +  tags: +    - role::nginx-geoip +  notify: +    - reload the nginx service diff --git a/roles/nginx-ufw/README.md b/roles/nginx-ufw/README.md new file mode 100644 index 0000000..e657afb --- /dev/null +++ b/roles/nginx-ufw/README.md @@ -0,0 +1,3 @@ +# Role "nginx-ufw" + +Allows NGINX HTTP and HTTPS traffic through the UFW firewall. diff --git a/roles/nginx-ufw/meta/main.yml b/roles/nginx-ufw/meta/main.yml new file mode 100644 index 0000000..dac7049 --- /dev/null +++ b/roles/nginx-ufw/meta/main.yml @@ -0,0 +1,4 @@ +--- +dependencies: +  - nginx +  - ufw diff --git a/roles/nginx-ufw/tasks/main.yml b/roles/nginx-ufw/tasks/main.yml new file mode 100644 index 0000000..fb703bf --- /dev/null +++ b/roles/nginx-ufw/tasks/main.yml @@ -0,0 +1,7 @@ +--- +- name: Allow http(s) traffic through the firewall +  ufw: +    app: Nginx Full +    rule: allow +  tags: +    - role::nginx-ufw diff --git a/roles/nginx/README.md b/roles/nginx/README.md new file mode 100644 index 0000000..9961a69 --- /dev/null +++ b/roles/nginx/README.md @@ -0,0 +1,3 @@ +# Role "nginx" + +Installs nginx on target hosts and provides a handler for reloading nginx, for instance on configuration change. diff --git a/roles/nginx/files/default_server.conf b/roles/nginx/files/default_server.conf new file mode 100644 index 0000000..13e74a0 --- /dev/null +++ b/roles/nginx/files/default_server.conf @@ -0,0 +1,24 @@ +# Managed by Ansible +server { +  listen 443 ssl http2 default_server; + +  ssl_certificate      /etc/letsencrypt/live/pydis.wtf/fullchain.pem; +  ssl_certificate_key  /etc/letsencrypt/live/pydis.wtf/privkey.pem; + +  location / { +    set_by_lua_block $url { +      local urls = { +            "https://fasterthanli.me/articles/i-want-off-mr-golangs-wild-ride", +            "https://en.wikipedia.org/wiki/Tax_evasion", +            "https://jchri.st/blog/apfs-sadness-on-macos-big-sur.html", +            "https://cdn.discordapp.com/attachments/675756741417369640/852688961516077086/Screenshot_2021-06-11_at_00.21.22.png", +            "https://news.ycombinator.com/", +            "https://www.hertfordshire.gov.uk/latest/letchworth-webcam.jpg", +            "https://media.discordapp.net/attachments/922169059175444501/952929630459924501/1svkf3xto3n61.png" +      } +      return urls [ math.random(#urls) ] +    } + +    return 302 $url; +  } +} diff --git a/roles/nginx/handlers/main.yml b/roles/nginx/handlers/main.yml new file mode 100644 index 0000000..376d85a --- /dev/null +++ b/roles/nginx/handlers/main.yml @@ -0,0 +1,7 @@ +--- +- name: reload the nginx service +  service: +    name: nginx +    state: reloaded +  tags: +    - role::nginx diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml new file mode 100644 index 0000000..3419cb3 --- /dev/null +++ b/roles/nginx/tasks/main.yml @@ -0,0 +1,21 @@ +--- +- name: Install NGINX & modules +  package: +    name: +      - nginx +      - libnginx-mod-http-lua +    state: present +  tags: +    - role::nginx + +- name: Copy NGINX default config +  copy: +    src: default_server.conf +    dest: /etc/nginx/conf.d/default_server.conf +    group: root +    owner: root +    mode: 0644 +  tags: +    - role::nginx +  notify: +    - reload the nginx service diff --git a/roles/postgres/tasks/main.yml b/roles/postgres/tasks/main.yml index 01638af..9551c4e 100644 --- a/roles/postgres/tasks/main.yml +++ b/roles/postgres/tasks/main.yml @@ -1,4 +1,4 @@ -- name: Install postgres packages. +- name: Install postgres packages    apt:      name:        - python3-psycopg2 @@ -9,7 +9,7 @@    tags:      - role::postgres -- name: Check postgres is started and enabled on boot. +- name: Check postgres is started and enabled on boot    service:      name: '{{ postgresql_daemon }}'      state: started @@ -17,7 +17,7 @@    tags:      - role::postgres -- name: Add postgres users. +- name: Add postgres users    community.postgresql.postgresql_user: "{{ item }}"    with_items: "{{ postgresql_users }}"    become: true @@ -25,7 +25,7 @@    tags:      - role::postgres -- name: Add postgres databases. +- name: Add postgres databases    community.postgresql.postgresql_db: "{{ item }}"    with_items: "{{ postgresql_databases }}"    become: true diff --git a/roles/wireguard/defaults/main/vars.yml b/roles/wireguard/defaults/main/vars.yml new file mode 100644 index 0000000..10c80ae --- /dev/null +++ b/roles/wireguard/defaults/main/vars.yml @@ -0,0 +1,4 @@ +extra_keys: +  - name: Joe +    pubkey: /dJ+tKXzxv7nrUleNlF+CGyq7OIVlqL8/9Sn8j+cEAc= +    subnet: 10.0.1.0/24 diff --git a/roles/wireguard/handlers/main.yml b/roles/wireguard/handlers/main.yml new file mode 100644 index 0000000..0edcf3a --- /dev/null +++ b/roles/wireguard/handlers/main.yml @@ -0,0 +1,4 @@ +- name: reload wg-quick +  service: +    name: wg-quick@wg0 +    state: reloaded diff --git a/roles/wireguard/tasks/main.yml b/roles/wireguard/tasks/main.yml new file mode 100644 index 0000000..46ff3e9 --- /dev/null +++ b/roles/wireguard/tasks/main.yml @@ -0,0 +1,72 @@ +- name: Install WireGuard +  apt: +    update_cache: true +    cache_valid_time: 3600 +    pkg: +      - wireguard +      - wireguard-tools +      - linux-headers-{{ ansible_kernel }} +  tags: +    - role::wireguard + +- name: Generate WireGuard private key +  shell: set -o pipefail && wg genkey > /etc/wireguard/key.priv +  args: +    executable: /bin/bash +    creates: /etc/wireguard/key.priv +  tags: +    - role::wireguard + +- name: Generate WireGuard public key +  shell: set -o pipefail && cat /etc/wireguard/key.priv | wg pubkey > /etc/wireguard/key.pub +  args: +    executable: /bin/bash +    creates: /etc/wireguard/key.pub +  tags: +    - role::wireguard + +- name: Ensure file permissions for keys set correctly +  file: +    path: '{{ item }}' +    owner: root +    group: root +    mode: '0600' +  with_items: +    - /etc/wireguard/key.priv +    - /etc/wireguard/key.pub +  tags: +    - role::wireguard + +- name: Fetch private key for all hosts +  slurp: +    src: /etc/wireguard/key.priv +  register: wg_priv_key +  tags: +    - role::wireguard + +- name: Fetch public key for all hosts +  slurp: +    src: /etc/wireguard/key.pub +  register: wg_pub_key +  tags: +    - role::wireguard + +- name: Generate WireGuard configuration file +  template: +    src: wg0.conf.j2 +    dest: /etc/wireguard/wg0.conf +    mode: '0600' +    group: root +    owner: root +  notify: +    - reload wg-quick +  tags: +    - role::wireguard + +- name: Start and enable the WireGuard service +  service: +    name: wg-quick@wg0 +    enabled: true +    state: started +  tags: +    - role::wireguard diff --git a/roles/wireguard/templates/wg0.conf.j2 b/roles/wireguard/templates/wg0.conf.j2 new file mode 100644 index 0000000..647854a --- /dev/null +++ b/roles/wireguard/templates/wg0.conf.j2 @@ -0,0 +1,25 @@ +# Configuration managed by Ansible +[Interface] +Address = {{ wireguard_subnet }} +ListenPort = {{ wireguard_port }} +PrivateKey = {{ wg_priv_key['content'] | b64decode | trim }} + +PostUp = ip route add local {{ wireguard_subnet }} dev eth0 + +{% for host in hostvars.keys() if not host == inventory_hostname %} +# Peer config for: {{ host }} +[Peer] +AllowedIPs = {{ hostvars[host]['wireguard_subnet'] }} +PublicKey = {{ hostvars[host]['wg_pub_key']['content'] | b64decode | trim }} +Endpoint = {{ host }}.box.pydis.wtf:{{ wireguard_port }} +PersistentKeepalive = 30 + +{% endfor %} + +{% for key in extra_keys %} +# DevOps config for: {{ key.name }} +[Peer] +AllowedIPs = {{ key.subnet }} +PublicKey = {{ key.pubkey }} + +{% endfor %} | 
