aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--group_vars/all.yml10
-rw-r--r--host_vars/dc0.yml17
-rw-r--r--host_vars/stack0-dev0.yml10
-rw-r--r--playbooks/site.yml9
-rw-r--r--roles/acme/defaults/main.yml2
-rw-r--r--roles/acme/tasks/main.yml31
-rw-r--r--roles/acme/templates/acme-client.conf.j226
-rw-r--r--roles/cgit/defaults/main.yml8
-rw-r--r--roles/pf/defaults/main.yml1
-rw-r--r--roles/pf/handlers/main.yml2
-rw-r--r--roles/pf/tasks/main.yml25
-rw-r--r--roles/pf/templates/pf.conf.j224
-rw-r--r--roles/relayd/defaults/main.yml4
-rw-r--r--roles/relayd/tasks/main.yml13
-rw-r--r--roles/relayd/templates/relayd.conf.j282
15 files changed, 260 insertions, 4 deletions
diff --git a/group_vars/all.yml b/group_vars/all.yml
index 380aa39..91af459 100644
--- a/group_vars/all.yml
+++ b/group_vars/all.yml
@@ -2,6 +2,8 @@ ansible_hostname: "{{ ansible_host }}"
ansible_become_method: su
wireguard_domain_controller: "{{ __global_domain_controller }}"
+relayd_domain_name: "{{ __global_domain_name }}"
+acme_domain_name: "{{ __global_domain_name }}"
nfsclient_server: stack0
httpd_use_nfs: true
@@ -19,3 +21,11 @@ __global_domain_name_hosts: owo
__global_domain_name_servers:
- 8.8.8.8
- 1.1.1.1
+
+# __services:
+# - domain: status.test
+# is_public: true
+# port: 120
+# protocols:
+# - tcp
+# - udp
diff --git a/host_vars/dc0.yml b/host_vars/dc0.yml
index d145b81..6bc7c96 100644
--- a/host_vars/dc0.yml
+++ b/host_vars/dc0.yml
@@ -5,3 +5,20 @@ __is_vm: true
__ip:
external: 185.203.114.234
internal: 10.10.0.1
+
+__services:
+ - name: ssh
+ protocol: tcp
+ port: 22
+
+ - name: wireguard
+ protocol: udp
+ port: 53
+
+ - name: http
+ protocol: tcp
+ port: 80
+
+ - name: https
+ protocol: tcp
+ port: 443
diff --git a/host_vars/stack0-dev0.yml b/host_vars/stack0-dev0.yml
index aa571d4..98db313 100644
--- a/host_vars/stack0-dev0.yml
+++ b/host_vars/stack0-dev0.yml
@@ -3,3 +3,13 @@ __is_vm: true
__ip:
external: 192.168.5.61
internal: 10.10.0.61
+
+__services:
+ - name: ssh
+ protocol: tcp
+ port: 22
+
+ - name: cgit
+ domain: git
+ protocol: tcp
+ port: 1235
diff --git a/playbooks/site.yml b/playbooks/site.yml
index eef50b8..af002a6 100644
--- a/playbooks/site.yml
+++ b/playbooks/site.yml
@@ -3,6 +3,15 @@
- role: wireguard
tags: role_wireguard
+- hosts: dc0
+ roles:
+ - role: pf
+ tags: role_pf
+ - role: relayd
+ tags: role_relayd
+ - role: acme
+ tags: role_acme
+
- hosts: servers
roles:
- role: sshd
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 %}
+}
remember that computers suck.