diff --git a/.gitignore b/.gitignore index 40ba34d..0712049 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ # Ansible configuration files ansible/inventory.yml -ansible/vars.yml +vars-*.yml diff --git a/README.md b/README.md index 99541f5..b7e9c19 100644 --- a/README.md +++ b/README.md @@ -4,88 +4,27 @@ This repository contains the scripts, configuration files, and Ansible playbooks Feel free to read, copy, use, and suggest improvements. -## Configuration +## Ansible configurations -Ansible is used for configuration. The playbooks use a `vars.yml` file for settings. This file contains sensitive information, so it is not included in the repository. Below is an example of its structure: +Available roles: -```yml ---- -ssh_public_key: "ssh-rsa AAAAB3...ak4EsUU=" -mx1_domains: - - mx1.pikami.org -mx2_domains: - - mx2.pikami.org -mx1_mail_domain: mx1.pikami.org -mx2_mail_domain: mx2.pikami.org -mail_domains: - - pikami.net - - pikami.org -mail_users: - - user: bob@pikami.org - password: Password123 - virtuals: - - "bob@pikami.net" - - "bob.coolman@pikami.net" - - user: alice@pikami.org - password: Password123 - virtuals: - - "alice@pikami.net" +- firewall - configures firewall +- initial-setup + - applies available system patches + - upgrades all installed packages + - installs nano and curl + - disables ssh password logins + - adds ssh public key +- mail-primary - installs and configures opensmtpd with dovecot and sive +- mail-secondary - installs and configures opensmtpd as a backup mail receiver +- prometheus-exporters - installs prometheus exporters +- ssl - generates ssl certificates and adds renew cron job +- vpn - configures wireguard vpn -mx1_wg: - private_key: - address: - port: 21841 - interface: wg0 - peers: - - name: Gateway - public_key: - endpoint: :21841 - allowed_ips: 10.2.0.1/32 -mx2_wg: - private_key: - address: - port: 21841 - interface: wg0 - peers: - - name: Gateway - public_key: - endpoint: :21841 - allowed_ips: 10.2.0.1/32 +Available playbooks: -mx1_fw: - interfaces: - - name: vio0 - allowed_tcp: - - 22 # SSH - - 80 # HTTP - - 443 # HTTPS - - 25 # SMTP Relay - - 587 # SMTP Submission - - 465 # SMTPS Submission - - 143 # IMAP - - 993 # IMAPS - - 4190 # Sive - allowed_udp: - - 21841 # Wireguard - - name: wg0 - allowed_tcp: - - 22 # SSH - - 9100 # Prometheus node exporter -mx2_fw: - interfaces: - - name: vio0 - allowed_tcp: - - 22 # SSH - - 80 # HTTP - - 443 # HTTPS - - 25 # SMTP Relay - allowed_udp: - - 21841 # Wireguard - - name: wg0 - allowed_tcp: - - 22 # SSH - - 9100 # Prometheus node exporter -``` +- primary-mail.yml - sets up a primary mail server, uses `vars-mx1.yml` for configuration +- secondary-mail.yml - sets up a secondary mail server, uses `vars-mx2.yml` for configuration The hosts are taken from the `inventory.yml` file: @@ -93,7 +32,7 @@ The hosts are taken from the `inventory.yml` file: all: hosts: mx1: - ansible_host: + ansible_host: mx2: ansible_host: ``` @@ -117,21 +56,6 @@ ansible-galaxy collection install community.general Run playbooks ```sh -# replace "01-initial_setup.yml" with the playbook you want to run -ansible-playbook -i inventory.yml 01-initial_setup.yml +# replace "secondary.yml" with the playbook you want to run +ansible-playbook -i inventory.yml secondary.yml ``` - -Current ansible playbooks: - -- 01-initial_setup.yml - - applies available system patches - - upgrades all installed packages - - installs nano, curl and git - - disables ssh password logins - - adds ssh public key - - configures firewall -- 02-ssl.yml - generates ssl certificates and adds a renew cron job -- 03-mail.yml - installs and configures dovecot and opensmtpd -- 04-secondary-mail.yml - installs and configures opensmtpd as a backup mail receiver -- 05-vpn.yml - configures wireguard vpn -- 06-prometheus-exporters.yml - installs prometheus exporters diff --git a/ansible/01-initial_setup.yml b/ansible/01-initial_setup.yml deleted file mode 100644 index 1251b42..0000000 --- a/ansible/01-initial_setup.yml +++ /dev/null @@ -1,56 +0,0 @@ -- name: Initial System Setup - hosts: - - mx1 - - mx2 - remote_user: root - become: true - become_method: su - vars_files: - - vars.yml - tasks: - - name: Apply all available system patches - command: syspatch - register: syspatch - failed_when: syspatch.rc != 0 and syspatch.rc != 2 - changed_when: syspatch.rc == 0 - - - name: Update package list and upgrade all packages - command: pkg_add -u - - - name: Install essential packages - community.general.openbsd_pkg: - name: - - nano - - curl - - git - state: present - - - name: Disable SSH password authentication - ansible.builtin.lineinfile: - path: /etc/ssh/sshd_config - regexp: "^#?PasswordAuthentication" - line: "PasswordAuthentication no" - state: present - register: sshd_config - - - name: Restart SSH service to apply changes - ansible.builtin.service: - name: sshd - state: restarted - when: sshd_config.changed - - - name: Add SSH public key to authorized_keys - ansible.posix.authorized_key: - user: root - key: "{{ ssh_public_key }}" - - - name: Configure firewall - template: - src: "templates/pf.conf.j2" - dest: /etc/pf.conf - validate: pfctl -n -f %s - register: pf - - - name: Load config to pf if needed - command: pfctl -f /etc/pf.conf - when: pf.changed diff --git a/ansible/02-ssl.yml b/ansible/02-ssl.yml deleted file mode 100644 index 6a9b5da..0000000 --- a/ansible/02-ssl.yml +++ /dev/null @@ -1,52 +0,0 @@ -- name: SSL Setup - hosts: - - mx1 - - mx2 - remote_user: root - vars_files: - - vars.yml - tasks: - - name: Create vhost directories - file: - path: "/var/www/vhosts/{{ item }}" - state: directory - owner: www - with_items: "{{ lookup('vars', inventory_hostname + '_domains') }}" - - - name: Install httpd.conf - template: - src: "templates/httpd.conf" - dest: "/etc/httpd.conf" - - - name: Enable and start httpd - service: - name: httpd - enabled: yes - state: started - - - name: Install acme-client.conf - template: - src: "templates/acme-client.conf" - dest: "/etc/acme-client.conf" - - - name: Initial acme-client run - command: "/usr/sbin/acme-client {{ item }}" - args: - creates: "/etc/ssl/{{ item }}.fullchain.pem" - with_items: "{{ lookup('vars', inventory_hostname + '_domains') }}" - notify: - - reload httpd - - - name: Renew certificates via root crontab - cron: - name: "acme-client renew {{ item }}" - minute: "0" - job: "sleep $((RANDOM \\% 2048)) && acme-client {{ item }} && rcctl reload httpd" - user: root - with_items: "{{ lookup('vars', inventory_hostname + '_domains') }}" - - handlers: - - name: reload httpd - service: - name: httpd - state: reloaded diff --git a/ansible/03-mail.yml b/ansible/03-mail.yml deleted file mode 100644 index e70ef1f..0000000 --- a/ansible/03-mail.yml +++ /dev/null @@ -1,121 +0,0 @@ -- name: OpenSMTPD Installation and Configuration - hosts: - - mx1 - remote_user: root - vars_files: - - vars.yml - tasks: - - name: Install Packages - community.general.openbsd_pkg: - name: - - opensmtpd-filter-dkimsign - - dovecot - - dovecot-pigeonhole - - opensmtpd-extras - state: present - - - name: Create the vmail group - group: - name: vmail - gid: 2000 - - - name: Create vmail user - user: - name: vmail - group: vmail - shell: /sbin/nologin - createhome: yes - home: /var/mail/vmail - uid: 2000 - - - name: Generate dkim keys - shell: | - KEYLEN=1024 - DOMAIN={{ mx1_mail_domain }} - - mkdir -p /etc/mail/dkim - if [ -f /etc/mail/dkim/$DOMAIN.key ]; then - echo "$DOMAIN.key already exists." - exit 0 - fi - - cd /etc/mail/dkim - (umask 337; openssl genrsa -out $DOMAIN.key $KEYLEN) - openssl rsa -in $DOMAIN.key -pubout -out $DOMAIN.pub - group info _dkimsign >/dev/null && chgrp _dkimsign $DOMAIN.key - echo "add the $DOMAIN.dns to the zone file" - echo "selector1._domainkey.$DOMAIN. 3600 IN TXT \"v=DKIM1; k=rsa; p=$(sed -e '1d' -e '$d' $DOMAIN.pub | tr -d '\n')\"" > ~/$DOMAIN.dns - - - name: Configure OpenSMTPD smtpd.conf - template: - src: "templates/smtpd.conf" - dest: /etc/mail/smtpd.conf - notify: - - reload smtpd - - - name: Enable and start OpenSMTPD service - service: - name: smtpd - enabled: yes - state: started - - - name: Delete default dovecot configs - shell: | - if [ -f /etc/dovecot/conf.d/10-ssl.conf ]; then - cd /etc/dovecot/ - rm -rf * - fi - - - name: Install dovecot.conf - template: - src: "templates/dovecot.conf" - dest: "/etc/dovecot/dovecot.conf" - notify: - - reload dovecot - - - name: Configure users - block: - - name: Remove existing - shell: | - echo "" > /etc/dovecot/users - chmod 640 /etc/dovecot/users - chown _smtpd:_dovecot /etc/dovecot/users - - echo "" > /etc/mail/accounts - chmod 640 /etc/mail/accounts - chown _smtpd: /etc/mail/accounts - - echo "" > /etc/mail/virtuals - chown _smtpd: /etc/mail/virtuals - - - name: Add user accounts - loop: "{{ mail_users }}" - no_log: true - shell: | - DOVECOT_PASS=$(doveadm pw -p {{ item.password }}) - SMTP_PASS=$(smtpctl encrypt {{ item.password }}) - - echo "{{ item.user }}:$DOVECOT_PASS::::" >> /etc/dovecot/users - echo "{{ item.user }}:$SMTP_PASS::::" >> /etc/mail/accounts - - - name: Install dovecot.conf - template: - src: "templates/virtuals.conf" - dest: "/etc/mail/virtuals" - - - name: Enable dovecot service - service: - name: dovecot - enabled: true - state: started - - handlers: - - name: reload smtpd - service: - name: smtpd - state: restarted - - - name: reload dovecot - service: - name: dovecot - state: reloaded diff --git a/ansible/04-secondary-mail.yml b/ansible/04-secondary-mail.yml deleted file mode 100644 index af72e71..0000000 --- a/ansible/04-secondary-mail.yml +++ /dev/null @@ -1,25 +0,0 @@ -- name: Secondary MX OpenSMTPD Configuration - hosts: - - mx2 - remote_user: root - vars_files: - - vars.yml - tasks: - - name: Configure OpenSMTPD smtpd.conf - template: - src: "templates/secondary-smtpd.conf" - dest: /etc/mail/smtpd.conf - notify: - - reload smtpd - - - name: Enable and start OpenSMTPD service - service: - name: smtpd - enabled: yes - state: started - - handlers: - - name: reload smtpd - service: - name: smtpd - state: restarted diff --git a/ansible/05-vpn.yml b/ansible/05-vpn.yml deleted file mode 100644 index 82190e7..0000000 --- a/ansible/05-vpn.yml +++ /dev/null @@ -1,37 +0,0 @@ -- name: VPN Setup - hosts: - - mx1 - - mx2 - remote_user: root - vars_files: - - vars.yml - tasks: - - name: Install wireguard - community.general.openbsd_pkg: - name: - - wireguard-tools - state: present - - - name: Ensures /etc/wireguard dir exists - file: - path: "/etc/wireguard" - state: directory - - - name: Create wireguard config - template: - src: "templates/wireguard.conf.j2" - dest: "/etc/wireguard/{{ lookup('vars', inventory_hostname + '_wg').interface }}.conf" - owner: root - group: wheel - mode: "0600" - register: wg_config - - - name: Create wireguard interface - template: - src: "templates/wireguard.if.j2" - dest: "/etc/hostname.{{ lookup('vars', inventory_hostname + '_wg').interface }}" - register: iface_config - - - name: Apply network configuration if changed - shell: sh /etc/netstart {{ lookup('vars', inventory_hostname + '_wg').interface }} - when: wg_config.changed or iface_config.changed diff --git a/ansible/06-prometheus-exporters.yml b/ansible/06-prometheus-exporters.yml deleted file mode 100644 index e2d7b9d..0000000 --- a/ansible/06-prometheus-exporters.yml +++ /dev/null @@ -1,19 +0,0 @@ -- name: VPN Setup - hosts: - - mx1 - - mx2 - remote_user: root - vars_files: - - vars.yml - tasks: - - name: Install node-exporter - community.general.openbsd_pkg: - name: - - node_exporter - state: present - - - name: Enable and start node_exporter - service: - name: node_exporter - enabled: yes - state: started diff --git a/ansible/primary-mail.yml b/ansible/primary-mail.yml new file mode 100644 index 0000000..59e0ef7 --- /dev/null +++ b/ansible/primary-mail.yml @@ -0,0 +1,13 @@ +- name: Primary server configuration + hosts: + - mx1 + remote_user: root + vars_files: + - vars-{{ inventory_hostname }}.yml + roles: + - initial-setup + - firewall + - ssl + - mail-primary + - vpn + - prometheus-exporters diff --git a/ansible/roles/firewall/defaults/main.yml b/ansible/roles/firewall/defaults/main.yml new file mode 100644 index 0000000..d1c618d --- /dev/null +++ b/ansible/roles/firewall/defaults/main.yml @@ -0,0 +1,9 @@ +--- +firewall_interfaces: + - name: vio0 + allowed_tcp: + - 22 # SSH + - 80 # HTTP + - 443 # HTTPS + allowed_udp: + - 21841 # Wireguard diff --git a/ansible/roles/firewall/tasks/main.yml b/ansible/roles/firewall/tasks/main.yml new file mode 100644 index 0000000..dfd09de --- /dev/null +++ b/ansible/roles/firewall/tasks/main.yml @@ -0,0 +1,11 @@ +--- +- name: Configure firewall + template: + src: templates/pf.conf.j2 + dest: /etc/pf.conf + validate: pfctl -n -f %s + register: pf + +- name: Load config to pf if needed + command: pfctl -f /etc/pf.conf + when: pf.changed diff --git a/ansible/templates/pf.conf.j2 b/ansible/roles/firewall/templates/pf.conf.j2 similarity index 90% rename from ansible/templates/pf.conf.j2 rename to ansible/roles/firewall/templates/pf.conf.j2 index 843454d..901c542 100644 --- a/ansible/templates/pf.conf.j2 +++ b/ansible/roles/firewall/templates/pf.conf.j2 @@ -1,4 +1,3 @@ -{% set _fw = lookup('vars', inventory_hostname + '_fw') %} # {{ ansible_managed }} # Skip filtering on the loopback interface set skip on lo @@ -12,7 +11,7 @@ block return in on ! lo0 proto tcp to port 6000:6010 # Port build user does not need network block return out log proto {tcp udp} user _pbuild -{% for interface in _fw.interfaces %} +{% for interface in firewall_interfaces %} # Pass rules for the specific ports on the {{ interface.name }} interface {% if (interface.allowed_tcp is defined) and interface.allowed_tcp %} {% for port in interface.allowed_tcp %} diff --git a/ansible/roles/initial-setup/defaults/main.yml b/ansible/roles/initial-setup/defaults/main.yml new file mode 100644 index 0000000..5e8eb6a --- /dev/null +++ b/ansible/roles/initial-setup/defaults/main.yml @@ -0,0 +1,2 @@ +--- +ssh_public_key: "ssh-rsa AAAAB3...ak4EsUU=" diff --git a/ansible/roles/initial-setup/tasks/main.yml b/ansible/roles/initial-setup/tasks/main.yml new file mode 100644 index 0000000..25c8f53 --- /dev/null +++ b/ansible/roles/initial-setup/tasks/main.yml @@ -0,0 +1,35 @@ +--- +- name: Apply all available system patches + command: syspatch + register: syspatch + failed_when: syspatch.rc != 0 and syspatch.rc != 2 + changed_when: syspatch.rc == 0 + +- name: Update package list and upgrade all packages + command: pkg_add -u + +- name: Install essential packages + community.general.openbsd_pkg: + name: + - nano + - curl + state: present + +- name: Disable SSH password authentication + ansible.builtin.lineinfile: + path: /etc/ssh/sshd_config + regexp: "^#?PasswordAuthentication" + line: "PasswordAuthentication no" + state: present + register: sshd_config + +- name: Restart SSH service to apply changes + ansible.builtin.service: + name: sshd + state: restarted + when: sshd_config.changed + +- name: Add SSH public key to authorized_keys + ansible.posix.authorized_key: + user: root + key: "{{ ssh_public_key }}" diff --git a/ansible/roles/mail-primary/defaults/main.yml b/ansible/roles/mail-primary/defaults/main.yml new file mode 100644 index 0000000..c55fbfb --- /dev/null +++ b/ansible/roles/mail-primary/defaults/main.yml @@ -0,0 +1,15 @@ +--- +server_domain: mx1.example.com +mail_receive_domains: + - example.net + - example.com +mail_users: + - user: bob@example.com + password: Password123 + virtuals: + - "bob@example.net" + - "bob.coolman@example.net" + - user: alice@example.com + password: Password123 + virtuals: + - "alice@example.net" diff --git a/ansible/roles/mail-primary/handlers/main.yml b/ansible/roles/mail-primary/handlers/main.yml new file mode 100644 index 0000000..92e4b8b --- /dev/null +++ b/ansible/roles/mail-primary/handlers/main.yml @@ -0,0 +1,10 @@ +--- +- name: reload smtpd + service: + name: smtpd + state: restarted + +- name: reload dovecot + service: + name: dovecot + state: reloaded diff --git a/ansible/roles/mail-primary/tasks/main.yml b/ansible/roles/mail-primary/tasks/main.yml new file mode 100644 index 0000000..7d06b4f --- /dev/null +++ b/ansible/roles/mail-primary/tasks/main.yml @@ -0,0 +1,104 @@ +--- +- name: Install Packages + community.general.openbsd_pkg: + name: + - opensmtpd-filter-dkimsign + - dovecot + - dovecot-pigeonhole + - opensmtpd-extras + state: present + +- name: Create the vmail group + group: + name: vmail + gid: 2000 + +- name: Create vmail user + user: + name: vmail + group: vmail + shell: /sbin/nologin + createhome: yes + home: /var/mail/vmail + uid: 2000 + +- name: Generate dkim keys + shell: | + KEYLEN=1024 + DOMAIN={{ server_domain }} + + mkdir -p /etc/mail/dkim + if [ -f /etc/mail/dkim/$DOMAIN.key ]; then + echo "$DOMAIN.key already exists." + exit 0 + fi + + cd /etc/mail/dkim + (umask 337; openssl genrsa -out $DOMAIN.key $KEYLEN) + openssl rsa -in $DOMAIN.key -pubout -out $DOMAIN.pub + group info _dkimsign >/dev/null && chgrp _dkimsign $DOMAIN.key + echo "add the $DOMAIN.dns to the zone file" + echo "selector1._domainkey.$DOMAIN. 3600 IN TXT \"v=DKIM1; k=rsa; p=$(sed -e '1d' -e '$d' $DOMAIN.pub | tr -d '\n')\"" > ~/$DOMAIN.dns + +- name: Configure OpenSMTPD smtpd.conf + template: + src: "templates/smtpd.conf" + dest: /etc/mail/smtpd.conf + notify: + - reload smtpd + +- name: Enable and start OpenSMTPD service + service: + name: smtpd + enabled: yes + state: started + +- name: Delete default dovecot configs + shell: | + if [ -f /etc/dovecot/conf.d/10-ssl.conf ]; then + cd /etc/dovecot/ + rm -rf * + fi + +- name: Install dovecot.conf + template: + src: "templates/dovecot.conf" + dest: "/etc/dovecot/dovecot.conf" + notify: + - reload dovecot + +- name: Configure users + block: + - name: Remove existing + shell: | + echo "" > /etc/dovecot/users + chmod 640 /etc/dovecot/users + chown _smtpd:_dovecot /etc/dovecot/users + + echo "" > /etc/mail/accounts + chmod 640 /etc/mail/accounts + chown _smtpd: /etc/mail/accounts + + echo "" > /etc/mail/virtuals + chown _smtpd: /etc/mail/virtuals + + - name: Add user accounts + loop: "{{ mail_users }}" + no_log: true + shell: | + DOVECOT_PASS=$(doveadm pw -p {{ item.password }}) + SMTP_PASS=$(smtpctl encrypt {{ item.password }}) + + echo "{{ item.user }}:$DOVECOT_PASS::::" >> /etc/dovecot/users + echo "{{ item.user }}:$SMTP_PASS::::" >> /etc/mail/accounts + + - name: Install dovecot.conf + template: + src: "templates/virtuals.conf" + dest: "/etc/mail/virtuals" + +- name: Enable dovecot service + service: + name: dovecot + enabled: true + state: started diff --git a/ansible/templates/dovecot.conf b/ansible/roles/mail-primary/templates/dovecot.conf similarity index 95% rename from ansible/templates/dovecot.conf rename to ansible/roles/mail-primary/templates/dovecot.conf index 1b3d264..39c0bbf 100644 --- a/ansible/templates/dovecot.conf +++ b/ansible/roles/mail-primary/templates/dovecot.conf @@ -1,7 +1,8 @@ +# {{ ansible_managed }} # Enable ssl ssl = required -ssl_cert = < /etc/ssl/{{ mx1_mail_domain }}.fullchain.pem -ssl_key = < /etc/ssl/private/{{ mx1_mail_domain }}.key +ssl_cert = < /etc/ssl/{{ server_domain }}.fullchain.pem +ssl_key = < /etc/ssl/private/{{ server_domain }}.key ssl_min_protocol = TLSv1.2 ssl_prefer_server_ciphers = yes diff --git a/ansible/roles/mail-primary/templates/smtpd.conf b/ansible/roles/mail-primary/templates/smtpd.conf new file mode 100644 index 0000000..bc58af5 --- /dev/null +++ b/ansible/roles/mail-primary/templates/smtpd.conf @@ -0,0 +1,25 @@ +# {{ ansible_managed }} +pki {{ server_domain }} cert "/etc/ssl/{{ server_domain }}.fullchain.pem" +pki {{ server_domain }} key "/etc/ssl/private/{{ server_domain }}.key" + +table aliases file:/etc/mail/aliases +table users passwd:/etc/mail/accounts +table virtuals file:/etc/mail/virtuals + +filter dkimsign_rsa proc-exec "filter-dkimsign -d {{ server_domain }} -s selector1 \ + -k /etc/mail/dkim/{{ server_domain }}.key" user _dkimsign group _dkimsign + +listen on socket filter dkimsign_rsa +listen on all tls pki {{ server_domain }} +listen on all port submission tls-require pki {{ server_domain }} auth filter dkimsign_rsa +listen on all port smtps tls-require pki {{ server_domain }} auth filter dkimsign_rsa + +action "local_mail" lmtp "/var/dovecot/lmtp" rcpt-to virtual +action "outbound" relay + +{% for domain in mail_receive_domains %} +match from any for domain {{ domain }} action "local_mail" +{% endfor %} +match from local for local action "local_mail" +match from local for any action "outbound" +match auth from any for any action "outbound" diff --git a/ansible/templates/virtuals.conf b/ansible/roles/mail-primary/templates/virtuals.conf similarity index 89% rename from ansible/templates/virtuals.conf rename to ansible/roles/mail-primary/templates/virtuals.conf index 00cf32b..3ad70bf 100644 --- a/ansible/templates/virtuals.conf +++ b/ansible/roles/mail-primary/templates/virtuals.conf @@ -1,3 +1,4 @@ +# {{ ansible_managed }} {% for user in mail_users %} {{ user.user }}: vmail {% if (user.virtuals is defined) and user.virtuals %} diff --git a/ansible/roles/mail-secondary/defaults/main.yml b/ansible/roles/mail-secondary/defaults/main.yml new file mode 100644 index 0000000..75a0b47 --- /dev/null +++ b/ansible/roles/mail-secondary/defaults/main.yml @@ -0,0 +1,6 @@ +--- +primary_mail_server: mx1.example.com +server_domain: mx1.example.com +mail_receive_domains: + - example.net + - example.com \ No newline at end of file diff --git a/ansible/roles/mail-secondary/handlers/main.yml b/ansible/roles/mail-secondary/handlers/main.yml new file mode 100644 index 0000000..7b43c6b --- /dev/null +++ b/ansible/roles/mail-secondary/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: reload smtpd + service: + name: smtpd + state: restarted diff --git a/ansible/roles/mail-secondary/tasks/main.yml b/ansible/roles/mail-secondary/tasks/main.yml new file mode 100644 index 0000000..775a464 --- /dev/null +++ b/ansible/roles/mail-secondary/tasks/main.yml @@ -0,0 +1,13 @@ +--- +- name: Configure OpenSMTPD smtpd.conf + template: + src: templates/smtpd.conf + dest: /etc/mail/smtpd.conf + notify: + - reload smtpd + +- name: Enable and start OpenSMTPD service + service: + name: smtpd + enabled: yes + state: started diff --git a/ansible/roles/mail-secondary/templates/smtpd.conf b/ansible/roles/mail-secondary/templates/smtpd.conf new file mode 100644 index 0000000..f6ed5c3 --- /dev/null +++ b/ansible/roles/mail-secondary/templates/smtpd.conf @@ -0,0 +1,16 @@ +# {{ ansible_managed }} +pki {{ server_domain }} cert "/etc/ssl/{{ server_domain }}.fullchain.pem" +pki {{ server_domain }} key "/etc/ssl/private/{{ server_domain }}.key" + +listen on all tls pki {{ server_domain }} + +table aliases file:/etc/mail/aliases + +action "local" mbox alias +action "relay" relay host {{ primary_mail_server }} + +{% for domain in mail_receive_domains %} +match from any for domain {{ domain }} action "relay" +{% endfor %} +match from local for local action "local" +match from local for any action "relay" diff --git a/ansible/roles/prometheus-exporters/tasks/main.yml b/ansible/roles/prometheus-exporters/tasks/main.yml new file mode 100644 index 0000000..545d4fb --- /dev/null +++ b/ansible/roles/prometheus-exporters/tasks/main.yml @@ -0,0 +1,12 @@ +--- +- name: Install node-exporter + community.general.openbsd_pkg: + name: + - node_exporter + state: present + +- name: Enable and start node_exporter + service: + name: node_exporter + enabled: yes + state: started diff --git a/ansible/roles/ssl/defaults/main.yml b/ansible/roles/ssl/defaults/main.yml new file mode 100644 index 0000000..3052d9a --- /dev/null +++ b/ansible/roles/ssl/defaults/main.yml @@ -0,0 +1,2 @@ +domains: + - mx1.example.com diff --git a/ansible/roles/ssl/handlers/main.yml b/ansible/roles/ssl/handlers/main.yml new file mode 100644 index 0000000..3670374 --- /dev/null +++ b/ansible/roles/ssl/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: reload httpd + ansible.builtin.service: + name: httpd + state: reloaded diff --git a/ansible/roles/ssl/tasks/main.yml b/ansible/roles/ssl/tasks/main.yml new file mode 100644 index 0000000..34e1eb0 --- /dev/null +++ b/ansible/roles/ssl/tasks/main.yml @@ -0,0 +1,39 @@ +--- +- name: Create vhost directories + file: + path: "/var/www/vhosts/{{ item }}" + state: directory + owner: www + with_items: "{{ domains }}" + +- name: Install httpd.conf + template: + src: "templates/httpd.conf" + dest: "/etc/httpd.conf" + +- name: Enable and start httpd + service: + name: httpd + enabled: yes + state: started + +- name: Install acme-client.conf + template: + src: "templates/acme-client.conf" + dest: "/etc/acme-client.conf" + +- name: Initial acme-client run + command: "/usr/sbin/acme-client {{ item }}" + args: + creates: "/etc/ssl/{{ item }}.fullchain.pem" + with_items: "{{ domains }}" + notify: + - reload httpd + +- name: Renew certificates via root crontab + cron: + name: "acme-client renew {{ item }}" + minute: "0" + job: "sleep $((RANDOM \\% 2048)) && acme-client {{ item }} && rcctl reload httpd" + user: root + with_items: "{{ domains }}" diff --git a/ansible/templates/acme-client.conf b/ansible/roles/ssl/templates/acme-client.conf similarity index 82% rename from ansible/templates/acme-client.conf rename to ansible/roles/ssl/templates/acme-client.conf index a2f2329..1819702 100644 --- a/ansible/templates/acme-client.conf +++ b/ansible/roles/ssl/templates/acme-client.conf @@ -1,9 +1,10 @@ +# {{ ansible_managed }} authority letsencrypt { api url "https://acme-v02.api.letsencrypt.org/directory" account key "/etc/acme/letsencrypt-privkey.pem" } -{% for domain in lookup('vars', inventory_hostname + '_domains') %} +{% for domain in domains %} domain "{{ domain }}" { domain key "/etc/ssl/private/{{ domain }}.key" domain full chain certificate "/etc/ssl/{{ domain }}.fullchain.pem" diff --git a/ansible/templates/httpd.conf b/ansible/roles/ssl/templates/httpd.conf similarity index 89% rename from ansible/templates/httpd.conf rename to ansible/roles/ssl/templates/httpd.conf index 761fb4c..080ef13 100644 --- a/ansible/templates/httpd.conf +++ b/ansible/roles/ssl/templates/httpd.conf @@ -1,3 +1,4 @@ +# {{ ansible_managed }} server "{{ inventory_hostname }}" { listen on * port 80 location "/.well-known/acme-challenge/*" { @@ -9,7 +10,7 @@ server "{{ inventory_hostname }}" { } } -{% for vhost in lookup('vars', inventory_hostname + '_domains') %} +{% for vhost in domains %} server "{{ vhost }}" { listen on * tls port 443 tls { diff --git a/ansible/roles/vpn/defaults/main.yml b/ansible/roles/vpn/defaults/main.yml new file mode 100644 index 0000000..74914ca --- /dev/null +++ b/ansible/roles/vpn/defaults/main.yml @@ -0,0 +1,11 @@ +--- +wireguard: + private_key: + address: + port: 21841 + interface: wg0 + peers: + - name: Gateway + public_key: + endpoint: :21841 + allowed_ips: 10.2.0.1/32 diff --git a/ansible/roles/vpn/tasks/main.yml b/ansible/roles/vpn/tasks/main.yml new file mode 100644 index 0000000..c457b9e --- /dev/null +++ b/ansible/roles/vpn/tasks/main.yml @@ -0,0 +1,30 @@ +--- +- name: Install wireguard + community.general.openbsd_pkg: + name: + - wireguard-tools + state: present + +- name: Ensures /etc/wireguard dir exists + file: + path: /etc/wireguard + state: directory + +- name: Create wireguard config + template: + src: templates/wireguard.conf.j2 + dest: /etc/wireguard/{{ wireguard.interface }}.conf + owner: root + group: wheel + mode: "0600" + register: wg_config + +- name: Create wireguard interface + template: + src: "templates/wireguard.if.j2" + dest: "/etc/hostname.{{ wireguard.interface }}" + register: iface_config + +- name: Apply network configuration if changed + shell: sh /etc/netstart {{ wireguard.interface }} + when: wg_config.changed or iface_config.changed diff --git a/ansible/templates/wireguard.conf.j2 b/ansible/roles/vpn/templates/wireguard.conf.j2 similarity index 53% rename from ansible/templates/wireguard.conf.j2 rename to ansible/roles/vpn/templates/wireguard.conf.j2 index a51c0ca..9e3a401 100644 --- a/ansible/templates/wireguard.conf.j2 +++ b/ansible/roles/vpn/templates/wireguard.conf.j2 @@ -1,10 +1,9 @@ -{% set _wg = lookup('vars', inventory_hostname + '_wg') %} # {{ ansible_managed }} [Interface] -PrivateKey = {{ _wg.private_key }} -ListenPort = {{ _wg.port }} +PrivateKey = {{ wireguard.private_key }} +ListenPort = {{ wireguard.port }} -{% for peer in _wg.peers %} +{% for peer in wireguard.peers %} [Peer] # {{ peer.name }} PublicKey = {{ peer.public_key }} diff --git a/ansible/roles/vpn/templates/wireguard.if.j2 b/ansible/roles/vpn/templates/wireguard.if.j2 new file mode 100644 index 0000000..815df0f --- /dev/null +++ b/ansible/roles/vpn/templates/wireguard.if.j2 @@ -0,0 +1,5 @@ +# {{ ansible_managed }} +inet {{ wireguard.address }} 255.255.255.0 NONE +up + +!/usr/local/bin/wg setconf {{ wireguard.interface }} /etc/wireguard/{{ wireguard.interface }}.conf diff --git a/ansible/secondary-mail.yml b/ansible/secondary-mail.yml new file mode 100644 index 0000000..903d7bc --- /dev/null +++ b/ansible/secondary-mail.yml @@ -0,0 +1,13 @@ +- name: Secondary server configuration + hosts: + - mx2 + remote_user: root + vars_files: + - vars-{{ inventory_hostname }}.yml + roles: + - initial-setup + - firewall + - ssl + - mail-secondary + - vpn + - prometheus-exporters diff --git a/ansible/templates/secondary-smtpd.conf b/ansible/templates/secondary-smtpd.conf deleted file mode 100644 index 7a9c3c2..0000000 --- a/ansible/templates/secondary-smtpd.conf +++ /dev/null @@ -1,16 +0,0 @@ -{% set _mx_domain = lookup('vars', inventory_hostname + '_mail_domain') %} -pki {{ _mx_domain }} cert "/etc/ssl/{{ _mx_domain }}.fullchain.pem" -pki {{ _mx_domain }} key "/etc/ssl/private/{{ _mx_domain }}.key" - -listen on all tls pki {{ _mx_domain }} - -table aliases file:/etc/mail/aliases - -action "local" mbox alias -action "relay" relay host {{ mx1_mail_domain }} - -{% for domain in mail_domains %} -match from any for domain {{ domain }} action "relay" -{% endfor %} -match from local for local action "local" -match from local for any action "relay" diff --git a/ansible/templates/smtpd.conf b/ansible/templates/smtpd.conf deleted file mode 100644 index e1a4837..0000000 --- a/ansible/templates/smtpd.conf +++ /dev/null @@ -1,24 +0,0 @@ -pki {{ mx1_mail_domain }} cert "/etc/ssl/{{ mx1_mail_domain }}.fullchain.pem" -pki {{ mx1_mail_domain }} key "/etc/ssl/private/{{ mx1_mail_domain }}.key" - -table aliases file:/etc/mail/aliases -table users passwd:/etc/mail/accounts -table virtuals file:/etc/mail/virtuals - -filter dkimsign_rsa proc-exec "filter-dkimsign -d {{ mx1_mail_domain }} -s selector1 \ - -k /etc/mail/dkim/{{ mx1_mail_domain }}.key" user _dkimsign group _dkimsign - -listen on socket filter dkimsign_rsa -listen on all tls pki {{ mx1_mail_domain }} -listen on all port submission tls-require pki {{ mx1_mail_domain }} auth filter dkimsign_rsa -listen on all port smtps tls-require pki {{ mx1_mail_domain }} auth filter dkimsign_rsa - -action "local_mail" lmtp "/var/dovecot/lmtp" rcpt-to virtual -action "outbound" relay - -{% for domain in mail_domains %} -match from any for domain {{ domain }} action "local_mail" -{% endfor %} -match from local for local action "local_mail" -match from local for any action "outbound" -match auth from any for any action "outbound" diff --git a/ansible/templates/wireguard.if.j2 b/ansible/templates/wireguard.if.j2 deleted file mode 100644 index cb5900d..0000000 --- a/ansible/templates/wireguard.if.j2 +++ /dev/null @@ -1,6 +0,0 @@ -{% set _wg = lookup('vars', inventory_hostname + '_wg') %} -# {{ ansible_managed }} -inet {{ _wg.address }} 255.255.255.0 NONE -up - -!/usr/local/bin/wg setconf {{ _wg.interface }} /etc/wireguard/{{ _wg.interface }}.conf