diff options
| author | Roman Ilin <me@romanilin.is> | 2026-06-15 12:59:09 +0300 |
|---|---|---|
| committer | Roman Ilin <me@romanilin.is> | 2026-06-15 22:04:41 +0300 |
| commit | 5e4bf1268c266e63d0e92e845ad910a2103b86ff (patch) | |
| tree | 532c01a9658a05048ef1ba76d4f30fca84005643 | |
| download | infrastructure-main.tar.gz | |
| -rw-r--r-- | README.md | 30 | ||||
| -rw-r--r-- | ansible.cfg | 6 | ||||
| -rw-r--r-- | group_vars/all/vars.yaml | 13 | ||||
| -rw-r--r-- | group_vars/all/vault.yaml | 12 | ||||
| -rw-r--r-- | inventory.yaml | 18 | ||||
| -rw-r--r-- | roles/container_base/handlers/main.yaml | 4 | ||||
| -rw-r--r-- | roles/container_base/tasks/main.yaml | 27 | ||||
| -rw-r--r-- | roles/git/handlers/main.yaml | 7 | ||||
| -rw-r--r-- | roles/git/tasks/main.yaml | 22 | ||||
| -rw-r--r-- | roles/git/templates/git_nginx.conf.j2 | 16 | ||||
| -rw-r--r-- | roles/host/files/60-machinectl-fast-user-auth.rules | 6 | ||||
| -rw-r--r-- | roles/host/handlers/main.yaml | 25 | ||||
| -rw-r--r-- | roles/host/tasks/main.yaml | 195 | ||||
| -rw-r--r-- | roles/host/templates/nginx-reload.sh.j2 | 3 | ||||
| -rw-r--r-- | roles/host/templates/nginx.conf.j2 | 67 | ||||
| -rw-r--r-- | roles/host/templates/nspawn.j2 | 10 | ||||
| -rw-r--r-- | roles/mail/handlers/main.yaml | 15 | ||||
| -rw-r--r-- | roles/mail/tasks/main.yaml | 61 | ||||
| -rw-r--r-- | roles/mail/templates/dovecot.conf.j2 | 22 | ||||
| -rw-r--r-- | roles/mail/templates/opendkim.conf.j2 | 15 | ||||
| -rw-r--r-- | roles/mail/templates/postfix_main.cf.j2 | 27 | ||||
| -rw-r--r-- | roles/mail/templates/postfix_master.cf.j2 | 31 | ||||
| -rw-r--r-- | site.yaml | 23 |
23 files changed, 655 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..96a3680 --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +# Infrastructure + +## Prerequisites + +Before running the playbook for the first time, ensure `certbot` is installed and you have generated a wildcard Let's Encrypt certificate for the main domain on the host machine. (If the certificate doesn't exist yet, Ansible will generate a temporary self-signed certificate so HAProxy can start). + +```bash +sudo dnf install -y epel-release certbot +# Run certbot dns challenge to get wildcard certs +sudo certbot certonly --manual --preferred-challenges dns -d "*.romanilin.is" -d "romanilin.is" +``` + +## Deployment + +```bash +sudo dnf install -y ansible-core git +ansible-galaxy collection install ansible.posix community.general +git clone <your-git-repo> +cd infrastructure + +# Run the playbook +ansible-playbook site.yaml --ask-vault-pass --ask-become-pass +``` + +## Reading Secrets + +```bash +# Example: read the DKIM key from the mail container +sudo machinectl shell mail /bin/cat /etc/opendkim/keys/romanilin.is/default.txt +``` diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..95ac857 --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,6 @@ +[defaults] +inventory = inventory.yaml +stdout_callback = default +callback_result_format = yaml +interpreter_python = auto_silent +host_key_checking = False diff --git a/group_vars/all/vars.yaml b/group_vars/all/vars.yaml new file mode 100644 index 0000000..b851351 --- /dev/null +++ b/group_vars/all/vars.yaml @@ -0,0 +1,13 @@ +bridge_ip: 10.0.0.1 + +containers: + mail: + ip: 10.0.0.2 + forward_ports: + - "25" + - "465" + - "993" + git: + ip: 10.0.0.3 + forward_ports: [] + web_subdomain: "git" diff --git a/group_vars/all/vault.yaml b/group_vars/all/vault.yaml new file mode 100644 index 0000000..f06bd5d --- /dev/null +++ b/group_vars/all/vault.yaml @@ -0,0 +1,12 @@ +$ANSIBLE_VAULT;1.1;AES256 +32643761326637343234326437343234386366663463313836313836323032636433366264626535 +3531383065383133363035303336396532616632303332380a643437376661633937313561633138 +32336333646365633263656433313032386461663534323231323961396238666230653963346262 +6630643965623639310a313431616133303533643162666636666338656566633732396663383938 +63643732653935386462323465363863636565376661383032643137646531323534343261353138 +62656339663363333032393365373962343635353038643738636436333332316139313265303266 +37373433353833376262643964656630356638666539306166663962333433316466643565616539 +35623763616365393831303661613363313933653163643364636537346136616462303263346436 +38356564386535663531663630316535333066346536316434653365636133396463366366646366 +30626539623163396534613930346562383035386462316330316163333932333462363633313731 +343062373761666361626533643035616166 diff --git a/inventory.yaml b/inventory.yaml new file mode 100644 index 0000000..f1bf383 --- /dev/null +++ b/inventory.yaml @@ -0,0 +1,18 @@ +all: + children: + metal_servers: + hosts: + host: + ansible_connection: local + containers: + vars: + ansible_connection: local + ansible_become_method: community.general.machinectl + ansible_become_user: root + children: + mail_servers: + hosts: + mail: + git_servers: + hosts: + git: diff --git a/roles/container_base/handlers/main.yaml b/roles/container_base/handlers/main.yaml new file mode 100644 index 0000000..93df41f --- /dev/null +++ b/roles/container_base/handlers/main.yaml @@ -0,0 +1,4 @@ +- name: Restart Container systemd-networkd + ansible.builtin.systemd: + name: systemd-networkd + state: restarted diff --git a/roles/container_base/tasks/main.yaml b/roles/container_base/tasks/main.yaml new file mode 100644 index 0000000..275a24a --- /dev/null +++ b/roles/container_base/tasks/main.yaml @@ -0,0 +1,27 @@ +- name: Ensure systemd-networkd is running in container + ansible.builtin.systemd: + name: systemd-networkd + state: started + enabled: yes + +- name: Set static IP for container dynamically + ansible.builtin.copy: + dest: /etc/systemd/network/80-container-host0.network + content: | + [Match] + Name=host0 + [Network] + Address={{ containers[inventory_hostname].ip }}/24 + Gateway={{ bridge_ip }} + DNS=1.1.1.1 8.8.8.8 + notify: Restart Container systemd-networkd + +- name: Apply container network changes immediately + ansible.builtin.meta: flush_handlers + +- name: Install Packages + ansible.builtin.dnf: + name: + - epel-release + - python3-passlib + state: present diff --git a/roles/git/handlers/main.yaml b/roles/git/handlers/main.yaml new file mode 100644 index 0000000..3503da0 --- /dev/null +++ b/roles/git/handlers/main.yaml @@ -0,0 +1,7 @@ +- name: Restart Git Services + ansible.builtin.systemd: + name: "{{ item }}" + state: restarted + loop: + - nginx + - fcgiwrap@nginx.socket diff --git a/roles/git/tasks/main.yaml b/roles/git/tasks/main.yaml new file mode 100644 index 0000000..b80989e --- /dev/null +++ b/roles/git/tasks/main.yaml @@ -0,0 +1,22 @@ +- name: Install Git, Nginx, and Fcgiwrap + ansible.builtin.dnf: + name: + - cgit + - nginx + - fcgiwrap + state: present + +- name: Deploy Git Nginx configuration + ansible.builtin.template: + src: git_nginx.conf.j2 + dest: /etc/nginx/conf.d/default.conf + notify: Restart Git Services + +- name: Ensure Git Web Services are Enabled and Running + ansible.builtin.systemd: + name: "{{ item }}" + state: started + enabled: yes + loop: + - nginx + - fcgiwrap@nginx.socket diff --git a/roles/git/templates/git_nginx.conf.j2 b/roles/git/templates/git_nginx.conf.j2 new file mode 100644 index 0000000..cd37f25 --- /dev/null +++ b/roles/git/templates/git_nginx.conf.j2 @@ -0,0 +1,16 @@ +server { + listen 80; + server_name _; + + root /usr/share/cgit; + try_files $uri @cgit; + + location @cgit { + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME /var/www/cgi-bin/cgit; + fastcgi_param PATH_INFO $uri; + fastcgi_param QUERY_STRING $args; + fastcgi_param HTTP_HOST $server_name; + fastcgi_pass unix:/run/fcgiwrap-nginx.sock; + } +} diff --git a/roles/host/files/60-machinectl-fast-user-auth.rules b/roles/host/files/60-machinectl-fast-user-auth.rules new file mode 100644 index 0000000..eee39ea --- /dev/null +++ b/roles/host/files/60-machinectl-fast-user-auth.rules @@ -0,0 +1,6 @@ +polkit.addRule(function(action, subject) { + if(action.id == "org.freedesktop.machine1.host-shell" && + subject.isInGroup("wheel")) { + return polkit.Result.YES; + } +}); diff --git a/roles/host/handlers/main.yaml b/roles/host/handlers/main.yaml new file mode 100644 index 0000000..e643580 --- /dev/null +++ b/roles/host/handlers/main.yaml @@ -0,0 +1,25 @@ +- name: Reload Firewalld + ansible.builtin.systemd: + name: firewalld + state: reloaded + +- name: Restart Host systemd-networkd + ansible.builtin.systemd: + name: systemd-networkd + state: restarted + +- name: Restart Containers + ansible.builtin.systemd: + name: "systemd-nspawn@{{ item.key }}" + state: restarted + loop: "{{ containers | dict2items }}" + +- name: Restart Nginx + ansible.builtin.systemd: + name: nginx + state: restarted + +- name: Restart Polkit + ansible.builtin.systemd: + name: polkit + state: restarted diff --git a/roles/host/tasks/main.yaml b/roles/host/tasks/main.yaml new file mode 100644 index 0000000..007d44d --- /dev/null +++ b/roles/host/tasks/main.yaml @@ -0,0 +1,195 @@ +- name: Install firewalld and systemd-networkd packages + ansible.builtin.dnf: + name: + - firewalld + - systemd-container + - systemd-networkd + - openssl + - python3-libsemanage + - policycoreutils + state: present + +- name: Configure Polkit to allow machinectl fast user auth + ansible.builtin.copy: + src: 60-machinectl-fast-user-auth.rules + dest: /etc/polkit-1/rules.d/60-machinectl-fast-user-auth.rules + mode: "0644" + notify: Restart Polkit + +- name: Ensure firewalld and systemd-networkd are running + ansible.builtin.systemd: + name: "{{ item }}" + state: started + enabled: yes + loop: + - firewalld + - systemd-networkd + +- name: Enable masquerading for container internet access + ansible.posix.firewalld: + masquerade: yes + state: enabled + permanent: yes + zone: public + notify: Reload Firewalld + +- name: Open HTTP and HTTPS (TCP) for Nginx + ansible.posix.firewalld: + service: "{{ item }}" + state: enabled + permanent: yes + zone: public + loop: + - http + - https + notify: Reload Firewalld + +- name: Open HTTPS (UDP) for HTTP/3 QUIC support + ansible.posix.firewalld: + port: 443/udp + state: enabled + permanent: yes + zone: public + notify: Reload Firewalld + +- name: Configure Host Firewall Port Forwarding dynamically + ansible.posix.firewalld: + port_forward: + - port: "{{ item.1 }}" + proto: tcp + toaddr: "{{ item.0.value.ip }}" + toport: "{{ item.1 }}" + permanent: yes + state: enabled + loop: "{{ containers | dict2items | subelements('value.forward_ports', skip_missing=True) }}" + notify: Reload Firewalld + +- name: Create bridge netdev on host + ansible.builtin.copy: + dest: /etc/systemd/network/10-nspawn-br0.netdev + content: | + [NetDev] + Name=nspawn-br0 + Kind=bridge + notify: Restart Host systemd-networkd + +- name: Assign IP to the host bridge + ansible.builtin.copy: + dest: /etc/systemd/network/10-nspawn-br0.network + content: | + [Match] + Name=nspawn-br0 + [Network] + Address={{ bridge_ip }}/24 + notify: Restart Host systemd-networkd + +- name: Apply networking changes immediately + ansible.builtin.meta: flush_handlers + +- name: Ensure containers are bootstrapped (AlmaLinux 10) + ansible.builtin.dnf: + installroot: "/var/lib/machines/{{ item.key }}" + releasever: "10" + name: + - almalinux-release + - systemd + - dbus + - systemd-networkd + - passwd + - dnf + - iproute + state: present + loop: "{{ containers | dict2items }}" + +- name: Initialize machine-id for containers + ansible.builtin.command: systemd-machine-id-setup --root=/var/lib/machines/{{ item.key }} + args: + creates: "/var/lib/machines/{{ item.key }}/etc/machine-id" + loop: "{{ containers | dict2items }}" + +- name: Fix SELinux contexts in container rootfs + ansible.builtin.command: restorecon -R /var/lib/machines/{{ item.key }} + register: restorecon_result + changed_when: "'Relabeled' in restorecon_result.stdout" + loop: "{{ containers | dict2items }}" + +- name: Ensure systemd-nspawn directory exists + ansible.builtin.file: + path: /etc/systemd/nspawn + state: directory + mode: "0755" + +- name: Ensure Let's Encrypt mock directories exist + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: '0755' + loop: + - "/etc/letsencrypt/archive/{{ vault_public_domain }}" + - "/etc/letsencrypt/live/{{ vault_public_domain }}" + +- name: Check if Let's Encrypt certificate exists + ansible.builtin.stat: + path: "/etc/letsencrypt/live/{{ vault_public_domain }}/fullchain.pem" + register: le_cert + +- name: Generate self-signed fallback cert in Let's Encrypt paths if missing + ansible.builtin.command: > + openssl req -x509 -nodes -days 365 + -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 + -keyout /etc/letsencrypt/live/{{ vault_public_domain }}/privkey.pem + -out /etc/letsencrypt/live/{{ vault_public_domain }}/fullchain.pem + -subj "/CN={{ vault_public_domain }}" + args: + creates: "/etc/letsencrypt/live/{{ vault_public_domain }}/fullchain.pem" + when: not le_cert.stat.exists + +- name: Create systemd-nspawn configuration + ansible.builtin.template: + src: nspawn.j2 + dest: "/etc/systemd/nspawn/{{ item.key }}.nspawn" + loop: "{{ containers | dict2items }}" + notify: Restart Containers + +- name: Enable and Start Containers + ansible.builtin.systemd: + name: "systemd-nspawn@{{ item.key }}" + state: started + enabled: yes + loop: "{{ containers | dict2items }}" + +- name: Install Nginx + ansible.builtin.dnf: + name: nginx + state: present + +- name: Allow Nginx to connect to upstream servers (SELinux) + ansible.posix.seboolean: + name: httpd_can_network_connect + state: yes + persistent: yes + +- name: Configure Nginx dynamically + ansible.builtin.template: + src: nginx.conf.j2 + dest: /etc/nginx/nginx.conf + validate: nginx -t -c %s + notify: Restart Nginx + +- name: Ensure Nginx is enabled and running + ansible.builtin.systemd: + name: nginx + state: started + enabled: yes + +- name: Ensure Let's Encrypt renewal hook directory exists + ansible.builtin.file: + path: /etc/letsencrypt/renewal-hooks/post + state: directory + mode: '0755' + +- name: Deploy Let's Encrypt post-renewal hook for Nginx + ansible.builtin.template: + src: nginx-reload.sh.j2 + dest: /etc/letsencrypt/renewal-hooks/post/nginx-reload.sh + mode: '0755' diff --git a/roles/host/templates/nginx-reload.sh.j2 b/roles/host/templates/nginx-reload.sh.j2 new file mode 100644 index 0000000..f248776 --- /dev/null +++ b/roles/host/templates/nginx-reload.sh.j2 @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +systemctl reload nginx diff --git a/roles/host/templates/nginx.conf.j2 b/roles/host/templates/nginx.conf.j2 new file mode 100644 index 0000000..7360cae --- /dev/null +++ b/roles/host/templates/nginx.conf.j2 @@ -0,0 +1,67 @@ +user nginx; +worker_processes auto; +worker_rlimit_nofile 8192; +error_log /var/log/nginx/error.log notice; +pid /run/nginx.pid; + +events { + worker_connections 4096; +} + +http { + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + tcp_nopush on; + keepalive_timeout 65; + types_hash_max_size 4096; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Modern SSL configuration + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers on; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 1d; + ssl_session_tickets off; + + # Redirect all HTTP traffic to HTTPS + server { + listen 80 default_server; + server_name _; + return 301 https://$host$request_uri; + } + +{% for name, config in containers.items() %} +{% if config.web_subdomain is defined %} + server { + listen 443 ssl; # TCP for HTTP/1.1 & HTTP/2 + listen 443 quic; # UDP for HTTP/3 QUIC + http2 on; # Enable HTTP/2 over TCP + + server_name {{ config.web_subdomain }}.{{ vault_public_domain }}; + + # Nginx reads them natively, no combining needed + ssl_certificate /etc/letsencrypt/live/{{ vault_public_domain }}/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/{{ vault_public_domain }}/privkey.pem; + + # Advertise HTTP/3 availability to browsers + add_header Alt-Svc 'h3=":443"; ma=2592000' always; + + location / { + proxy_pass http://{{ config.ip }}:80; + proxy_set_header Host $host; + 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 https; + } + } +{% endif %} +{% endfor %} +} diff --git a/roles/host/templates/nspawn.j2 b/roles/host/templates/nspawn.j2 new file mode 100644 index 0000000..15faf53 --- /dev/null +++ b/roles/host/templates/nspawn.j2 @@ -0,0 +1,10 @@ +[Exec] +Boot=yes +Hostname={{ item.key }}.{{ vault_public_domain }} + +[Files] +BindReadOnly=/etc/letsencrypt/live/{{ vault_public_domain }}:/etc/letsencrypt/live/{{ vault_public_domain }} +BindReadOnly=/etc/letsencrypt/archive/{{ vault_public_domain }}:/etc/letsencrypt/archive/{{ vault_public_domain }} + +[Network] +Bridge=nspawn-br0 diff --git a/roles/mail/handlers/main.yaml b/roles/mail/handlers/main.yaml new file mode 100644 index 0000000..2d6ec58 --- /dev/null +++ b/roles/mail/handlers/main.yaml @@ -0,0 +1,15 @@ +- name: Fix DKIM permissions + ansible.builtin.file: + path: /etc/opendkim/keys/ + owner: opendkim + group: opendkim + recurse: yes + +- name: Restart Mail Services + ansible.builtin.systemd: + name: "{{ item }}" + state: restarted + loop: + - postfix + - dovecot + - opendkim diff --git a/roles/mail/tasks/main.yaml b/roles/mail/tasks/main.yaml new file mode 100644 index 0000000..66b4215 --- /dev/null +++ b/roles/mail/tasks/main.yaml @@ -0,0 +1,61 @@ +- name: Install Mail Packages + ansible.builtin.dnf: + name: + - postfix + - dovecot + - opendkim + - opendkim-tools + state: present + +- name: Ensure OpenDKIM keys directory exists + ansible.builtin.file: + path: "/etc/opendkim/keys/{{ vault_public_domain }}" + state: directory + owner: opendkim + group: opendkim + mode: "0750" + +- name: Generate DKIM Key + ansible.builtin.command: + cmd: "opendkim-genkey -a ed25519 -s default -d {{ vault_public_domain }} -D /etc/opendkim/keys/{{ vault_public_domain }}/" + creates: "/etc/opendkim/keys/{{ vault_public_domain }}/default.private" + notify: Fix DKIM permissions + +- name: Configure OpenDKIM mappings + ansible.builtin.copy: + dest: "{{ item.path }}" + content: "{{ item.content }}" + mode: "0644" + loop: + - { path: /etc/opendkim/KeyTable, content: "default._domainkey.{{ vault_public_domain }} {{ vault_public_domain }}:default:/etc/opendkim/keys/{{ vault_public_domain }}/default.private\n" } + - { path: /etc/opendkim/SigningTable, content: "*@{{ vault_public_domain }} default._domainkey.{{ vault_public_domain }}\n" } + - { path: /etc/opendkim/TrustedHosts, content: "127.0.0.1\nlocalhost\n10.0.0.0/24\n" } + notify: Restart Mail Services + +- name: Deploy Configurations + ansible.builtin.template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + mode: "0644" + loop: + - { src: postfix_main.cf.j2, dest: /etc/postfix/main.cf } + - { src: postfix_master.cf.j2, dest: /etc/postfix/master.cf } + - { src: dovecot.conf.j2, dest: /etc/dovecot/dovecot.conf } + - { src: opendkim.conf.j2, dest: /etc/opendkim.conf } + notify: Restart Mail Services + +- name: Add Mail User + ansible.builtin.user: + name: me + shell: /sbin/nologin + password: "{{ vault_mail_user_password | password_hash('sha512') }}" + +- name: Ensure Services are Enabled and Running + ansible.builtin.systemd: + name: "{{ item }}" + state: started + enabled: yes + loop: + - postfix + - dovecot + - opendkim diff --git a/roles/mail/templates/dovecot.conf.j2 b/roles/mail/templates/dovecot.conf.j2 new file mode 100644 index 0000000..e733bae --- /dev/null +++ b/roles/mail/templates/dovecot.conf.j2 @@ -0,0 +1,22 @@ +protocols = imap +listen = * +mail_location = maildir:~/Maildir +auth_mechanisms = plain login +ssl = required +ssl_cert = </etc/letsencrypt/live/{{ vault_public_domain }}/fullchain.pem +ssl_key = </etc/letsencrypt/live/{{ vault_public_domain }}/privkey.pem + +passdb { + driver = pam +} +userdb { + driver = passwd +} + +service auth { + unix_listener /var/spool/postfix/private/auth { + group = postfix + mode = 0660 + user = postfix + } +} diff --git a/roles/mail/templates/opendkim.conf.j2 b/roles/mail/templates/opendkim.conf.j2 new file mode 100644 index 0000000..b29fcb3 --- /dev/null +++ b/roles/mail/templates/opendkim.conf.j2 @@ -0,0 +1,15 @@ +PidFile /run/opendkim/opendkim.pid +Mode sv +Syslog yes +SyslogSuccess yes +LogWhy yes +UserID opendkim:opendkim +Socket inet:8891@localhost +Umask 002 +Canonicalization relaxed/relaxed +Selector default +MinimumKeyBits 1024 +KeyTable /etc/opendkim/KeyTable +SigningTable refile:/etc/opendkim/SigningTable +ExternalIgnoreList refile:/etc/opendkim/TrustedHosts +InternalHosts refile:/etc/opendkim/TrustedHosts diff --git a/roles/mail/templates/postfix_main.cf.j2 b/roles/mail/templates/postfix_main.cf.j2 new file mode 100644 index 0000000..df20610 --- /dev/null +++ b/roles/mail/templates/postfix_main.cf.j2 @@ -0,0 +1,27 @@ +myhostname = mail.{{ vault_public_domain }} +mydomain = {{ vault_public_domain }} +myorigin = $mydomain +mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain +inet_interfaces = all +inet_protocols = ipv4 +mynetworks = 127.0.0.0/8, 10.0.0.0/24 +home_mailbox = Maildir/ +message_size_limit = 26214400 +alias_maps = lmdb:/etc/aliases +alias_database = lmdb:/etc/aliases + +smtpd_tls_cert_file = /etc/letsencrypt/live/{{ vault_public_domain }}/fullchain.pem +smtpd_tls_key_file = /etc/letsencrypt/live/{{ vault_public_domain }}/privkey.pem +smtpd_tls_security_level = may +smtp_tls_security_level = may +smtpd_tls_protocols = >=TLSv1.2 +smtp_tls_protocols = >=TLSv1.2 + +smtpd_sasl_type = dovecot +smtpd_sasl_path = private/auth +smtpd_sasl_auth_enable = yes +smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination + +smtpd_milters = inet:localhost:8891 +non_smtpd_milters = inet:localhost:8891 +milter_default_action = accept diff --git a/roles/mail/templates/postfix_master.cf.j2 b/roles/mail/templates/postfix_master.cf.j2 new file mode 100644 index 0000000..c2648ca --- /dev/null +++ b/roles/mail/templates/postfix_master.cf.j2 @@ -0,0 +1,31 @@ +smtp inet n - y - - smtpd +pickup unix n - y 60 1 pickup +cleanup unix n - y - 0 cleanup +qmgr unix n - n 300 1 qmgr +tlsmgr unix - - y 1000? 1 tlsmgr +rewrite unix - - y - - trivial-rewrite +bounce unix - - y - 0 bounce +defer unix - - y - 0 bounce +trace unix - - y - 0 bounce +verify unix - - y - 1 verify +flush unix n - y 1000? 0 flush +proxymap unix - - n - - proxymap +proxywrite unix - - n - 1 proxymap +smtp unix - - y - - smtp +relay unix - - y - - smtp +showq unix n - y - - showq +error unix - - y - - error +retry unix - - y - - error +discard unix - - y - - discard +local unix - n n - - local +virtual unix - n n - - virtual +lmtp unix - - y - - lmtp +anvil unix - - y - 1 anvil +scache unix - - y - 1 scache +smtps inet n - y - - smtpd + -o syslog_name=postfix/smtps + -o smtpd_tls_wrappermode=yes + -o smtpd_sasl_auth_enable=yes + -o smtpd_reject_unlisted_recipient=no + -o smtpd_client_restrictions=permit_sasl_authenticated,reject + -o milter_macro_daemon_name=ORIGINATING diff --git a/site.yaml b/site.yaml new file mode 100644 index 0000000..99ff13f --- /dev/null +++ b/site.yaml @@ -0,0 +1,23 @@ +- name: 1. Configure Bare Metal Host + hosts: metal_servers + become: yes + roles: + - host + +- name: 2. Configure Base System in all Containers + hosts: containers + become: yes + roles: + - container_base + +- name: 3. Configure Mail Container + hosts: mail_servers + become: yes + roles: + - mail + +- name: 4. Configure Git Container + hosts: git_servers + become: yes + roles: + - git |