summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.env.sample5
-rw-r--r--README.md6
-rw-r--r--deploy-ca.yml31
-rw-r--r--deploy-common.yml (renamed from common.yml)0
-rw-r--r--deploy-lldap.yml4
-rw-r--r--deploy-pihole.yml4
-rw-r--r--deploy-private.yml4
-rw-r--r--deploy-vpn-hosts.yml (renamed from deploy-vpn-tags.yml)0
-rw-r--r--group_vars/all.yml8
-rw-r--r--group_vars/ca.yml6
-rw-r--r--group_vars/lldap.yml3
-rw-r--r--group_vars/pihole.yml2
-rw-r--r--inventory9
-rw-r--r--roles/authelia/templates/docker-compose.yml.j24
-rw-r--r--roles/ca/tasks/main.yml15
-rw-r--r--roles/ca/templates/crt.j21
-rw-r--r--roles/common/tasks/systemd-resolved.yml10
-rw-r--r--roles/lldap/tasks/main.yml28
-rw-r--r--roles/lldap/templates/docker-compose.yml.j218
-rw-r--r--roles/nameservers/templates/db.simponic.xyz.j25
-rw-r--r--roles/pihole/tasks/main.yml36
-rw-r--r--roles/pihole/templates/docker-compose.yml.j218
-rw-r--r--roles/private/files/johan/http.ca.internal.simponic.xyz.conf13
-rw-r--r--roles/private/files/johan/http.lldap.internal.simponic.xyz.conf13
-rw-r--r--roles/private/files/johan/http.pihole.internal.simponic.xyz.conf13
-rw-r--r--roles/private/files/johan/https.ca.internal.simponic.xyz.conf32
-rw-r--r--roles/private/files/johan/https.lldap.internal.simponic.xyz.conf32
-rw-r--r--roles/private/files/johan/https.pihole.internal.simponic.xyz.conf32
-rw-r--r--roles/private/files/nginx.conf27
-rw-r--r--roles/private/tasks/main.yml95
-rw-r--r--roles/vpn/files/config/acl.json11
-rw-r--r--roles/vpn/files/docker-compose.yml4
-rw-r--r--roles/vpn/templates/config.yml.j26
33 files changed, 480 insertions, 15 deletions
diff --git a/.env.sample b/.env.sample
index c3e0e81..120ed58 100644
--- a/.env.sample
+++ b/.env.sample
@@ -1,2 +1,7 @@
HEADSCALE_PREAUTH_KEY=
HEADSCALE_OIDC_SECRET=
+LLDAP_USER_PASS=
+LLDAP_JWT_SECRET=
+PIHOLE_WEBPWD=
+STEP_CA_ROOT_PASSWORD=
+STEP_CA_INTERMEDIATE_PASSWORD=
diff --git a/README.md b/README.md
index c91b725..e57f075 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,9 @@
order:
- - common.yml
+ - deploy-common.yml
- deploy-nameservers.yml
- deploy-webservers.yml
+ - deploy-lldap.yml
- deploy-authelia.yml
+ - deploy-pihole.yml
- deploy-vpn.yml
- - deploy-vpn-tags.yml
+ - deploy-vpn-hosts.yml
diff --git a/deploy-ca.yml b/deploy-ca.yml
new file mode 100644
index 0000000..699fc5f
--- /dev/null
+++ b/deploy-ca.yml
@@ -0,0 +1,31 @@
+- name: add acme CA
+ hosts: ca
+ become: yes
+ roles:
+ - role: maxhoesel.smallstep.step_ca
+ tasks:
+ - name: add an acme provisioner to the ca
+ maxhoesel.smallstep.step_ca_provisioner:
+ name: ACME
+ type: ACME
+ become_user: step-ca
+ - name: restart step-ca
+ ansible.builtin.systemd_service:
+ name: step-ca
+ state: restarted
+ enabled: true
+ - name: allow step-ca port traffic on vpn
+ ufw:
+ rule: allow
+ from: 100.64.0.0/10
+ port: "{{ step_ca_port }}"
+ - name: restart ufw
+ ansible.builtin.systemd_service:
+ name: ufw
+ state: restarted
+ enabled: true
+
+- name: configure trust to internal ca on all hosts
+ hosts: all
+ roles:
+ - ca
diff --git a/common.yml b/deploy-common.yml
index 6b3c6f3..6b3c6f3 100644
--- a/common.yml
+++ b/deploy-common.yml
diff --git a/deploy-lldap.yml b/deploy-lldap.yml
new file mode 100644
index 0000000..d632d92
--- /dev/null
+++ b/deploy-lldap.yml
@@ -0,0 +1,4 @@
+- name: lldap setup
+ hosts: lldap
+ roles:
+ - lldap
diff --git a/deploy-pihole.yml b/deploy-pihole.yml
new file mode 100644
index 0000000..59644ae
--- /dev/null
+++ b/deploy-pihole.yml
@@ -0,0 +1,4 @@
+- name: pihole setup
+ hosts: pihole
+ roles:
+ - pihole
diff --git a/deploy-private.yml b/deploy-private.yml
new file mode 100644
index 0000000..4274a21
--- /dev/null
+++ b/deploy-private.yml
@@ -0,0 +1,4 @@
+- name: private setup
+ hosts: private
+ roles:
+ - private
diff --git a/deploy-vpn-tags.yml b/deploy-vpn-hosts.yml
index 9e281ab..9e281ab 100644
--- a/deploy-vpn-tags.yml
+++ b/deploy-vpn-hosts.yml
diff --git a/group_vars/all.yml b/group_vars/all.yml
index ff86d8b..6a4a738 100644
--- a/group_vars/all.yml
+++ b/group_vars/all.yml
@@ -1,6 +1,14 @@
---
+johan_ip: '100.64.0.5'
+
dns_servers:
- 1.1.1.1
- 1.0.0.1
dns_dnssec: true
dns_domains: ["simponic.xyz"]
+dns_stub_listener: false
+
+step_bootstrap_fingerprint: '2de0c420e3b6f9f8e47f325de908b2b2d395d3bc7e49ed9b672ce9be89bea1bf'
+step_bootstrap_ca_url: 'ca.internal.simponic.xyz'
+step_acme_cert_contact: 'elizabeth.hunt@simponic.xyz'
+step_ca_port: 5239
diff --git a/group_vars/ca.yml b/group_vars/ca.yml
new file mode 100644
index 0000000..9ef5c52
--- /dev/null
+++ b/group_vars/ca.yml
@@ -0,0 +1,6 @@
+---
+step_ca_root_password: "{{ lookup('env', 'STEP_CA_ROOT_PASSWORD') }}"
+step_ca_intermediate_password: "{{ lookup('env', 'STEP_CA_INTERMEDIATE_PASSWORD') }}"
+step_ca_dns: "{{ step_bootstrap_ca_url }}, {{ johan_ip }}"
+step_ca_name: Simponic Internal CA
+step_ca_address: ":{{ step_ca_port }}"
diff --git a/group_vars/lldap.yml b/group_vars/lldap.yml
new file mode 100644
index 0000000..2322e7f
--- /dev/null
+++ b/group_vars/lldap.yml
@@ -0,0 +1,3 @@
+---
+lldap_jwt_secret: "{{ lookup('env', 'LLDAP_JWT_SECRET') }}"
+lldap_user_pass: "{{ lookup('env', 'LLDAP_USER_PASS') }}"
diff --git a/group_vars/pihole.yml b/group_vars/pihole.yml
new file mode 100644
index 0000000..354d74c
--- /dev/null
+++ b/group_vars/pihole.yml
@@ -0,0 +1,2 @@
+---
+pihole_webpwd: "{{ lookup('env', 'PIHOLE_WEBPWD') }}"
diff --git a/inventory b/inventory
index 1a1b4a1..b855b00 100644
--- a/inventory
+++ b/inventory
@@ -31,5 +31,14 @@ nijika ansible_user=root ansible_connection=ssh
[dnsinternal]
johan ansible_user=root ansible_connection=ssh
+[pihole]
+johan ansible_user=root ansible_connection=ssh
+
+[lldap]
+johan ansible_user=root ansible_connection=ssh
+
+[ca]
+johan ansible_user=root ansible_connection=ssh
+
[mail]
#ash ansible_user=root ansible_connection=ssh
diff --git a/roles/authelia/templates/docker-compose.yml.j2 b/roles/authelia/templates/docker-compose.yml.j2
index b60545f..aa53bb9 100644
--- a/roles/authelia/templates/docker-compose.yml.j2
+++ b/roles/authelia/templates/docker-compose.yml.j2
@@ -7,8 +7,10 @@ services:
volumes:
- ./authelia:/config
ports:
- - 9091:9091
+ - 127.0.0.1:9091:9091
restart: unless-stopped
+ dns:
+ - {{ johan_ip }}
redis:
image: redis:alpine
container_name: redis
diff --git a/roles/ca/tasks/main.yml b/roles/ca/tasks/main.yml
new file mode 100644
index 0000000..2649686
--- /dev/null
+++ b/roles/ca/tasks/main.yml
@@ -0,0 +1,15 @@
+---
+
+- name: get root CA certificate
+ command: >
+ curl -k -X GET -H "Content-Type:application/json" \
+ "https://{{ step_bootstrap_ca_url }}:{{ step_ca_port }}/root/{{ step_bootstrap_fingerprint }}"
+ register: root_ca_fp
+
+- name: copy to os certificates
+ template:
+ src: "../templates/crt.j2"
+ dest: "/usr/local/share/ca-certificates/{{ step_bootstrap_ca_url }}.crt"
+
+- name: update trusted certs
+ command: "update-ca-certificates"
diff --git a/roles/ca/templates/crt.j2 b/roles/ca/templates/crt.j2
new file mode 100644
index 0000000..f775757
--- /dev/null
+++ b/roles/ca/templates/crt.j2
@@ -0,0 +1 @@
+{{ (root_ca_fp.stdout | from_json).ca }}
diff --git a/roles/common/tasks/systemd-resolved.yml b/roles/common/tasks/systemd-resolved.yml
index dc40275..dbf9742 100644
--- a/roles/common/tasks/systemd-resolved.yml
+++ b/roles/common/tasks/systemd-resolved.yml
@@ -41,6 +41,16 @@
no_extra_spaces: true
register: conf_domains
+- name: stub listener
+ community.general.ini_file:
+ path: /etc/systemd/resolved.conf
+ section: Resolve
+ option: DNSStubListener
+ value: '{{ "yes" if dns_stub_listener else "no" }}'
+ mode: '0644'
+ no_extra_spaces: true
+ register: conf_domains
+
- name: Check if systemd-resolve runs
ansible.builtin.shell: pgrep systemd-resolve
failed_when: false
diff --git a/roles/lldap/tasks/main.yml b/roles/lldap/tasks/main.yml
new file mode 100644
index 0000000..79b9a86
--- /dev/null
+++ b/roles/lldap/tasks/main.yml
@@ -0,0 +1,28 @@
+---
+- name: ensure lldap docker/compose exist
+ file:
+ path: /etc/docker/compose/lldap
+ state: directory
+ owner: root
+ group: root
+ mode: 0700
+
+- name: build lldap docker-compose.yml.j2
+ template:
+ src: ../templates/docker-compose.yml.j2
+ dest: /etc/docker/compose/lldap/docker-compose.yml
+ owner: root
+ group: root
+ mode: u=rw,g=r,o=r
+
+- name: daemon-reload and enable lldap
+ ansible.builtin.systemd_service:
+ state: restarted
+ enabled: true
+ name: docker-compose@lldap
+
+- name: allow ldap on vpn
+ ufw:
+ rule: allow
+ port: '3890'
+ from: '100.64.0.0/10'
diff --git a/roles/lldap/templates/docker-compose.yml.j2 b/roles/lldap/templates/docker-compose.yml.j2
new file mode 100644
index 0000000..c4757b2
--- /dev/null
+++ b/roles/lldap/templates/docker-compose.yml.j2
@@ -0,0 +1,18 @@
+version: "3"
+
+volumes:
+ lldap_data:
+ driver: local
+
+services:
+ lldap:
+ image: lldap/lldap:stable
+ ports:
+ - "{{ johan_ip }}:3890:3890"
+ - "127.0.0.1:17170:17170"
+ volumes:
+ - "lldap_data:/data"
+ environment:
+ - LLDAP_JWT_SECRET="{{ lldap_jwt_secret }}"
+ - LLDAP_LDAP_USER_PASS="{{ lldap_user_pass }}"
+ - LLDAP_LDAP_BASE_DN=dc=simponic,dc=xyz
diff --git a/roles/nameservers/templates/db.simponic.xyz.j2 b/roles/nameservers/templates/db.simponic.xyz.j2
index 5861870..58db1a0 100644
--- a/roles/nameservers/templates/db.simponic.xyz.j2
+++ b/roles/nameservers/templates/db.simponic.xyz.j2
@@ -38,8 +38,3 @@ simponic.xyz. 1 IN MX 10 mail.simponic.xyz.
;; TXT Records
dkim._domainkey.simponic.xyz. 1 IN TXT "v=DKIM1; p= MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoNWLcbrcGq0z8f0fSmxLbfK/Q/ZwmnPyJEfljS2VuDIm7DUXahHIFtB8hfZ/WAocoirb8kUHTvTAgmUOXPpNxTDve3tV9S+CBBYHH2c9XBsuaZn/Vi0TR5vbBDuISmlXT6k+2cdq0LO+PYRwJI65t/JWTR5fQlCmVgxbI5gwDYFRZC0Nl5gEwuKw7pdEJg4Pymyox" "i" "zcikaGk/plXj6BDvv9pK1q8Wa+QPIkuBPFvsEh3KSApMP1p5thzHFaeNyCn5PuYEvbgkal0722px6GvYfR2W/APNRztbmWVewXH6kEWCgOYMkmWiYYLgEwz62rq2SzszP1rrl3WjVi26916wIDAQAB"
simponic.xyz. 1 IN TXT "v=spf1 mx ip4:192.3.248.205 ~all"
-
-_acme-challenge.simponic.xyz. 1 IN TXT "6GcrSuBAj8pNKqiCBWUIONRJjbDVtOizadBDGngW4-U"
-_acme-challenge.simponic.xyz. 1 IN TXT "IErMxNBY3v1Wc3RV06khZXKDC1f9EObYHSXETK5ZKYE"
-_acme-challenge.simponic.xyz. 1 IN TXT "3jWQSVbU-SdebeymX58bir5kEHG1dVHRXYE-P_4Qao8"
-_acme-challenge.simponic.xyz. 1 IN TXT "LXaVBwQCanWKF4cNxzPHHPM9N-kkjhn8_4r6RsveBqs"
diff --git a/roles/pihole/tasks/main.yml b/roles/pihole/tasks/main.yml
new file mode 100644
index 0000000..0467b80
--- /dev/null
+++ b/roles/pihole/tasks/main.yml
@@ -0,0 +1,36 @@
+---
+- name: ensure pihole docker/compose exist
+ file:
+ path: /etc/docker/compose/pihole
+ state: directory
+ owner: root
+ group: root
+ mode: 0700
+
+- name: build pihole docker-compose.yml.j2
+ template:
+ src: ../templates/docker-compose.yml.j2
+ dest: /etc/docker/compose/pihole/docker-compose.yml
+ owner: root
+ group: root
+ mode: u=rw,g=r,o=r
+
+- name: daemon-reload and enable pihole
+ ansible.builtin.systemd_service:
+ state: restarted
+ enabled: true
+ name: docker-compose@pihole
+
+- name: allow dns queries in vpn/tcp
+ ufw:
+ rule: allow
+ from: '100.64.0.0/10'
+ port: '53'
+ proto: 'tcp'
+
+- name: allow dns queries in vpn/udp
+ ufw:
+ rule: allow
+ from: '100.64.0.0/10'
+ port: '53'
+ proto: 'udp'
diff --git a/roles/pihole/templates/docker-compose.yml.j2 b/roles/pihole/templates/docker-compose.yml.j2
new file mode 100644
index 0000000..ed98d52
--- /dev/null
+++ b/roles/pihole/templates/docker-compose.yml.j2
@@ -0,0 +1,18 @@
+version: "3"
+
+services:
+ pihole:
+ container_name: pihole
+ image: pihole/pihole:latest
+ ports:
+ - "{{ johan_ip }}:53:53/tcp"
+ - "{{ johan_ip }}:53:53/udp"
+ - "127.0.0.1:53:53/tcp"
+ - "127.0.0.1:53:53/udp"
+ - "127.0.0.1:9135:80/tcp"
+ environment:
+ WEBPASSWORD: '{{ pihole_webpwd }}'
+ volumes:
+ - './etc-pihole:/etc/pihole'
+ - './etc-dnsmasq.d:/etc/dnsmasq.d'
+ restart: unless-stopped
diff --git a/roles/private/files/johan/http.ca.internal.simponic.xyz.conf b/roles/private/files/johan/http.ca.internal.simponic.xyz.conf
new file mode 100644
index 0000000..f1cea50
--- /dev/null
+++ b/roles/private/files/johan/http.ca.internal.simponic.xyz.conf
@@ -0,0 +1,13 @@
+server {
+ listen 80;
+ server_name ca.internal.simponic.xyz;
+
+ location /.well-known/acme-challenge {
+ root /var/www/letsencrypt;
+ try_files $uri $uri/ =404;
+ }
+
+ location / {
+ rewrite ^ https://ca.internal.simponic.xyz$request_uri? permanent;
+ }
+}
diff --git a/roles/private/files/johan/http.lldap.internal.simponic.xyz.conf b/roles/private/files/johan/http.lldap.internal.simponic.xyz.conf
new file mode 100644
index 0000000..8b9efe5
--- /dev/null
+++ b/roles/private/files/johan/http.lldap.internal.simponic.xyz.conf
@@ -0,0 +1,13 @@
+server {
+ listen 80;
+ server_name lldap.internal.simponic.xyz;
+
+ location /.well-known/acme-challenge {
+ root /var/www/letsencrypt;
+ try_files $uri $uri/ =404;
+ }
+
+ location / {
+ rewrite ^ https://lldap.internal.simponic.xyz$request_uri? permanent;
+ }
+}
diff --git a/roles/private/files/johan/http.pihole.internal.simponic.xyz.conf b/roles/private/files/johan/http.pihole.internal.simponic.xyz.conf
new file mode 100644
index 0000000..a30ffc3
--- /dev/null
+++ b/roles/private/files/johan/http.pihole.internal.simponic.xyz.conf
@@ -0,0 +1,13 @@
+server {
+ listen 80;
+ server_name pihole.internal.simponic.xyz;
+
+ location /.well-known/acme-challenge {
+ root /var/www/letsencrypt;
+ try_files $uri $uri/ =404;
+ }
+
+ location / {
+ rewrite ^ https://pihole.internal.simponic.xyz$request_uri? permanent;
+ }
+}
diff --git a/roles/private/files/johan/https.ca.internal.simponic.xyz.conf b/roles/private/files/johan/https.ca.internal.simponic.xyz.conf
new file mode 100644
index 0000000..8d7d190
--- /dev/null
+++ b/roles/private/files/johan/https.ca.internal.simponic.xyz.conf
@@ -0,0 +1,32 @@
+server {
+ listen 443 ssl;
+ server_name ca.internal.simponic.xyz;
+
+ ssl_certificate /etc/letsencrypt/live/ca.internal.simponic.xyz/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/ca.internal.simponic.xyz/privkey.pem;
+ ssl_trusted_certificate /etc/letsencrypt/live/ca.internal.simponic.xyz/fullchain.pem;
+
+ ssl_session_cache shared:SSL:50m;
+ ssl_session_timeout 5m;
+ ssl_stapling on;
+ ssl_stapling_verify on;
+
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
+ ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
+
+ ssl_dhparam /etc/nginx/dhparams.pem;
+ ssl_prefer_server_ciphers on;
+
+ location / {
+ proxy_pass https://ca.internal.simponic.xyz:5239;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_set_header Host $server_name;
+ proxy_buffering off;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
+ add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
+ }
+}
diff --git a/roles/private/files/johan/https.lldap.internal.simponic.xyz.conf b/roles/private/files/johan/https.lldap.internal.simponic.xyz.conf
new file mode 100644
index 0000000..98306f5
--- /dev/null
+++ b/roles/private/files/johan/https.lldap.internal.simponic.xyz.conf
@@ -0,0 +1,32 @@
+server {
+ listen 443 ssl;
+ server_name lldap.internal.simponic.xyz;
+
+ ssl_certificate /etc/letsencrypt/live/lldap.internal.simponic.xyz/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/lldap.internal.simponic.xyz/privkey.pem;
+ ssl_trusted_certificate /etc/letsencrypt/live/lldap.internal.simponic.xyz/fullchain.pem;
+
+ ssl_session_cache shared:SSL:50m;
+ ssl_session_timeout 5m;
+ ssl_stapling on;
+ ssl_stapling_verify on;
+
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
+ ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
+
+ ssl_dhparam /etc/nginx/dhparams.pem;
+ ssl_prefer_server_ciphers on;
+
+ location / {
+ proxy_pass http://127.0.0.1:17170;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_set_header Host $server_name;
+ proxy_buffering off;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
+ add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
+ }
+}
diff --git a/roles/private/files/johan/https.pihole.internal.simponic.xyz.conf b/roles/private/files/johan/https.pihole.internal.simponic.xyz.conf
new file mode 100644
index 0000000..b231f3f
--- /dev/null
+++ b/roles/private/files/johan/https.pihole.internal.simponic.xyz.conf
@@ -0,0 +1,32 @@
+server {
+ listen 443 ssl;
+ server_name pihole.internal.simponic.xyz;
+
+ ssl_certificate /etc/letsencrypt/live/pihole.internal.simponic.xyz/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/pihole.internal.simponic.xyz/privkey.pem;
+ ssl_trusted_certificate /etc/letsencrypt/live/pihole.internal.simponic.xyz/fullchain.pem;
+
+ ssl_session_cache shared:SSL:50m;
+ ssl_session_timeout 5m;
+ ssl_stapling on;
+ ssl_stapling_verify on;
+
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
+ ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
+
+ ssl_dhparam /etc/nginx/dhparams.pem;
+ ssl_prefer_server_ciphers on;
+
+ location / {
+ proxy_pass http://127.0.0.1:9135;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_set_header Host $server_name;
+ proxy_buffering off;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
+ add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
+ }
+}
diff --git a/roles/private/files/nginx.conf b/roles/private/files/nginx.conf
new file mode 100644
index 0000000..f978052
--- /dev/null
+++ b/roles/private/files/nginx.conf
@@ -0,0 +1,27 @@
+user www-data;
+worker_processes 4;
+pid /run/nginx.pid;
+load_module modules/ndk_http_module.so;
+
+events {
+ worker_connections 768;
+}
+
+http {
+ sendfile on;
+ tcp_nopush on;
+ tcp_nodelay on;
+ keepalive_timeout 65;
+ types_hash_max_size 2048;
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ access_log /var/log/nginx/access.log;
+ error_log /var/log/nginx/error.log;
+
+ gzip on;
+ gzip_disable "msie6";
+
+ include /etc/nginx/conf.d/*.conf;
+ include /etc/nginx/sites-enabled/*;
+}
diff --git a/roles/private/tasks/main.yml b/roles/private/tasks/main.yml
new file mode 100644
index 0000000..5c4eb7e
--- /dev/null
+++ b/roles/private/tasks/main.yml
@@ -0,0 +1,95 @@
+---
+- name: allow http from vpn
+ ufw:
+ rule: allow
+ port: '80'
+ proto: tcp
+ from: 100.64.0.0/10
+
+- name: allow https from vpn
+ ufw:
+ rule: allow
+ port: '443'
+ proto: tcp
+ from: 100.64.0.0/10
+
+- name: restart ufw
+ service: name=ufw state=restarted enabled=yes
+
+- name: install letsencrypt
+ apt: name=letsencrypt state=latest
+
+- name: create letsencrypt directory
+ file: name=/var/www/letsencrypt state=directory
+
+- name: install nginx
+ apt: name=nginx state=latest
+
+- name: remove default nginx
+ file: name=/etc/nginx/sites-enabled/default state=absent
+
+- name: generate dhparams
+ shell: openssl dhparam -out /etc/nginx/dhparams.pem 2048
+ args:
+ creates: /etc/nginx/dhparams.pem
+
+- name: add system nginx config
+ template:
+ src: ../files/nginx.conf
+ dest: /etc/nginx/nginx.conf
+
+- name: copy http nginx configuration for each domain
+ copy:
+ src: "{{ item }}"
+ dest: "/etc/nginx/sites-enabled/"
+ with_fileglob:
+ - "files/{{ inventory_hostname }}/http.*.conf"
+
+- name: restart nginx to get letsencrypt certificate
+ service: name=nginx state=restarted enabled=yes
+
+- name: find deployed domains
+ ansible.builtin.find:
+ paths: "/etc/nginx/sites-enabled/"
+ patterns: "http.*.conf"
+ register: nginx_conf_files
+ delegate_to: "{{ inventory_hostname }}"
+
+- name: extract domains from deployed nginx configurations
+ shell: |
+ grep -oP 'server_name\s+\K[^;]+' "{{ item.path }}"
+ loop: "{{ nginx_conf_files.files }}"
+ register: extracted_domains
+
+# crt is given from the "ca" role to all hosts; that needs to run first
+- name: request letsencrypt certificate
+ shell: >
+ REQUESTS_CA_BUNDLE="/usr/local/share/ca-certificates/{{ step_bootstrap_ca_url }}.crt" \
+ letsencrypt certonly -n -d {{ item.stdout }} \
+ --server https://{{ step_bootstrap_ca_url }}:{{ step_ca_port }}/acme/ACME/directory \
+ --webroot -w /var/www/letsencrypt \
+ --agree-tos --email {{ step_acme_cert_contact }}
+ args:
+ creates: "/etc/letsencrypt/live/{{ item.stdout }}"
+ loop: "{{ extracted_domains.results }}"
+ when: item.stdout != ""
+
+- name: copy https nginx configuration for each domain
+ copy:
+ src: "{{ item }}"
+ dest: "/etc/nginx/sites-enabled/"
+ with_fileglob:
+ - "files/{{ inventory_hostname }}/https.*.conf"
+
+- name: reload nginx to activate sites
+ service: name=nginx state=restarted
+
+- name: add monthly letsencrypt cronjob for cert renewal based on hash of domain name to prevent hitting LE rate limits
+ cron:
+ name: "letsencrypt_renewal_{{ item.stdout }}"
+ day: "{{ '%02d' | format(1 + (item.stdout | hash('md5') | int(0, 16) % 27)) }}"
+ hour: "{{ (item.stdout | hash('md5') | int(0, 16) % 24 ) }}"
+ minute: "{{ (item.stdout | hash('md5') | int(0, 16) % 60 ) }}"
+ job: "REQUESTS_CA_BUNDLE=/usr/local/share/ca-certificates/{{ step_bootstrap_ca_url }}.crt letsencrypt renew --server https://{{ step_bootstrap_ca_url }}:{{ step_ca_port }}/acme/ACME/directory --cert-name {{ item.stdout }} -n --webroot -w /var/www/letsencrypt --agree-tos --email {{ step_acme_cert_contact }} && service nginx reload"
+ loop: "{{ extracted_domains.results }}"
+ when: item.stdout != ""
diff --git a/roles/vpn/files/config/acl.json b/roles/vpn/files/config/acl.json
index 2dbb13a..7c28276 100644
--- a/roles/vpn/files/config/acl.json
+++ b/roles/vpn/files/config/acl.json
@@ -1,6 +1,7 @@
{
"groups": {
- "group:admin": ["elizabeth.hunt"]
+ "group:admin": ["elizabeth.hunt"],
+ "group:sys": ["sys"]
},
"tagOwners": {
"tag:prod": ["group:admin"],
@@ -14,8 +15,14 @@
"dst": [
"tag:dev:*",
"tag:private:*",
- "tag:prod:*"
+ "tag:prod:*",
+ "group:sys:*"
]
+ },
+ {
+ "action": "accept",
+ "src": ["group:sys"],
+ "dst": ["group:sys:*"]
}
]
}
diff --git a/roles/vpn/files/docker-compose.yml b/roles/vpn/files/docker-compose.yml
index dc5e961..38d58d3 100644
--- a/roles/vpn/files/docker-compose.yml
+++ b/roles/vpn/files/docker-compose.yml
@@ -7,7 +7,7 @@ services:
- ./config:/etc/headscale
- ./data:/var/lib/headscale
ports:
- - 27896:8080
+ - 127.0.0.1:27896:8080
command: headscale serve
restart: unless-stopped
headscale-ui:
@@ -15,4 +15,4 @@ services:
restart: unless-stopped
container_name: headscale-ui
ports:
- - 9443:443
+ - 127.0.0.1:9443:443
diff --git a/roles/vpn/templates/config.yml.j2 b/roles/vpn/templates/config.yml.j2
index 4f815f4..d083c8f 100644
--- a/roles/vpn/templates/config.yml.j2
+++ b/roles/vpn/templates/config.yml.j2
@@ -177,7 +177,7 @@ dns_config:
# List of DNS servers to expose to clients.
nameservers:
- - 1.1.1.1
+ - {{ johan_ip }}
# NextDNS (see https://tailscale.com/kb/1218/nextdns/).
# "abc123" is example NextDNS ID, replace with yours.
@@ -202,7 +202,7 @@ dns_config:
# - 8.8.8.8
# Search domains to inject.
- domains: ['simponic.xyz']
+ domains: ['simponic.xyz', 'internal.simponic.xyz']
# Extra DNS records
# so far only A-records are supported (on the tailscale side)
@@ -223,7 +223,7 @@ dns_config:
# `base_domain` must be a FQDNs, without the trailing dot.
# The FQDN of the hosts will be
# `hostname.user.base_domain` (e.g., _myhost.myuser.example.com_).
- base_domain: headscale.simponic.xyz
+ base_domain: internal.simponic.xyz
# Unix socket used for the CLI to connect without authentication
# Note: for production you will want to set this to something like: