diff options
author | Romain Gonçalves <me@rgoncalves.se> | 2021-12-11 18:50:33 +0000 |
---|---|---|
committer | Romain Gonçalves <me@rgoncalves.se> | 2021-12-11 18:50:33 +0000 |
commit | de3373e97d133e0ac76fb44deb5dea27c18d8815 (patch) | |
tree | 5b63b301ff180ef837ca6fb6a676e31cb87d326c /roles | |
parent | e60e99796111ee6d43080b4e48971c08886c0570 (diff) | |
download | rules-de3373e97d133e0ac76fb44deb5dea27c18d8815.tar.gz |
roles: Add pf and relayd roles for domain controller
Diffstat (limited to 'roles')
-rw-r--r-- | roles/acme/defaults/main.yml | 2 | ||||
-rw-r--r-- | roles/acme/tasks/main.yml | 31 | ||||
-rw-r--r-- | roles/acme/templates/acme-client.conf.j2 | 26 | ||||
-rw-r--r-- | roles/cgit/defaults/main.yml | 8 | ||||
-rw-r--r-- | roles/pf/defaults/main.yml | 1 | ||||
-rw-r--r-- | roles/pf/handlers/main.yml | 2 | ||||
-rw-r--r-- | roles/pf/tasks/main.yml | 25 | ||||
-rw-r--r-- | roles/pf/templates/pf.conf.j2 | 24 | ||||
-rw-r--r-- | roles/relayd/defaults/main.yml | 4 | ||||
-rw-r--r-- | roles/relayd/tasks/main.yml | 13 | ||||
-rw-r--r-- | roles/relayd/templates/relayd.conf.j2 | 82 |
11 files changed, 214 insertions, 4 deletions
diff --git a/roles/acme/defaults/main.yml b/roles/acme/defaults/main.yml new file mode 100644 index 0000000..80c091a --- /dev/null +++ b/roles/acme/defaults/main.yml @@ -0,0 +1,2 @@ +acme_configuration_file: /etc/acme-client.conf +acme_domain_name: null diff --git a/roles/acme/tasks/main.yml b/roles/acme/tasks/main.yml new file mode 100644 index 0000000..aad4342 --- /dev/null +++ b/roles/acme/tasks/main.yml @@ -0,0 +1,31 @@ +- name: generate acme-client configuration + template: + src: acme-client.conf.j2 + dest: "{{ acme_configuration_file }}" + owner: 0 + group: 0 + mode: 0644 + +- name: retrieve enabled domains + shell: grep "^domain" /etc/acme-client.conf | cut -d " " -f 2 + register: subdomains + +- name: generate acme certificates + command: acme-client -v {{ item }} + loop: "{{ subdomains.stdout_lines }}" + register: result + failed_when: + - result.rc != 0 + - "'certificate valid' not in result.stderr" + +- name: display registered certificates + debug: + var: result + +- name: enable automatic acme certificates update + cron: + name: "automatic acme certificates update for subdomain : {{ item }}" + minute: 0 + hour: 6,18 + job: "acme-client -v {{ item }} && rcctl reload relayd" + loop: "{{ subdomains.stdout_lines }}" diff --git a/roles/acme/templates/acme-client.conf.j2 b/roles/acme/templates/acme-client.conf.j2 new file mode 100644 index 0000000..3792009 --- /dev/null +++ b/roles/acme/templates/acme-client.conf.j2 @@ -0,0 +1,26 @@ +# managed by Ansible +{% import 'macros.j2' as macros with context %} + +authority letsencrypt { + api url "https://acme-v02.api.letsencrypt.org/directory" + account key "/etc/acme/letsencrypt-privkey.pem" +} + +domain {{ acme_domain_name }} { + alternative names { www.{{ acme_domain_name }} } + domain key "/etc/ssl/private/{{ acme_domain_name }}.key" + domain full chain certificate "/etc/ssl/{{ acme_domain_name }}.crt" + sign with letsencrypt +} + +{% call(h) macros.loop_valid_hosts("servers") -%} +{% for service in h.__services if service.domain is defined %} +domain {{ service.domain }}.{{ acme_domain_name }} { + {% set domain = service.domain ~ "." ~ acme_domain_name %} + alternative names { www.{{ domain }} } + domain key "/etc/ssl/private/{{ domain }}.key" + domain full chain certificate "/etc/ssl/{{ domain }}.crt" + sign with letsencrypt +} +{% endfor %} +{%- endcall %} diff --git a/roles/cgit/defaults/main.yml b/roles/cgit/defaults/main.yml index 0c60bd5..715c258 100644 --- a/roles/cgit/defaults/main.yml +++ b/roles/cgit/defaults/main.yml @@ -6,11 +6,11 @@ cgit_ip: 0.0.0.0 cgit_port: 1235 cgit_authenticate: false -cgit__favicon: http://rgoncalves.se/logo.png -cgit__logo: http://rgoncalves.se/logo.png -cgit__css: http://rgoncalves.se/style/cgit.css +cgit__favicon: https://rgoncalves.se/logo.png +cgit__logo: https://rgoncalves.se/logo.png +cgit__css: https://rgoncalves.se/style/cgit.css cgit__root_desc: development hub -cgit__root_readme: http://rgoncalves.se +cgit__root_readme: https://rgoncalves.se cgit__footer: /conf/footer.html cgit__clone_urls: - git://git.{{ __global_domain_name }}/$CGIT_REPO_URL diff --git a/roles/pf/defaults/main.yml b/roles/pf/defaults/main.yml new file mode 100644 index 0000000..777717d --- /dev/null +++ b/roles/pf/defaults/main.yml @@ -0,0 +1 @@ +pf_configuration_file: /etc/pf.conf diff --git a/roles/pf/handlers/main.yml b/roles/pf/handlers/main.yml new file mode 100644 index 0000000..187e769 --- /dev/null +++ b/roles/pf/handlers/main.yml @@ -0,0 +1,2 @@ +- name: lint pf configuration + command: "pfctl -nf {{ pf_configuration_file }}" diff --git a/roles/pf/tasks/main.yml b/roles/pf/tasks/main.yml new file mode 100644 index 0000000..4fafb77 --- /dev/null +++ b/roles/pf/tasks/main.yml @@ -0,0 +1,25 @@ +- name: generate pf configuration + template: + src: pf.conf.j2 + dest: "{{ pf_configuration_file }}" + owner: 0 + group: 0 + mode: 0600 + notify: + - lint pf configuration + +- name: enable pf + command: pfctl -e + register: result + failed_when: + - result.rc != 0 + - "'already enabled' not in result.stderr" + +- name: restart pf + command: pfctl -f "{{ pf_configuration_file }}" + +- name: test ssh connection on new pf rule + wait_for: + port: 22 + delay: 2 + state: started diff --git a/roles/pf/templates/pf.conf.j2 b/roles/pf/templates/pf.conf.j2 new file mode 100644 index 0000000..6bc936a --- /dev/null +++ b/roles/pf/templates/pf.conf.j2 @@ -0,0 +1,24 @@ +# managed by Ansible +{% import 'macros.j2' as macros with context %} + +# common configuration +set block-policy drop +set loginterface egress +set skip on { lo wg0 } +block all + +# force ssh if not present below +pass in quick on egress proto tcp to port 22 + +# host services +{% for service in __services %} +pass in quick on egress proto {{ service["protocol"] }} to port {{ service["port"] }} +{% endfor %} + +# wireguard +pass in on egress inet proto udp from any to any port 50000 +pass out quick on egress inet from (wg0:network) nat-to (egress:0) + +# output network +pass out quick inet +pass in proto { icmp, icmp6 } all diff --git a/roles/relayd/defaults/main.yml b/roles/relayd/defaults/main.yml new file mode 100644 index 0000000..174a889 --- /dev/null +++ b/roles/relayd/defaults/main.yml @@ -0,0 +1,4 @@ +relayd_configuration_file: /etc/relayd.conf +relayd_domain_name: example.com +relayd_transparent: true +relayd_block_msg: aah! diff --git a/roles/relayd/tasks/main.yml b/roles/relayd/tasks/main.yml new file mode 100644 index 0000000..14df192 --- /dev/null +++ b/roles/relayd/tasks/main.yml @@ -0,0 +1,13 @@ +- name: generate relayd configuration + template: + src: relayd.conf.j2 + dest: "{{ relayd_configuration_file }}" + +- name: verify relayd configuration + command: "relayd -nf {{ relayd_configuration_file }}" + +- name: enable and restart relayd + service: + name: relayd + state: restarted + enabled: true diff --git a/roles/relayd/templates/relayd.conf.j2 b/roles/relayd/templates/relayd.conf.j2 new file mode 100644 index 0000000..c97e9da --- /dev/null +++ b/roles/relayd/templates/relayd.conf.j2 @@ -0,0 +1,82 @@ +# managed by Ansible +{% import 'macros.j2' as macros with context %} + +# general +log connection errors + +# hosts +table <local> { 127.0.0.1 } +{% call(h) macros.loop_valid_hosts("servers") -%} +table <{{ h.inventory_hostname }}> { {{ h.__ip.internal }} } +{% for service in h.__services if service.domain is defined %} +table <{{ h.inventory_hostname }}_{{ service.domain }}> { {{ h.__ip.internal }} } +{% endfor %} +{%- endcall %} + +# protocols + +http protocol "https" { + + tls ciphers "HIGH:!AES128:!kRSA:!aNULL" + tls ecdhe "P-384,P-256,X25519" + + tcp { sack, backlog 128 } + + match request header append "X-Forwarded-For" value "$REMOTE_ADDR" + match request header append "X-Forwarded-By" value "$SERVER_ADDR:$SERVER_PORT" + match request header set "Connection" value "close" + match request header set "X-Forwarded-Proto" value "https" + match request header set "X-Forwarded-Port" value "443" + match response header set "Content-Security-Policy" value "upgrade-insecure-requests" + match response header set "Referrer-Policy" value "no-referrer" + match response header set "X-XSS-Protection" value "1; mode=block" + + tls keypair "{{ relayd_domain_name }}" + pass request quick header "Host" value "{{ relayd_domain_name }}" forward to <local> +{% call(h) macros.loop_valid_hosts("servers") -%} +{% for service in h.__services if service.domain is defined %} + {% set domain_name = service.domain ~ "." ~ relayd_domain_name -%} + tls keypair "{{ domain_name }}" + pass request quick header "Host" value "{{ domain_name }}" forward to <{{ h.inventory_hostname }}_{{ service.domain }}> +{% endfor %} +{%- endcall %} + + block label "{{ relayd_block_msg }}" + return error +} + +http protocol "http" { + + # acme + pass request quick path "/.well-known/acme-challenge/*" forward to <local> + + pass request quick header "Host" value "{{ relayd_domain_name }}" forward to <local> +{% call(h) macros.loop_valid_hosts("servers") -%} +{% for service in h.__services if service.domain is defined %} + {% set domain_name = service.domain ~ "." ~ relayd_domain_name -%} + pass request quick header "Host" value "{{ domain_name }}" forward to <{{ h.inventory_hostname }}_{{ service.domain }}> +{% endfor %} +{%- endcall %} + + return error +} + +# relays + +relay "www" { + listen on egress port 80 + protocol "http" + # assume httpd reverse proxy is running for https redirection + forward to <local> port 8888 check icmp +} + +relay "wwwtls" { + listen on egress port 443 tls + protocol "https" + forward to <local> port 80 check http "/" code 200 +{% call(h) macros.loop_valid_hosts("servers") -%} +{% for service in h.__services if service.domain is defined %} + forward to <{{ h.inventory_hostname }}_{{ service.domain }}> port {{ service.port }} check tcp +{% endfor %} +{%- endcall %} +} |