diff --git a/ansible/group_vars/all/main.yml b/ansible/group_vars/all/main.yml index 2ad43b5..4639e33 100644 --- a/ansible/group_vars/all/main.yml +++ b/ansible/group_vars/all/main.yml @@ -66,5 +66,8 @@ nomad_podman_driver_version: 0.1.0 podman_version: 2.1.1+dfsg1-4 # lnd -lnd_version: v0.12.0-beta.rc3 +lnd_version: 0.11.1-beta + +# lego +lego_version: 4.1.3 ... diff --git a/ansible/group_vars/haproxy/main.yml b/ansible/group_vars/haproxy/main.yml new file mode 100644 index 0000000..a110d47 --- /dev/null +++ b/ansible/group_vars/haproxy/main.yml @@ -0,0 +1,4 @@ +--- +lego_email_address: amarpreet@minhas.io +letsencrypt_account_id: "{{ lookup('hashi_vault', 'secret=kv/data/acme:data')['account_id'] }}" +... diff --git a/ansible/host_vars/ivyking.minhas.io/haproxy.yml b/ansible/host_vars/ivyking.minhas.io/haproxy.yml new file mode 100644 index 0000000..be8dadd --- /dev/null +++ b/ansible/host_vars/ivyking.minhas.io/haproxy.yml @@ -0,0 +1,9 @@ +--- +haproxy_domains: + - { name: "gitea", url: "git.minhas.io" } + - { name: "freshrss", url: "rss.minhas.io" } + - { name: "radicale", url: "dav.minhas.io" } + - { name: "wallabag", url: "wallabag.minhas.io" } + - { name: "kanban", url: "kanban.minhas.io" } + - { name: "api", url: "api.sudoscientist.com" } +... diff --git a/ansible/host_vars/ivyking.minhas.io/lego.yml b/ansible/host_vars/ivyking.minhas.io/lego.yml new file mode 100644 index 0000000..779e618 --- /dev/null +++ b/ansible/host_vars/ivyking.minhas.io/lego.yml @@ -0,0 +1,5 @@ +--- +lego_certs: + - { name: "_.minhas.io", domain: "*.minhas.io", dns: "namecheap" } + - { name: "api.sudoscientist.com", domain: "api.sudoscientist.com", dns: "route53" } +... diff --git a/ansible/host_vars/ivyking.minhas.io/main.yml b/ansible/host_vars/ivyking.minhas.io/nexus.yml similarity index 100% rename from ansible/host_vars/ivyking.minhas.io/main.yml rename to ansible/host_vars/ivyking.minhas.io/nexus.yml diff --git a/ansible/inventory.txt b/ansible/inventory.txt index 3bb1770..6546123 100644 --- a/ansible/inventory.txt +++ b/ansible/inventory.txt @@ -28,3 +28,6 @@ sedan.minhas.io [wekan] sedan.minhas.io + +[haproxy] +ivyking.minhas.io diff --git a/ansible/playbooks/haproxy.yml b/ansible/playbooks/haproxy.yml new file mode 100644 index 0000000..4710b0d --- /dev/null +++ b/ansible/playbooks/haproxy.yml @@ -0,0 +1,6 @@ +--- +- hosts: haproxy + roles: + - role: lego + - role: haproxy +... diff --git a/ansible/playbooks/site.yml b/ansible/playbooks/site.yml index f00f795..c29f327 100644 --- a/ansible/playbooks/site.yml +++ b/ansible/playbooks/site.yml @@ -7,4 +7,5 @@ - import_playbook: nexus.yml - import_playbook: lnd.yml - import_playbook: wekan.yml +- import_playbook: haproxy.yml ... diff --git a/ansible/roles/haproxy/handlers/main.yml b/ansible/roles/haproxy/handlers/main.yml new file mode 100644 index 0000000..fe2943a --- /dev/null +++ b/ansible/roles/haproxy/handlers/main.yml @@ -0,0 +1,6 @@ +--- +- name: reload haproxy + systemd: + name: haproxy + state: reloaded +... diff --git a/ansible/roles/haproxy/tasks/main.yml b/ansible/roles/haproxy/tasks/main.yml new file mode 100644 index 0000000..f8ea822 --- /dev/null +++ b/ansible/roles/haproxy/tasks/main.yml @@ -0,0 +1,28 @@ +--- +- name: ensure haproxy exists + apt: + name: haproxy + state: present + +- name: ensure haproxy certs dir exists + file: + path: /etc/haproxy/certs + state: directory + owner: haproxy + group: haproxy + mode: 0750 + +- name: template haproxy config + template: + src: templates/haproxy.cfg.j2 + dest: /etc/haproxy/haproxy.cfg + owner: haproxy + group: haproxy + mode: 0644 + notify: reload haproxy + +- name: ensure haproxy is started and enabled + systemd: + name: haproxy + state: started + enabled: True diff --git a/ansible/roles/haproxy/templates/haproxy.cfg.j2 b/ansible/roles/haproxy/templates/haproxy.cfg.j2 new file mode 100644 index 0000000..f39265e --- /dev/null +++ b/ansible/roles/haproxy/templates/haproxy.cfg.j2 @@ -0,0 +1,63 @@ +global + log /dev/log local0 + log /dev/log local1 notice + chroot /var/lib/haproxy + stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners + stats timeout 30s + user haproxy + group haproxy + daemon + + # Default SSL material locations + ca-base /etc/ssl/certs + crt-base /etc/ssl/private + ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 + ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tlsv12 no-tls-tickets + ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 + ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tlsv12 no-tls-tickets + +defaults + log global + mode http + option httplog + option dontlognull + timeout connect 5000 + timeout client 50000 + timeout server 50000 + errorfile 400 /etc/haproxy/errors/400.http + errorfile 403 /etc/haproxy/errors/403.http + errorfile 408 /etc/haproxy/errors/408.http + errorfile 500 /etc/haproxy/errors/500.http + errorfile 502 /etc/haproxy/errors/502.http + errorfile 503 /etc/haproxy/errors/503.http + errorfile 504 /etc/haproxy/errors/504.http + +frontend fe_default + mode http + bind :443 ssl crt /etc/haproxy/certs/ alpn h2,http/1.1 + bind :80 + redirect scheme https code 301 if !{ ssl_fc } + http-response set-header Strict-Transport-Security max-age=63072000 +{% for domain in haproxy_domains %} + acl host_{{ domain.name }} hdr(host) -i {{ domain.url }} +{% endfor %} +{% for domain in haproxy_domains %} + use_backend be_{{ domain.name }} if host_{{ domain.name }} +{% endfor %} + +{% for domain in haproxy_domains %} +{% if domain.name != 'api' %} +backend be_{{ domain.name }} + balance leastconn + server-template {{ domain.name }} 1 _{{ domain.name }}._tcp.service.masked.name resolvers consul resolve-opts allow-dup-ip resolve-prefer ipv4 check + +{% endif %} +{% endfor %} +backend be_api + balance leastconn + server server1 192.168.122.77:8080 + +resolvers consul + nameserver consul 127.0.0.1:8600 + accepted_payload_size 8192 + hold valid 5s diff --git a/ansible/roles/lego/defaults/main.yml b/ansible/roles/lego/defaults/main.yml new file mode 100644 index 0000000..8de7cc1 --- /dev/null +++ b/ansible/roles/lego/defaults/main.yml @@ -0,0 +1,3 @@ +--- +lego_path: /etc/lego/ +... diff --git a/ansible/roles/lego/tasks/main.yml b/ansible/roles/lego/tasks/main.yml new file mode 100644 index 0000000..6a9a9ee --- /dev/null +++ b/ansible/roles/lego/tasks/main.yml @@ -0,0 +1,117 @@ +--- +- name: ensure lego group + group: + name: lego + state: present + system: True + +- name: ensure lego user + user: + name: lego + state: present + group: lego + system: True + home: /etc/lego + shell: /bin/bash + +- name: check lego version + shell: + cmd: "/usr/local/bin/lego --version | cut -d ' ' -f3" + args: + executable: /bin/bash + changed_when: False + register: installed_lego_version + check_mode: False + +- name: get lego + unarchive: + src: "https://github.com/go-acme/lego/releases/download/v{{ lego_version }}/lego_v{{ lego_version }}_linux_amd64.tar.gz" + dest: /usr/local/bin/ + mode: 0755 + owner: root + group: root + remote_src: True + when: installed_lego_version.stdout != lego_version + register: installed_lego + +- name: remove LICENSE/CHANGELOG + file: + path: "{{ item }}" + state: absent + loop: + - /usr/local/bin/CHANGELOG.md + - /usr/local/bin/LICENSE + changed_when: False + when: installed_lego.changed + +- name: ensure lego account directory exists + file: + path: /etc/lego/accounts/acme-v02.api.letsencrypt.org/{{ lego_email_address }}/keys/ + state: directory + owner: lego + group: lego + mode: 0700 + +- name: ensure account.json exists + template: + src: templates/account.json.j2 + dest: /etc/lego/accounts/acme-v02.api.letsencrypt.org/{{ lego_email_address }}/account.json + owner: lego + group: lego + mode: 0600 + +- name: ensure account private key exists + template: + src: templates/{{ lego_email_address }}.key.j2 + dest: /etc/lego/accounts/acme-v02.api.letsencrypt.org/{{ lego_email_address }}/keys/{{ lego_email_address }}.key + owner: lego + group: lego + mode: 0600 + +- name: ensure namecheap api info exists + template: + src: templates/defaults + dest: /etc/default/lego + owner: lego + group: lego + mode: 0400 + +- name: check if certs exist + stat: + path: /etc/lego/certificates/{{ item.name }}.pem + loop: "{{ lego_certs }}" + register: statted + +- name: create new certs + shell: + cmd: 'source /etc/default/lego && /usr/local/bin/lego --pem --path {{ lego_path }} --email {{ lego_email_address }} --dns {{ item.item.dns }} --domains "{{ item.item.domain }}" run' + args: + executable: /bin/bash + when: item.stat.exists == False + loop: "{{ statted.results }}" + check_mode: False + +- name: create reload hook for domain + template: + src: templates/lego_reload.sh.j2 + dest: /usr/local/bin/lego_reload_{{ item.name }}.sh + owner: lego + group: lego + mode: 0700 + loop: "{{ lego_certs }}" + +- name: create renewal crontabs + cron: + name: "{{ item.name }} renewal" + hour: "4" + user: lego + job: 'source /etc/default/lego && /usr/local/bin/lego --pem --path {{ lego_path }} --email {{ lego_email_address }} --dns {{ item.dns }} --domains "{{ item.domain }}" renew --days 30' + loop: "{{ lego_certs }}" + +- name: create haproxy reload crontab + cron: + name: "{{ item.name }} haproxy reload" + hour: "5" + user: root + job: '/usr/local/bin/lego_reload_{{ item.name }}.sh' + loop: "{{ lego_certs }}" diff --git a/ansible/roles/lego/templates/account.json.j2 b/ansible/roles/lego/templates/account.json.j2 new file mode 100644 index 0000000..d7042d2 --- /dev/null +++ b/ansible/roles/lego/templates/account.json.j2 @@ -0,0 +1,12 @@ +{ + "email": "amarpreet@minhas.io", + "registration": { + "body": { + "status": "valid", + "contact": [ + "mailto:amarpreet@minhas.io" + ] + }, + "uri": "https://acme-v02.api.letsencrypt.org/acme/acct/{{ letsencrypt_account_id }}" + } +} diff --git a/ansible/roles/lego/templates/amarpreet@minhas.io.key.j2 b/ansible/roles/lego/templates/amarpreet@minhas.io.key.j2 new file mode 100644 index 0000000..c073889 --- /dev/null +++ b/ansible/roles/lego/templates/amarpreet@minhas.io.key.j2 @@ -0,0 +1 @@ +{{ lookup('hashi_vault', 'secret=kv/data/acme:data')['private_key'] }} diff --git a/ansible/roles/lego/templates/defaults b/ansible/roles/lego/templates/defaults new file mode 100644 index 0000000..19e9d7a --- /dev/null +++ b/ansible/roles/lego/templates/defaults @@ -0,0 +1,5 @@ +export NAMECHEAP_API_USER={{ lookup('hashi_vault', 'secret=kv/data/namecheap:data')['api_user'] }} +export NAMECHEAP_API_KEY={{ lookup('hashi_vault', 'secret=kv/data/namecheap:data')['api_key'] }} +export AWS_ACCESS_KEY_ID={{ lookup('hashi_vault', 'secret=kv/data/aws:data')['access_key'] }} +export AWS_SECRET_ACCESS_KEY={{ lookup('hashi_vault', 'secret=kv/data/aws:data')['secret_key'] }} +export AWS_HOSTED_ZONE_ID={{ lookup('hashi_vault', 'secret=kv/data/aws:data')['hosted_zone_id'] }} diff --git a/ansible/roles/lego/templates/lego_reload.sh.j2 b/ansible/roles/lego/templates/lego_reload.sh.j2 new file mode 100644 index 0000000..324f2cd --- /dev/null +++ b/ansible/roles/lego/templates/lego_reload.sh.j2 @@ -0,0 +1,12 @@ +#!/bin/bash +SOURCE_PEM=/etc/lego/certificates/{{ item.name }}.pem +DEST_PEM=/etc/haproxy/certs/{{ item.name }}.pem + +if [ -e ${DEST_PEM} ]; then + if [ $(diff ${SOURCE_PEM} ${DEST_PEM}) == 0 ]; then + exit + fi + cp ${SOURCE_PEM} ${DEST_PEM} + chown haproxy:haproxy ${DEST_PEM} + systemctl reload haproxy +fi diff --git a/ansible/roles/lnd/tasks/main.yml b/ansible/roles/lnd/tasks/main.yml index 5d9e905..8a74ff4 100644 --- a/ansible/roles/lnd/tasks/main.yml +++ b/ansible/roles/lnd/tasks/main.yml @@ -20,7 +20,7 @@ - name: update lnd unarchive: - src: 'https://github.com/lightningnetwork/lnd/releases/download/{{ lnd_version }}/lnd-linux-{{ lnd_arch }}-{{ lnd_version }}.tar.gz' + src: 'https://github.com/lightningnetwork/lnd/releases/download/v{{ lnd_version }}/lnd-linux-{{ lnd_arch }}-v{{ lnd_version }}.tar.gz' dest: /home/bitcoind/go/bin/ owner: bitcoind group: bitcoind