commit 0abcf3f1ccd9c5e67d443302daf37a3e31444b85 Author: Roxedus Date: Mon Jun 13 22:09:07 2022 +0200 Init push diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9e3b28b --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.key +ansible/.vault_pass +ansible/ansible.cfg +ansible/vars/vault.yml +playground +terraform/.terraform +terraform/*.tfvars +terraform/terraform.tfstate* \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..614b3f1 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "oderwat.indent-rainbow", + "redhat.vscode-yaml", + "hashicorp.terraform", + "samuelcolvin.jinjahtml" + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6fe3110 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "files.associations": { + "**wireguard**.conf": "jinja-properties", + "**docker-compose**.j2": "jinja-yaml", + }, + "yaml.schemas": { + "https://json.schemastore.org/ansible-role-2.9": ["**/tasks/**.yml"], + "https://json.schemastore.org/ansible-playbook": ["run.yml"], + "https://json.schemastore.org/github-workflow": [".github/worflows/**.yml"], + }, +} diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml new file mode 100755 index 0000000..9f94e9f --- /dev/null +++ b/ansible/group_vars/all.yml @@ -0,0 +1,38 @@ +ansible_become_password: "{{ secret_sudo }}" + +ntp_timezone: "Europe/Oslo" + +users: + - username: roxedus + groupname: roxedus + home: yes + uid: "1000" + gid: "1000" + github: Roxedus + password: "{{ secret_rox_pass }}" + +package_list: + - bash-completion + - ca-certificates + - curl + - git + - gnupg2 + - htop + - jq + - ncdu + - net-tools + - python3 + - python3-apt + - python3-pip + - software-properties-common + - tmux + - wget + +security_ssh_password_authentication: "no" +security_ssh_permit_root_login: "no" +security_ssh_port: "{{ secret_ssh_port }}" +security_ssh_usedns: "no" +security_autoupdate_enabled: true +security_fail2ban_enabled: true +security_sudoers_passwordless: + - "{{ users.0.username }}" diff --git a/ansible/group_vars/docker.yml b/ansible/group_vars/docker.yml new file mode 100755 index 0000000..8248cb0 --- /dev/null +++ b/ansible/group_vars/docker.yml @@ -0,0 +1,3 @@ +docker_install_compose: false +docker_users: + - "{{ users.0.username }}" diff --git a/ansible/group_vars/simple_login.yml b/ansible/group_vars/simple_login.yml new file mode 100644 index 0000000..c67527d --- /dev/null +++ b/ansible/group_vars/simple_login.yml @@ -0,0 +1,5 @@ +sl_domain: domain.com +mail_domain: "mail.{{ sl_domain }}" + +sl_alt_domains: + - mail.other_domain.com diff --git a/ansible/hosts b/ansible/hosts new file mode 100755 index 0000000..bb6ddbd --- /dev/null +++ b/ansible/hosts @@ -0,0 +1,5 @@ +[docker] +mail + +[simple_login] +mail set_hostname="{{ mail_domain }}" diff --git a/ansible/requirements.txt b/ansible/requirements.txt new file mode 100644 index 0000000..fc4d3cc --- /dev/null +++ b/ansible/requirements.txt @@ -0,0 +1,5 @@ +wheel +ansible==4.1.0 +ansible-base==2.10.10 +jmespath +ansible-lint[yamllint] \ No newline at end of file diff --git a/ansible/requirements.yml b/ansible/requirements.yml new file mode 100755 index 0000000..5a22e6e --- /dev/null +++ b/ansible/requirements.yml @@ -0,0 +1,7 @@ +--- +roles: + - name: geerlingguy.docker + - name: geerlingguy.ntp + - name: geerlingguy.pip + - name: geerlingguy.postfix + - name: geerlingguy.security diff --git a/ansible/roles/authentik/defaults/main.yml b/ansible/roles/authentik/defaults/main.yml new file mode 100644 index 0000000..0b95f99 --- /dev/null +++ b/ansible/roles/authentik/defaults/main.yml @@ -0,0 +1 @@ +AUTHENTIK_TAG: 2022.4.1 diff --git a/ansible/roles/authentik/tasks/main.yml b/ansible/roles/authentik/tasks/main.yml new file mode 100644 index 0000000..3ef157e --- /dev/null +++ b/ansible/roles/authentik/tasks/main.yml @@ -0,0 +1,24 @@ +- name: Create Authentik appdata directory + ansible.builtin.file: + path: "/opt/{{ item.name }}" + state: directory + owner: "{{ users.0.username }}" + group: "{{ users.0.groupname }}" + mode: "{{ item.mode | default('0755')}}" + loop: + - name: appdata + - name: appdata/authentik + - name: appdata/authentik/postgresql + mode: "0700" + - name: appdata/authentik/backups + - name: appdata/authentik/certs + - name: appdata/authentik/media + - name: appdata/authentik/templates + +- name: Seed compose + ansible.builtin.template: + src: "docker-compose.yml.j2" + dest: "/opt/appdata/authentik/docker-compose.yml" + owner: "{{ users.0.username }}" + group: "{{ users.0.groupname }}" + mode: 0644 diff --git a/ansible/roles/authentik/templates/docker-compose.yml.j2 b/ansible/roles/authentik/templates/docker-compose.yml.j2 new file mode 100644 index 0000000..a5a45dd --- /dev/null +++ b/ansible/roles/authentik/templates/docker-compose.yml.j2 @@ -0,0 +1,80 @@ +version: '3.2' + +services: + postgresql: + image: postgres:12-alpine + container_name: auth_postgres + user: "{{ users.0.uid }}:{{ users.0.gid }}" + volumes: + - /etc/passwd:/etc/passwd:ro + - /opt/appdata/authentik/postgresql:/var/lib/postgresql/data + environment: + - POSTGRES_PASSWORD={{ secret_authentik.postgres.password }} + - POSTGRES_USER={{ secret_authentik.postgres.user }} + - POSTGRES_DB={{ secret_authentik.postgres.database }} + networks: + - auth + + redis: + image: redis:alpine + restart: unless-stopped + networks: + - auth + + server: + image: ghcr.io/goauthentik/server:{{ AUTHENTIK_TAG }} + restart: unless-stopped + command: server + user: "{{ users.0.uid }}:{{ users.0.gid }}" + environment: + AUTHENTIK_REDIS__HOST: redis + AUTHENTIK_POSTGRESQL__HOST: postgresql + AUTHENTIK_POSTGRESQL__USER: {{ secret_authentik.postgres.user }} + AUTHENTIK_POSTGRESQL__NAME: {{ secret_authentik.postgres.database }} + AUTHENTIK_POSTGRESQL__PASSWORD: {{ secret_authentik.postgres.password }} + AUTHENTIK_SECRET_KEY: {{ secret_authentik.secret_key }} + # AUTHENTIK_ERROR_REPORTING__ENABLED: "true" + # WORKERS: 2 + volumes: + - /opt/appdata/authentik/media:/media + - /opt/appdata/authentik/custom-templates:/templates + - /opt/appdata/swag/config/geoip2db:/geoip:ro + networks: + - default + - auth + labels: + - swag=enable + - "swag_url=sso.{{ sl_domain }}" + - swag_port=9443 + - swag_proto=https + + worker: + image: ghcr.io/goauthentik/server:{{ AUTHENTIK_TAG }} + restart: unless-stopped + command: worker + environment: + AUTHENTIK_REDIS__HOST: redis + AUTHENTIK_POSTGRESQL__HOST: postgresql + AUTHENTIK_POSTGRESQL__USER: {{ secret_authentik.postgres.user }} + AUTHENTIK_POSTGRESQL__NAME: {{ secret_authentik.postgres.database }} + AUTHENTIK_POSTGRESQL__PASSWORD: {{ secret_authentik.postgres.password }} + AUTHENTIK_SECRET_KEY: {{ secret_authentik.secret_key }} + # AUTHENTIK_ERROR_REPORTING__ENABLED: "true" + user: "{{ users.0.uid }}:{{ users.0.gid }}" + volumes: + - /opt/appdata/authentik/backups:/backups + - /opt/appdata/authentik/certs:/certs + - /opt/appdata/authentik/media:/media + - /opt/appdata/authentik/custom-templates:/templates + - /opt/appdata/swag/config/geoip2db:/geoip:ro + networks: + - auth + +networks: + default: + external: true + name: proxynet + + auth: + internal: true + name: auth diff --git a/ansible/roles/simple-login/defaults/main.yml b/ansible/roles/simple-login/defaults/main.yml new file mode 100644 index 0000000..558432f --- /dev/null +++ b/ansible/roles/simple-login/defaults/main.yml @@ -0,0 +1,12 @@ +mail_domain: mail.domain.com + +sl_prem_domains: other.domain.com + +sl_version: v4.2.2 + +sl_postgres_version: 12 + +postgres: + user: user + password: password + dbname: dbname diff --git a/ansible/roles/simple-login/tasks/main.yml b/ansible/roles/simple-login/tasks/main.yml new file mode 100644 index 0000000..c69f0db --- /dev/null +++ b/ansible/roles/simple-login/tasks/main.yml @@ -0,0 +1,88 @@ +- name: Install packages for simple-login + ansible.builtin.apt: + update_cache: yes + pkg: + - dnsutils + - postfix + - postfix-pgsql + - postgresql-client-common + - "postgresql-client-{{ sl_postgres_version }}" + +- name: Create sl dockernet + community.docker.docker_network: + name: sl_net + #enable_ipv6: True + +- name: Get sl_net info + community.docker.docker_network_info: + name: sl_net + register: sl_net + +- include_tasks: postfix.yml + +- name: Create and chown simple appdata directories + ansible.builtin.file: + path: "/opt/{{ item.name }}" + state: directory + owner: "{{ users.0.username }}" + group: "{{ users.0.groupname }}" + mode: "{{ item.mode | default('0755')}}" + loop: + - name: appdata + - name: appdata/simple + - name: appdata/simple/keys + - name: appdata/simple/postgresql + mode: "0700" + - name: appdata/simple/sl + - name: appdata/simple/sl/pgp + - name: appdata/simple/upload + +- name: Create and chown postgres socket dir + ansible.builtin.file: + path: "/opt/run/postgresql" + state: directory + owner: "{{ users.0.username }}" + group: "{{ users.0.groupname }}" + mode: 0775 + +- name: Place certs + ansible.builtin.copy: + content: "{{ item.content }}" + dest: "/opt/appdata/simple/keys/{{ item.name }}" + owner: "{{ users.0.username }}" + group: "{{ users.0.groupname }}" + mode: 0644 + loop: + - name: dkim.key + content: "{{ secret_dkim }}" + - name: dkim.pub.key + content: "{{ secret_dkim_pub }}" + +- name: Premium script + ansible.builtin.template: + src: premium.j2 + dest: "/opt/scripts/premium.sh" + owner: "{{ users.0.username }}" + group: "{{ users.0.groupname }}" + mode: 0744 + +- name: Seed env-file + ansible.builtin.template: + src: .env.j2 + dest: "/opt/appdata/simple/sl.env" + owner: "{{ users.0.username }}" + group: "{{ users.0.groupname }}" + mode: 0644 + +- name: Seed composes + ansible.builtin.template: + src: "docker-compose.{{ item }}.j2" + dest: "/opt/appdata/simple/docker-compose.{{ item }}.yml" + owner: "{{ users.0.username }}" + group: "{{ users.0.groupname }}" + mode: 0644 + loop: + - app + - db + - init + - migration diff --git a/ansible/roles/simple-login/tasks/postfix.yml b/ansible/roles/simple-login/tasks/postfix.yml new file mode 100644 index 0000000..4a8c1e9 --- /dev/null +++ b/ansible/roles/simple-login/tasks/postfix.yml @@ -0,0 +1,43 @@ +- name: Set mailname + ansible.builtin.copy: + content: "{{ mail_domain }}" + dest: /etc/mailname + mode: 0755 + +- name: Create postfix directory + ansible.builtin.file: + path: "/etc/postfix" + state: directory + owner: root + mode: 0644 + +- name: Place main.cf + ansible.builtin.template: + src: main.j2 + dest: "/etc/postfix/main.cf" + owner: root + mode: 0644 + register: postfix_conf + +- name: Place pgsql-relay-domains.cf + ansible.builtin.template: + src: pgsql-relay-domains.j2 + dest: "/etc/postfix/pgsql-relay-domains.cf" + owner: root + mode: 0644 + register: postfix_conf + +- name: Place pgsql-transport-maps.cf + ansible.builtin.template: + src: pgsql-transport-maps.j2 + dest: "/etc/postfix/pgsql-transport-maps.cf" + owner: root + mode: 0644 + register: postfix_conf + +- name: Restart Postfix + ansible.builtin.service: + name: postfix + state: restarted + when: postfix_conf.changed + become: true diff --git a/ansible/roles/simple-login/templates/.env.j2 b/ansible/roles/simple-login/templates/.env.j2 new file mode 100644 index 0000000..5fe608f --- /dev/null +++ b/ansible/roles/simple-login/templates/.env.j2 @@ -0,0 +1,158 @@ +# OTHER ENVS + +TZ={{ ntp_timezone }} + +# This file contains all available options in SimpleLogin. +# Some are optional and are commented out by default. +# Some are only relevant for our SaaS version, for example for payment integration, analytics, etc. + +# Server url +URL=https://app.{{ sl_domain }} + + +# Only print email content, not sending it, for local development +# NOT_SEND_EMAIL=true + +# domain used to create alias +EMAIL_DOMAIN={{ mail_domain }} + +# Allow SimpleLogin to enforce SPF by using the extra headers from postfix +# ENFORCE_SPF=true + +# other domains that can be used to create aliases, in addition to EMAIL_DOMAIN +OTHER_ALIAS_DOMAINS={{ sl_alt_domains | safe }} + +# domains that can be used to create aliases. If set, override OTHER_ALIAS_DOMAINS +# ALIAS_DOMAINS={{ sl_alt_domains | safe }} + +# (optional) domains that are only available to premium accounts +PREMIUM_ALIAS_DOMAINS={{ sl_prem_domains | safe }} + +# the alias domain used when creating the first alias for user, default to EMAIL_DOMAIN if not set +# FIRST_ALIAS_DOMAIN = another-domain.com + +# transactional email is sent from this email address +SUPPORT_EMAIL=support@{{ mail_domain }} +SUPPORT_NAME=Rox + +# to receive general stats. +# ADMIN_EMAIL=admin@sl.local + +# Max number emails user can generate for free plan +# Set to 5 by default +MAX_NB_EMAIL_FREE_PLAN=50 + +# Close registration. Avoid people accidentally creating new account on a self-hosted SimpleLogin +# DISABLE_REGISTRATION=1 + +# custom domain needs to point to these MX servers +EMAIL_SERVERS_WITH_PRIORITY=[(10, "{{ mail_domain }}.")] + +# these emails are ignored when computing stats +# IGNORED_EMAILS = ["my_email@domain.com"] + +# By default, new aliases must end with ".{random_word}". This is to avoid a person taking all "nice" aliases. +# this option doesn't make sense in self-hosted. Set this variable to disable this option. +DISABLE_ALIAS_SUFFIX=1 + +# If you want to use another MTA to send email, you could set the address of your MTA here +# By default, emails are sent using the the same Postfix server that receives emails +POSTFIX_SERVER={{ sl_net.network.IPAM.Config[0].Gateway }} + +# the DKIM private key used to compute DKIM-Signature +DKIM_PRIVATE_KEY_PATH=/keys/dkim.key + +# delete and recreate the sqlite database, for local development +# RESET_DB=true + +# DB Connection +DB_URI=postgresql://{{ postgres.user }}:{{ postgres.password }}@sl_postgres:5432/{{ postgres.dbname }} + +FLASK_SECRET={{ secret_flask }} + +# AWS params +# BUCKET=to_fill +# AWS_ACCESS_KEY_ID=to_fill +# AWS_SECRET_ACCESS_KEY=to_fill +# AWS_REGION=to_fill + +# OpenId key +# OPENID_PRIVATE_KEY_PATH=local_data/jwtRS256.key +# OPENID_PUBLIC_KEY_PATH=local_data/jwtRS256.key.pub + +# Words to generate random email alias +# WORDS_FILE_PATH=local_data/words.txt + +# Login with Github +# GITHUB_CLIENT_ID=to_fill +# GITHUB_CLIENT_SECRET=to_fill + +# Login with Google +# GOOGLE_CLIENT_ID=to_fill +# GOOGLE_CLIENT_SECRET=to_fill + +# Flask profiler +# FLASK_PROFILER_PATH=/tmp/flask-profiler.sql +# FLASK_PROFILER_PASSWORD=password + +# Where to store GPG Keyring +GNUPGHOME=/sl/pgp + +# By default, files are uploaded to s3 +# Set this variable to use the local "static/upload/" directory instead +LOCAL_FILE_UPLOAD=true + +# The landing page +LANDING_PAGE_URL=https://app.{{ sl_domain }} + +# The status page +# STATUS_PAGE_URL=https://status.simplelogin.io + +# Used when querying info on Apple API +# APPLE_API_SECRET=secret +# MACAPP_APPLE_API_SECRET=secret + +# Disable onboarding emails +# For self-hosted instance +# DISABLE_ONBOARDING=true + +# By default use postfix port 25. This param is used to override the Postfix port, +# useful when using another SMTP server when developing locally +# POSTFIX_PORT=1025 + +# set the 2 below variables to enable hCaptcha +# HCAPTCHA_SECRET=very_long_string +# HCAPTCHA_SITEKEY=00000000-0000-0000-0000-000000000000 + +# Set the 2 below variables to enable Plausible Analytics +PLAUSIBLE_HOST=https://views.roxedus.net +PLAUSIBLE_DOMAIN=app.domain.com + +# Spamassassin server +# SPAMASSASSIN_HOST = 127.0.0.1 + +# if set, used to sign the forwarding emails +# PGP_SENDER_PRIVATE_KEY_PATH=local_data/private-pgp.asc + +# Coinbase +# COINBASE_WEBHOOK_SECRET=to_fill +# COINBASE_CHECKOUT_ID=to_fill +# COINBASE_API_KEY=to_fill +# COINBASE_YEARLY_PRICE=30.00 + +# set the frequency limit on alias creation +# ALIAS_LIMIT = "100/day;50/hour;5/minute" + +# whether to enable spam scan using SpamAssassin +# ENABLE_SPAM_ASSASSIN = 1 + +# Have I Been Pwned +# HIBP_SCAN_INTERVAL_DAYS = 7 +HIBP_API_KEYS={{ HIBP_secret }} + +# domains that can be present in the &next= section when using absolute urls +ALLOWED_REDIRECT_DOMAINS=[] + +# DNS nameservers to be used by the app +# Multiple nameservers can be specified, separated by ',' +NAMESERVERS="1.1.1.1" \ No newline at end of file diff --git a/ansible/roles/simple-login/templates/docker-compose.app.j2 b/ansible/roles/simple-login/templates/docker-compose.app.j2 new file mode 100644 index 0000000..2c513bb --- /dev/null +++ b/ansible/roles/simple-login/templates/docker-compose.app.j2 @@ -0,0 +1,49 @@ +--- +version: "2.1" +services: + sl_web: + image: "simplelogin/app-ci:{{ sl_version }}" + container_name: sl_web + user: "{{ users.0.uid }}:{{ users.0.gid }}" + depends_on: + - sl_postgres + env_file: + - sl.env + volumes: + - /opt/appdata/simple/keys:/keys:ro + - /opt/appdata/simple/sl:/sl + - /opt/appdata/simple/sl/upload:/code/static/upload + labels: + - swag=enable + - "swag_url=app.{{ sl_domain }}" + ports: + - 10.50.0.1:7777:7777 + networks: + - default + - proxynet + restart: unless-stopped + + sl_email: + image: "simplelogin/app-ci:{{ sl_version }}" + container_name: sl_email + user: "{{ users.0.uid }}:{{ users.0.gid }}" + command: python email_handler.py + depends_on: + - sl_postgres + env_file: + - sl.env + volumes: + - /opt/appdata/simple/keys:/keys:ro + - /opt/appdata/simple/sl:/sl + - /opt/appdata/simple/sl/upload:/code/static/upload + ports: + - 127.0.0.1:20381:20381 + restart: unless-stopped + +networks: + default: + external: true + name: sl_net + proxynet: + external: true + name: proxynet diff --git a/ansible/roles/simple-login/templates/docker-compose.db.j2 b/ansible/roles/simple-login/templates/docker-compose.db.j2 new file mode 100644 index 0000000..a77398d --- /dev/null +++ b/ansible/roles/simple-login/templates/docker-compose.db.j2 @@ -0,0 +1,24 @@ +--- +version: "2.1" +services: + sl_postgres: + image: "postgres:{{ sl_postgres_version }}" + container_name: sl_postgres + user: "{{ users.0.uid }}:{{ users.0.gid }}" + environment: + - "POSTGRES_DB={{ postgres.dbname }}" + - "POSTGRES_PASSWORD={{ postgres.password }}" + - "POSTGRES_USER={{ postgres.user }}" + - "TZ={{ ntp_timezone }}" + volumes: + - /etc/passwd:/etc/passwd:ro + - /opt/appdata/simple/postgresql:/var/lib/postgresql/data + - /opt/run/postgresql:/var/run/postgresql + ports: + - 127.0.0.1:5432:5432 + restart: unless-stopped + +networks: + default: + external: true + name: sl_net diff --git a/ansible/roles/simple-login/templates/docker-compose.init.j2 b/ansible/roles/simple-login/templates/docker-compose.init.j2 new file mode 100644 index 0000000..346c341 --- /dev/null +++ b/ansible/roles/simple-login/templates/docker-compose.init.j2 @@ -0,0 +1,19 @@ +--- +version: "2.1" +services: + sl_init: + image: "simplelogin/app-ci:{{ sl_version }}" + container_name: sl_init + user: "{{ users.0.uid }}:{{ users.0.gid }}" + command: python init_app.py + env_file: + - sl.env + volumes: + - /opt/appdata/simple/keys:/keys:ro + - /opt/appdata/simple/sl:/sl + - /opt/appdata/simple/sl/upload:/code/static/upload + +networks: + default: + external: true + name: sl_net diff --git a/ansible/roles/simple-login/templates/docker-compose.migration.j2 b/ansible/roles/simple-login/templates/docker-compose.migration.j2 new file mode 100644 index 0000000..6595acc --- /dev/null +++ b/ansible/roles/simple-login/templates/docker-compose.migration.j2 @@ -0,0 +1,20 @@ +--- +version: "2.1" +services: + sl_migration: + image: "simplelogin/app-ci:{{ sl_version }}" + container_name: sl_migration + user: "{{ users.0.uid }}:{{ users.0.gid }}" + command: alembic upgrade head + env_file: + - sl.env + volumes: + - /opt/appdata/simple/keys:/keys:ro + - /opt/appdata/simple/sl:/sl + - /opt/appdata/simple/sl/upload:/code/static/upload + - /opt/appdata/simple/sl.env:/code/.env + +networks: + default: + external: true + name: sl_net diff --git a/ansible/roles/simple-login/templates/main.j2 b/ansible/roles/simple-login/templates/main.j2 new file mode 100644 index 0000000..f2131fb --- /dev/null +++ b/ansible/roles/simple-login/templates/main.j2 @@ -0,0 +1,65 @@ +# POSTFIX config file, adapted for SimpleLogin +smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu) +biff = no + +# appending .domain is the MUA's job. +append_dot_mydomain = no + +# Uncomment the next line to generate "delayed mail" warnings +#delay_warning_time = 4h + +readme_directory = no + +# See http://www.postfix.org/COMPATIBILITY_README.html -- default to 2 on +# fresh installs. +compatibility_level = 2 + +# TLS parameters +smtpd_tls_cert_file=/opt/appdata/swag/config/etc/letsencrypt/live/{{ sl_domain }}/fullchain.pem +smtpd_tls_key_file=/opt/appdata/swag/config/etc/letsencrypt/live/{{ sl_domain }}/privkey.pem +smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache +smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache +smtp_tls_security_level = may +smtpd_tls_security_level = may + +# See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for +# information on enabling SSL in the smtp client. + +alias_maps = hash:/etc/aliases +mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 {{ sl_net.network.IPAM.Config[0].Subnet }} + +# Set your domain here +mydestination = +myhostname = app.{{ sl_domain }} +mydomain = {{ mail_domain }} +myorigin = {{ mail_domain }} + +relay_domains = pgsql:/etc/postfix/pgsql-relay-domains.cf +transport_maps = pgsql:/etc/postfix/pgsql-transport-maps.cf + +# HELO restrictions +smtpd_delay_reject = yes +smtpd_helo_required = yes +smtpd_helo_restrictions = + permit_mynetworks, + reject_non_fqdn_helo_hostname, + reject_invalid_helo_hostname, + permit + +# Sender restrictions: +smtpd_sender_restrictions = + permit_mynetworks, + reject_non_fqdn_sender, + reject_unknown_sender_domain, + permit + +# Recipient restrictions: +smtpd_recipient_restrictions = + reject_unauth_pipelining, + reject_non_fqdn_recipient, + reject_unknown_recipient_domain, + permit_mynetworks, + reject_unauth_destination, + reject_rbl_client zen.spamhaus.org, + reject_rbl_client bl.spamcop.net, + permit diff --git a/ansible/roles/simple-login/templates/pgsql-relay-domains.j2 b/ansible/roles/simple-login/templates/pgsql-relay-domains.j2 new file mode 100644 index 0000000..f556587 --- /dev/null +++ b/ansible/roles/simple-login/templates/pgsql-relay-domains.j2 @@ -0,0 +1,8 @@ +# postgres config +hosts = 127.0.0.1:5432 +user = {{ postgres.user }} +password = {{ postgres.password }} +dbname = {{ postgres.dbname }} + +query = SELECT domain FROM custom_domain WHERE domain='%s' AND verified=true + UNION SELECT '%s' WHERE '%s' IN ('{{ mail_domain }}', '{{ (sl_prem_domains + sl_alt_domains)|map('lower')|join("', '") }}') LIMIT 1; \ No newline at end of file diff --git a/ansible/roles/simple-login/templates/pgsql-transport-maps.j2 b/ansible/roles/simple-login/templates/pgsql-transport-maps.j2 new file mode 100644 index 0000000..d094ba2 --- /dev/null +++ b/ansible/roles/simple-login/templates/pgsql-transport-maps.j2 @@ -0,0 +1,9 @@ +# postgres config +hosts = 127.0.0.1:5432 +user = {{ postgres.user }} +password = {{ postgres.password }} +dbname = {{ postgres.dbname }} + +# forward to smtp:127.0.0.1:20381 for custom domain AND email domain +query = SELECT 'smtp:127.0.0.1:20381' FROM custom_domain WHERE domain = '%s' AND verified=true + UNION SELECT 'smtp:127.0.0.1:20381' WHERE '%s' IN ('{{ mail_domain }}', '{{ (sl_prem_domains + sl_alt_domains)|map('lower')|join("', '") }}') LIMIT 1; \ No newline at end of file diff --git a/ansible/roles/simple-login/templates/premium.j2 b/ansible/roles/simple-login/templates/premium.j2 new file mode 100644 index 0000000..d8f3452 --- /dev/null +++ b/ansible/roles/simple-login/templates/premium.j2 @@ -0,0 +1,3 @@ +#!/bin/bash + +psql -d {{ postgres.dbname }} -h /opt/run/postgresql -c "UPDATE users SET lifetime = '1' WHERE email = '$1';" diff --git a/ansible/roles/swag/tasks/main.yml b/ansible/roles/swag/tasks/main.yml new file mode 100644 index 0000000..a5b2168 --- /dev/null +++ b/ansible/roles/swag/tasks/main.yml @@ -0,0 +1,33 @@ +- name: Create SWAG appdata directory + ansible.builtin.file: + path: "/opt/{{ item.name }}" + state: directory + owner: "{{ users.0.username }}" + group: "{{ users.0.groupname }}" + mode: "{{ item.mode | default('0755')}}" + loop: + - name: appdata + - name: appdata/swag + - name: appdata/swag/config + - name: appdata/swag/config/dns-conf + +- name: Create proxynet + community.docker.docker_network: + name: proxynet + +- name: Seed compose + ansible.builtin.template: + src: "docker-compose.yml" + dest: "/opt/appdata/swag/docker-compose.yml" + owner: "{{ users.0.username }}" + group: "{{ users.0.groupname }}" + mode: 0644 + +- name: Install swag dns file + template: + src: "cloudflare.ini" + dest: "/opt/appdata/swag/config/dns-conf/cloudflare.ini" + mode: "600" # To prevent unnessecary nag in logs + owner: "{{ users.0.username }}" + group: "{{ users.0.groupname }}" + become: true diff --git a/ansible/roles/swag/templates/cloudflare.ini b/ansible/roles/swag/templates/cloudflare.ini new file mode 100644 index 0000000..7db79b3 --- /dev/null +++ b/ansible/roles/swag/templates/cloudflare.ini @@ -0,0 +1,3 @@ +{{ ansible_managed | comment }} + +dns_cloudflare_api_token = {{ secret_cloudflare[sl_domain].apikey }} \ No newline at end of file diff --git a/ansible/roles/swag/templates/docker-compose.yml b/ansible/roles/swag/templates/docker-compose.yml new file mode 100644 index 0000000..779b310 --- /dev/null +++ b/ansible/roles/swag/templates/docker-compose.yml @@ -0,0 +1,55 @@ +--- +version: "2.1" +services: + swag: + image: lscr.io/linuxserver/swag + container_name: swag + cap_add: + - NET_ADMIN + environment: + - PUID={{ users.0.uid }} + - PGID={{ users.0.gid }} + - TZ={{ ntp_timezone }} + - URL={{ sl_domain }} + - SUBDOMAINS=wildcard + - EXTRA_DOMAINS=*.{{ mail_domain }} + - VALIDATION=dns + - DNSPLUGIN=cloudflare + - EMAIL={{ secret_cloudflare.email }} + - STAGING=false + - DOCKER_MODS=linuxserver/mods:universal-docker|linuxserver/mods:swag-auto-proxy|swag-maxmind + - DOCKER_HOST=dockerproxy:2375 + volumes: + - type: bind + source: /opt/appdata/swag/config + target: /config + - type: tmpfs + target: /config/nginx/proxy-confs + networks: + - default + - docker + ports: + - 443:443 + - 80:80 + restart: unless-stopped + + dockerproxy: + image: ghcr.io/tecnativa/docker-socket-proxy:latest + container_name: dockerproxy + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + restart: unless-stopped + networks: + - docker + environment: + - CONTAINERS=1 + - POST=0 + +networks: + default: + external: true + name: proxynet + + docker: + internal: true + name: docker diff --git a/ansible/roles/ufw/tasks/main.yml b/ansible/roles/ufw/tasks/main.yml new file mode 100644 index 0000000..b96329f --- /dev/null +++ b/ansible/roles/ufw/tasks/main.yml @@ -0,0 +1,44 @@ +- name: Install ufw + ansible.builtin.apt: + update_cache: yes + pkg: + - ufw + +- name: Allow everything and enable UFW + community.general.ufw: + state: enabled + policy: deny + +- name: Allow ports + community.general.ufw: + rule: allow + port: "{{ item | int }}" + loop: + - "{{ secret_ssh_port }}" + - "{{ wireguard.port }}" + - 110 + - 143 + - 22 + - 25 + - 443 + - 465 + - 587 + - 80 + - 993 + - 995 + +- name: Endlessh + community.docker.docker_container: + name: endlessh + pull: yes + restart_policy: unless-stopped + recreate: yes + env: + PUID: "{{ users.0.uid }}" + PGID: "{{ users.0.gid }}" + TZ: "{{ ntp_timezone }}" + image: lscr.io/linuxserver/endlessh + ports: + - "22:2222" + tmpfs: + - /config diff --git a/ansible/roles/wireguard/tasks/main.yml b/ansible/roles/wireguard/tasks/main.yml new file mode 100644 index 0000000..be4c204 --- /dev/null +++ b/ansible/roles/wireguard/tasks/main.yml @@ -0,0 +1,56 @@ +- name: Install packages for wireguard + ansible.builtin.apt: + update_cache: yes + pkg: + - qrencode + - wireguard + - wireguard-tools + +- name: Wireguard server config + ansible.builtin.template: + src: wireguard-server.conf + dest: /etc/wireguard/wg0.conf + mode: "0600" + backup: yes + become: true + register: wireguard_conf + +- name: Enable wireguard + ansible.builtin.service: + name: wg-quick@wg0 + enabled: true + become: true + +- name: Restart wireguard + ansible.builtin.service: + name: wg-quick@wg0 + state: restarted + when: wireguard_conf.changed + become: true + +- name: Create wireguard client directory + ansible.builtin.file: + path: "/home/{{ users.0.username }}/wireguard-clients" + state: directory + owner: "{{ users.0.username }}" + mode: 0700 + +- name: Wireguard client configuration + ansible.builtin.template: + src: wireguard-client.conf + dest: "/home/{{ users.0.username }}/wireguard-clients/{{ item.key }}.conf" + owner: "{{ users.0.username }}" + mode: 0600 + loop: "{{ wireguard.clients|dict2items }}" + loop_control: + label: "{{ item.key }}" + +- name: Enable p2p communication + ansible.builtin.sysctl: + name: net.ipv4.ip_forward + value: "1" + sysctl_set: yes + state: present + reload: yes + sysctl_file: /etc/sysctl.d/99-sysctl.conf + become: true diff --git a/ansible/roles/wireguard/templates/wireguard-client.conf b/ansible/roles/wireguard/templates/wireguard-client.conf new file mode 100644 index 0000000..b0e3cae --- /dev/null +++ b/ansible/roles/wireguard/templates/wireguard-client.conf @@ -0,0 +1,10 @@ +[Interface] +Address = {{ item.value.ip }} +PrivateKey = {{ item.value.private_key }} + +[Peer] +PublicKey = {{ wireguard.server.public_key }} +Endpoint = {{ wireguard.public_ip }}:{{ wireguard.port }} +AllowedIPs = {{ wireguard.cidr }} + +PersistentKeepalive = 25 \ No newline at end of file diff --git a/ansible/roles/wireguard/templates/wireguard-server.conf b/ansible/roles/wireguard/templates/wireguard-server.conf new file mode 100644 index 0000000..50db149 --- /dev/null +++ b/ansible/roles/wireguard/templates/wireguard-server.conf @@ -0,0 +1,11 @@ +[Interface] +Address = {{ wireguard.server.ip }} +PrivateKey = {{ wireguard.server.private_key }} +ListenPort = {{ wireguard.port }} + +{% for name, config in wireguard.clients.items() %} +[Peer] +# {{ name }} +PublicKey = {{ config.public_key }} +AllowedIPs = {{ config.ip }}/32 +{% endfor %} \ No newline at end of file diff --git a/ansible/run.yml b/ansible/run.yml new file mode 100755 index 0000000..3992194 --- /dev/null +++ b/ansible/run.yml @@ -0,0 +1,230 @@ +- hosts: all + become: yes + tags: [never, init] + vars_files: + - "vars/vault.yml" + + collections: + - ansible.builtin.hostname + - ansible.builtin.group + - ansible.builtin.user + - ansible.posix.authorized_key + - ansible.builtin.lineinfile + - ansible.builtin.file + - ansible.builtin.copy + - ansible.builtin.reboot + + pre_tasks: + - include_tasks: tasks/user.yml + with_items: "{{ users }}" + loop_control: + loop_var: user + + - name: Change hostname + when: "set_hostname is defined" + register: new_hostname + ansible.builtin.hostname: + name: "{{ set_hostname }}" + + - name: Change hostname in hosts + when: new_hostname.changed + ansible.builtin.lineinfile: + path: /etc/hosts + regexp: '^127\.0\.0\.1 localhost' + line: "127.0.0.1 localhost {{ set_hostname }}" + owner: root + group: root + mode: "0644" + + - name: Create scripts directory + ansible.builtin.file: + path: "/opt/scripts" + state: directory + owner: "{{ users.0.username }}" + group: "{{ users.0.groupname }}" + mode: "0775" + register: script_dir + + - name: Add bin dir to system-wide $PATH. + ansible.builtin.copy: + dest: /etc/profile.d/custom-path.sh + content: "PATH=$PATH:/opt/scripts" + mode: "0644" + when: script_dir.changed + + - name: Reboot the server + ansible.builtin.reboot: + msg: "Reboot initiated by Ansible due to hostname change" + connect_timeout: 5 + reboot_timeout: 300 + pre_reboot_delay: 2 + post_reboot_delay: 30 + test_command: uptime + when: new_hostname.changed + + - name: Update packages + ansible.builtin.apt: + update_cache: true + cache_valid_time: 1 + + roles: + - role: geerlingguy.ntp + - role: geerlingguy.security + + tasks: + - name: Change ssh port + set_fact: + ansible_port: "{{ secret_ssh_port }}" + +- hosts: all + become: yes + tags: [always] + vars_files: + - "vars/vault.yml" + + collections: + - ansible.builtin.apt + + tasks: + - name: Install packages + ansible.builtin.apt: + update_cache: yes + pkg: "{{ package_list }}" + +- hosts: docker + become: yes + tags: [docker] + vars_files: + - "vars/vault.yml" + + collections: + - ansible.builtin.pip + - ansible.builtin.copy + - ansible.builtin.service + - ansible.builtin.file + - ansible.builtin.get_url + + post_tasks: + - name: Install pip packages + tags: [never, init] + ansible.builtin.pip: + name: + - docker + - github3.py + + - name: Set Default logging + tags: [never, init, logging] + register: docker_deamon + ansible.builtin.copy: + dest: /etc/docker/daemon.json + owner: "root" + group: "root" + content: | + { + "log-driver": "journald", + "log-opts": { + "mode": "non-blocking" + } + } + mode: "0600" + + - name: Restart service to apply changes + tags: [never, init, logging] + when: docker_deamon.changed + ansible.builtin.service: + name: docker + state: restarted + + - name: Create plugin directory if not present + ansible.builtin.file: + path: "/home/{{ users.0.username }}/.docker/cli-plugins/" + state: directory + owner: "{{ users.0.username }}" + group: "{{ users.0.groupname }}" + mode: "0775" + + - name: Get latest release of a public repository + community.general.github_release: + user: docker + repo: compose + action: latest_release + register: comp_cli + + - name: Install compose plugin + ansible.builtin.get_url: + url: "https://github.com/docker/compose/releases/download/{{comp_cli.tag}}/docker-compose-linux-x86_64" + dest: "/home/{{ users.0.username }}/.docker/cli-plugins/docker-compose" + mode: "0755" + owner: "{{ users.0.username }}" + group: "{{ users.0.groupname }}" + + roles: + - role: geerlingguy.docker + tags: [never, init] + +- hosts: simple_login + become: yes + tags: [init, mail] + vars_files: + - "vars/vault.yml" + + roles: + - role: wireguard + tags: [wireguard] + - role: simple-login + tags: [mail] + - role: swag + tags: [edge] + - role: authentik + tags: [edge] + - role: ufw + tags: [edge] + +- hosts: all + become: yes + tags: [update, init] + vars_files: + - "vars/vault.yml" + + collections: + - ansible.builtin.apt + - ansible.builtin.file + - ansible.builtin.reboot + + tasks: + # https://www.cyberciti.biz/faq/ansible-apt-update-all-packages-on-ubuntu-debian-linux/ + - name: Update packages + ansible.builtin.apt: + update_cache: true + force_apt_get: true + cache_valid_time: 3600 + upgrade: true + + - name: Remove ubuntu motd spam + ansible.builtin.file: + path: "/etc/update-motd.d/{{ item }}" + state: absent + loop: + - 10-help-text + - 50-landscape-sysinfo + - 50-motd-news + - 80-livepatch + - 90-updates-available + - 95-hwe-eol + when: ansible_distribution == 'Ubuntu' + + - name: Check if a reboot is needed for Debian and Ubuntu boxes + register: reboot_required_file + stat: + path: /var/run/reboot-required + get_md5: no + + - name: Reboot the server + ansible.builtin.reboot: + msg: "Reboot initiated by Ansible due to kernel updates" + connect_timeout: 5 + reboot_timeout: 300 + pre_reboot_delay: 0 + post_reboot_delay: 30 + test_command: uptime + when: reboot_required_file.stat.exists diff --git a/ansible/tasks/user.yml b/ansible/tasks/user.yml new file mode 100644 index 0000000..16d88b2 --- /dev/null +++ b/ansible/tasks/user.yml @@ -0,0 +1,20 @@ +- name: Ensure groups exists + ansible.builtin.group: + name: "{{ user.groupname }}" + gid: "{{ user.gid | default(None) }}" + state: present + +- name: Add users + ansible.builtin.user: + name: "{{ user.username }}" + uid: "{{ user.uid | default(None) }}" + group: "{{ user.groupname | default(user.username) }}" + shell: "{{ user.shell | default('/bin/bash') }}" + move_home: "{{ user.home | default(None) }}" + password: "{{ user.password | default(None) }}" + +- name: Add a Github key ssh key + when: "user.github is defined" + ansible.posix.authorized_key: + user: "{{ user.username }}" + key: "https://github.com/{{ user.github }}.keys" diff --git a/ansible/vars/example.yml b/ansible/vars/example.yml new file mode 100644 index 0000000..d143a77 --- /dev/null +++ b/ansible/vars/example.yml @@ -0,0 +1,50 @@ +--- +secret_rox_pass: #Hash +secret_ssh_port: +secret_sudo: + +secret_flask: #Used in SL + +HIBP_secret: #Used in SL + +sl_prem_domains: + - mail.domain.com + +postgres: + user: + password: + dbname: + +secret_dkim: | + RSA Key + +secret_dkim_pub: | + Pub Key + +wireguard: + public_ip: + port: + cidr: 10.50.0.0/24 + server: + ip: 10.50.0.1 + public_key: + private_key: + + clients: + phone: + ip: 10.50.0.10 + public_key: + private_key: + +secret_cloudflare: + email: + domain.com: + apikey: + zones: + +secret_authentik: + postgres: + password: + database: + user: + secret_key: diff --git a/terraform/.terraform.lock.hcl b/terraform/.terraform.lock.hcl new file mode 100644 index 0000000..eabd99b --- /dev/null +++ b/terraform/.terraform.lock.hcl @@ -0,0 +1,44 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/cloudflare/cloudflare" { + version = "2.21.0" + constraints = "2.21.0" + hashes = [ + "h1:eSPGBjaFZ6xgfRLcicjFDq1Ic7H9ErtLD0hViJS0SDI=", + "zh:10462d1f27dfeda3fadae75d05bb1b41791a3b69b70c8846745673649113a633", + "zh:1f5520b083996c6ebae0cfacb313afe0d4694861c58677957a4f39242ca22497", + "zh:39e3fc58f4ff8d1e2dd6a413f24bb9c49d6a619861218303727a7f2df4707b0c", + "zh:68f2f1d34e2819d580d6f3534369e3d14ce2cba4707b216ff3ad0b908733bd84", + "zh:6b175ba58094d8dfbcfbeee72dbef321146733e4e13029e0e41eb0f585ad4c88", + "zh:71c2652f19e09fa6a2826f0d690e7d7bdbf96e3dbd7d53564f302b0b866c6fc4", + "zh:7e903c7f55afcb9d17a76204392862670ff7131a248cd812bcf77be36e4403f9", + "zh:816671387134fe1c6c14e803a62ee14e316bbf812b5973b2e83434434cebff0d", + "zh:bb09e052c6f6d46484fa696ff2441834779f81e0c5e7631c722dc3123ab4fd57", + "zh:c6e538a48cc213e3d29d3203697e52dd9c19ed7abe04f640cc696c3ec175f3bc", + "zh:d01dce22be0bb7b63e201ac9186adc6a36781ef80c4446b0af7014f67dbba095", + "zh:f733ffa3b4a63866daaa1e93687c92726ddb790d714edd2ea9bfc8b541ecb1d4", + "zh:ff862bb0e1102ef6f44ac60737c8be3302f2540a0652c2899805b031e3220145", + ] +} + +provider "registry.terraform.io/linode/linode" { + version = "1.18.0" + constraints = "1.18.0" + hashes = [ + "h1:+8f4i5sj2WOd5bdWSPUafHdN+s1Fgq9/l1+Rc3TMf7U=", + "zh:0ead391cba4eccff9d46c91e9260ce5e2ccfd69e2aebef253768ce29e2de3a7d", + "zh:27708a55d1ba1594086c2015441243a38a608f68ea2f82f1d759c6baf2a0df14", + "zh:3d355a270e7eaeafd5044a326c527c23742b312376368e1019e3caa779cdbc91", + "zh:41dde82124e6c2e2640ef2963fe4f6faf16f8e8b82e7dbaebfdec7b781f5455a", + "zh:51e9139cdc1386053c6834585139dc74d6fb7653a00b495377bc445b5e532218", + "zh:6ba6560bf23736a2a6e4c0899afd2c25cac6697d90cf2573449fe9b655f87920", + "zh:79c1fa8e3a8705eee73f171229ff47688deaff8468cdf28fddaafe5aef7e2d8d", + "zh:80b008ded1c71313c4f76e5569142e3a56b866f7693e57270d15f13fc7af1e14", + "zh:b0ebb1e83e8d999dc1d8feecf9c1e293cd61fe72271610284fdcce46d4a8a7ed", + "zh:bdaa786f0381ccd61404ea1835733e852e9747f1daf9a63bd4149073dbce85b6", + "zh:c67cd9e8d4880dfa6cbbd25aa7fcd9c07a76f4801180ac3988ff3f84ede6181f", + "zh:c8ee62dfd07d83dd362b8ba5f13a957e1ec8107b22ac168da4fa8470c4537a33", + "zh:cf7bdc5eac5df6cfc6ab5c7cafaba72b6bf5a155017e25edc6d9dc192bb6d2ed", + ] +} diff --git a/terraform/mail.tf b/terraform/mail.tf new file mode 100644 index 0000000..4713535 --- /dev/null +++ b/terraform/mail.tf @@ -0,0 +1,207 @@ +terraform { + required_providers { + linode = { + source = "linode/linode" + version = "1.18.0" + } + cloudflare = { + source = "cloudflare/cloudflare" + version = "2.21.0" + } + } +} + +provider "linode" { + token = var.linode_token +} + +provider "cloudflare" { + email = var.cloudflare_email + api_token = var.cloudflare_api_token +} + +resource "linode_instance" "mail" { + image = "linode/ubuntu20.04" + label = "mail" + group = "Terraform" + region = "eu-central" + type = "g6-standard-1" + authorized_keys = var.authorized_keys + root_pass = var.root_pass +} + +resource "cloudflare_record" "dns_mail4" { + zone_id = var.cloudflare_zone_id + name = "mail" + value = linode_instance.mail.ip_address + type = "A" + ttl = 3600 +} + +resource "cloudflare_record" "dns_mail4_app" { + zone_id = var.cloudflare_zone_id + name = "app" + value = linode_instance.mail.ip_address + type = "A" + ttl = 3600 +} + +resource "cloudflare_record" "dns_mail6_app" { + zone_id = var.cloudflare_zone_id + name = "app" + value = trimsuffix(linode_instance.mail.ipv6, "/128") + type = "AAAA" + ttl = 3600 +} + +resource "cloudflare_record" "dns_mail4_wild" { + zone_id = var.cloudflare_zone_id + name = "*.mail" + value = linode_instance.mail.ip_address + type = "A" + ttl = 3600 +} + +resource "cloudflare_record" "dns_mail6" { + zone_id = var.cloudflare_zone_id + name = "mail" + value = trimsuffix(linode_instance.mail.ipv6, "/128") + type = "AAAA" + ttl = 3600 +} + +resource "cloudflare_record" "dns_mail6_wild" { + zone_id = var.cloudflare_zone_id + name = "*.mail" + value = trimsuffix(linode_instance.mail.ipv6, "/128") + type = "AAAA" + ttl = 3600 +} + +resource "cloudflare_record" "dns_mx" { + zone_id = var.cloudflare_zone_id + name = "@" + value = "mail.${var.domain}" + type = "MX" + priority = "1" +} + +resource "cloudflare_record" "dns_mx_wild" { + zone_id = var.cloudflare_zone_id + name = "*" + value = "mail.${var.domain}" + type = "MX" + priority = "1" +} + +resource "cloudflare_record" "spf" { + zone_id = var.cloudflare_zone_id + name = "@" + value = "v=spf1 mx -all" + type = "TXT" +} + +resource "cloudflare_record" "dmarc" { + zone_id = var.cloudflare_zone_id + name = "_dmarc.${var.domain}" + value = "v=DMARC1; p=quarantine; adkim=r; aspf=r" + type = "TXT" +} +### + +resource "cloudflare_record" "dns_alt4" { + zone_id = var.cloudflare_alt_zone_id + name = "mail" + value = linode_instance.mail.ip_address + type = "A" + ttl = 3600 +} + +resource "cloudflare_record" "dns_alt4_app" { + zone_id = var.cloudflare_alt_zone_id + name = "app" + value = linode_instance.mail.ip_address + type = "A" + ttl = 3600 +} + +resource "cloudflare_record" "dns_alt4_wild" { + zone_id = var.cloudflare_alt_zone_id + name = "*.mail" + value = linode_instance.mail.ip_address + type = "A" + ttl = 3600 +} + +resource "cloudflare_record" "dns_alt6" { + zone_id = var.cloudflare_alt_zone_id + name = "mail" + value = trimsuffix(linode_instance.mail.ipv6, "/128") + type = "AAAA" + ttl = 3600 +} + +resource "cloudflare_record" "dns_alt6_wild" { + zone_id = var.cloudflare_alt_zone_id + name = "*.mail" + value = trimsuffix(linode_instance.mail.ipv6, "/128") + type = "AAAA" + ttl = 3600 +} + +resource "cloudflare_record" "dns_alt_mx" { + zone_id = var.cloudflare_alt_zone_id + name = "@" + value = "mail.${var.alt_domain}" + type = "MX" + priority = "1" +} + +resource "cloudflare_record" "dns_alt_mx_wild" { + zone_id = var.cloudflare_alt_zone_id + name = "*" + value = "mail.${var.alt_domain}" + type = "MX" + priority = "1" +} + +resource "cloudflare_record" "spf_alt" { + zone_id = var.cloudflare_alt_zone_id + name = "@" + value = "v=spf1 include:mail.domain.com -all" + type = "TXT" +} + +resource "cloudflare_record" "dmarc_alt" { + zone_id = var.cloudflare_alt_zone_id + name = "_dmarc.${var.alt_domain}" + value = "v=DMARC1; p=quarantine; adkim=r; aspf=r" + type = "TXT" +} + +### +resource "linode_rdns" "rdns4" { + address = linode_instance.mail.ip_address + rdns = "mail.${var.domain}" +} + +resource "linode_rdns" "rdns6" { + address = trimsuffix(linode_instance.mail.ipv6, "/128") + rdns = "mail.${var.domain}" +} + +variable "linode_token" {} +variable "cloudflare_email" {} +variable "cloudflare_api_token" {} +variable "authorized_keys" {} +variable "root_pass" {} +variable "cloudflare_zone_id" {} +variable "cloudflare_alt_zone_id" {} +variable "domain" { + type = string + default = "domain.com" +} +variable "alt_domain" { + type = string + default = "other_domain.com" +}