diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml index 6e0dfda..9421654 100644 --- a/ansible/group_vars/all.yml +++ b/ansible/group_vars/all.yml @@ -15,9 +15,11 @@ wazuh_port: 55000 # 📦 Container Images nextcloud_aio_image: nextcloud/all-in-one:latest keycloak_image: quay.io/keycloak/keycloak:24.0.2 +mailcow_image: mailcow/mailcow-dockerized:latest # 🗂️ Data Directories nextcloud_data_dir: /srv/nextcloud +mailcow_data_dir: /opt/mailcow backup_base_dir: /srv/backups logs_dir: /var/log/open-cmmc @@ -37,3 +39,13 @@ hostname: open-cmmc-gateway # 📜 Default Realm for Keycloak keycloak_realm: OpenCMMC + +# 📧 Mailcow Settings +mailcow_hostname: mail +mailcow_domain: "{{ domain_name }}" +mailcow_fqdn: "{{ mailcow_hostname }}.{{ mailcow_domain }}" +mailcow_timezone: America/New_York +mailcow_admin_user: admin +mailcow_admin_password: change_me_securely +mailcow_letsencrypt_email: it@example.com +mailcow_use_letsencrypt: "n" diff --git a/ansible/roles/keycloak_init/README.md b/ansible/roles/keycloak_init/README.md deleted file mode 100644 index 7a96b8b..0000000 --- a/ansible/roles/keycloak_init/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Ansible Role: keycloak_init - -This role initializes a Keycloak instance with a predefined realm, groups, and clients for the OpenCMMC Stack. diff --git a/ansible/roles/keycloak_init/tasks/clients.yml b/ansible/roles/keycloak_init/tasks/clients.yml deleted file mode 100644 index 8673c15..0000000 --- a/ansible/roles/keycloak_init/tasks/clients.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -- name: Register SAML client for Nextcloud - shell: '/opt/keycloak/bin/kcadm.sh create clients -r OpenCMMC -s clientId=nextcloud-aio -s protocol=saml -s enabled=true' - -- name: Register OIDC client for Mailcow - shell: '/opt/keycloak/bin/kcadm.sh create clients -r OpenCMMC -s clientId=mailcow -s protocol=openid-connect -s enabled=true' diff --git a/ansible/roles/keycloak_init/tasks/groups.yml b/ansible/roles/keycloak_init/tasks/groups.yml deleted file mode 100644 index 2bcdb30..0000000 --- a/ansible/roles/keycloak_init/tasks/groups.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -- name: Create Keycloak Groups - shell: '/opt/keycloak/bin/kcadm.sh create groups -r OpenCMMC -s name=Access_CUI' - -- name: Create FCI Group - shell: '/opt/keycloak/bin/kcadm.sh create groups -r OpenCMMC -s name=Access_FCI' - -- name: Create Proprietary Group - shell: '/opt/keycloak/bin/kcadm.sh create groups -r OpenCMMC -s name=Access_Proprietary' diff --git a/ansible/roles/keycloak_init/tasks/main.yml b/ansible/roles/keycloak_init/tasks/main.yml deleted file mode 100644 index efea415..0000000 --- a/ansible/roles/keycloak_init/tasks/main.yml +++ /dev/null @@ -1,80 +0,0 @@ ---- -- name: Check if Keycloak is already installed - stat: - path: /opt/keycloak/bin/kcadm.sh - register: keycloak_installed - -- name: Download Keycloak if not already present - unarchive: - src: "https://github.com/keycloak/keycloak/releases/download/{{ keycloak_version }}/keycloak-{{ keycloak_version }}.tar.gz" - dest: /opt/ - remote_src: yes - when: not keycloak_installed.stat.exists - -- name: Rename keycloak directory - command: mv /opt/keycloak-{{ keycloak_version }} /opt/keycloak - args: - creates: /opt/keycloak/bin/kcadm.sh - when: not keycloak_installed.stat.exists - -- name: Set executable permissions on kcadm.sh - file: - path: /opt/keycloak/bin/kcadm.sh - mode: '0755' - when: not keycloak_installed.stat.exists - -- name: Log in to Keycloak Admin CLI - command: > - /opt/keycloak/bin/kcadm.sh config credentials - --server http://localhost:8080 - --realm master - --user {{ keycloak_admin_user }} - --password {{ keycloak_admin_password }} - environment: - KC_HOME: /opt/keycloak - register: kcadm_login - changed_when: false - -- name: Create OpenCMMC realm - command: > - /opt/keycloak/bin/kcadm.sh create realms - -s realm=OpenCMMC -s enabled=true - environment: - KC_HOME: /opt/keycloak - when: kcadm_login is succeeded - -- name: Create groups - loop: - - Access_CUI - - Access_FCI - - Access_Proprietary - command: > - /opt/keycloak/bin/kcadm.sh create groups -r OpenCMMC -s name={{ item }} - environment: - KC_HOME: /opt/keycloak - when: kcadm_login is succeeded - -- name: Create OIDC client for Mailcow - command: > - /opt/keycloak/bin/kcadm.sh create clients -r OpenCMMC - -s clientId=mailcow - -s enabled=true - -s protocol=openid-connect - -s publicClient=false - -s 'redirectUris=["https://mail.yourdomain.com/*"]' - environment: - KC_HOME: /opt/keycloak - when: kcadm_login is succeeded - -- name: Create SAML client for Nextcloud - command: > - /opt/keycloak/bin/kcadm.sh create clients -r OpenCMMC - -s clientId=nextcloud - -s enabled=true - -s protocol=saml - -s 'redirectUris=["https://nextcloud.yourdomain.com/*"]' - -s 'attributes.saml.assertion.signature=true' - -s 'attributes.saml.force.post.binding=true' - environment: - KC_HOME: /opt/keycloak - when: kcadm_login is succeeded diff --git a/ansible/roles/keycloak_init/tasks/realm.yml b/ansible/roles/keycloak_init/tasks/realm.yml deleted file mode 100644 index 5368c58..0000000 --- a/ansible/roles/keycloak_init/tasks/realm.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -- name: Create Keycloak Realm - shell: '/opt/keycloak/bin/kcadm.sh create realms -s realm=OpenCMMC -s enabled=true' - args: - creates: /opt/keycloak/data/realms/OpenCMMC diff --git a/ansible/roles/keycloak_init/tasks/user.yml b/ansible/roles/keycloak_init/tasks/user.yml deleted file mode 100644 index a9af48e..0000000 --- a/ansible/roles/keycloak_init/tasks/user.yml +++ /dev/null @@ -1,10 +0,0 @@ ---- -- name: Ensure kcadm.sh is present - stat: - path: /opt/keycloak/bin/kcadm.sh - register: kcadm_path - -- name: Install unzip (required for kcadm) - apt: - name: unzip - state: present diff --git a/ansible/roles/podman_services/files/mailcow/.env.eample b/ansible/roles/podman_services/files/mailcow/.env.eample new file mode 100644 index 0000000..8d771f3 --- /dev/null +++ b/ansible/roles/podman_services/files/mailcow/.env.eample @@ -0,0 +1,20 @@ +# 🐮 Mailcow Sample Environment File + +# Host Settings +HOSTNAME=mail +DOMAINNAME=example.cmmc.local +MAILCOW_HOSTNAME=mail.example.cmmc.local + +# Admin Interface +MAILCOW_ADMIN_USER=admin +MAILCOW_ADMIN_PASSWORD=change_me_securely + +# Timezone +TZ=America/New_York + +# DNS Resolver +RESOLVER=9.9.9.9 + +# Let's Encrypt (n = disabled, y = enabled) +SKIP_LETS_ENCRYPT=y +LETS_ENCRYPT_EMAIL=it@example.com diff --git a/ansible/roles/podman_services/files/mailcow/podman_compose.yml b/ansible/roles/podman_services/files/mailcow/podman_compose.yml new file mode 100644 index 0000000..f622d30 --- /dev/null +++ b/ansible/roles/podman_services/files/mailcow/podman_compose.yml @@ -0,0 +1,52 @@ +version: '3.7' + +services: + redis: + image: redis:alpine + restart: always + volumes: + - redis-data:/data:Z + + mysql: + image: mariadb:10.5 + restart: always + environment: + - MYSQL_ROOT_PASSWORD=mailcowrootpass + - MYSQL_DATABASE=mailcow + - MYSQL_USER=mailcow + - MYSQL_PASSWORD=mailcowpass + volumes: + - mysql-data:/var/lib/mysql:Z + + dovecot: + image: mailcow/dovecot:latest + restart: always + volumes: + - vmail-data:/var/vmail:Z + - rspamd-data:/var/lib/rspamd:Z + environment: + - MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME} + + postfix: + image: mailcow/postfix:latest + restart: always + environment: + - MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME} + + nginx: + image: mailcow/nginx:latest + restart: always + ports: + - "8081:80" + - "8443:443" + volumes: + - nginx-certs:/etc/ssl/mail:Z + environment: + - MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME} + +volumes: + redis-data: + mysql-data: + vmail-data: + rspamd-data: + nginx-certs: diff --git a/ansible/roles/podman_services/tasks/keycloak.yml b/ansible/roles/podman_services/tasks/keycloak.yml deleted file mode 100644 index a14bb54..0000000 --- a/ansible/roles/podman_services/tasks/keycloak.yml +++ /dev/null @@ -1,38 +0,0 @@ ---- -- name: Deploy keycloak container with Podman - containers.podman.podman_container: - name: keycloak - image: "{{ keycloak_image }}" - state: started - restart_policy: always - volumes: - - "{{ keycloak_data_dir }}:/data:z" - env: - CONFIG_PATH: "/data/config" - -- name: Ensure systemd service is enabled for keycloak - copy: - dest: "/etc/systemd/system/podman-keycloak.service" - content: | - [Unit] - Description=Podman container for keycloak - Wants=network.target - After=network.target - - [Service] - ExecStart=/usr/bin/podman start -a keycloak - ExecStop=/usr/bin/podman stop -t 10 keycloak - Restart=always - - [Install] - WantedBy=multi-user.target - owner: root - group: root - mode: "0644" - -- name: Reload systemd and enable service for keycloak - systemd: - daemon_reload: yes - name: podman-keycloak.service - enabled: yes - state: started diff --git a/ansible/roles/podman_services/tasks/mailcow.yml b/ansible/roles/podman_services/tasks/mailcow.yml index 984ebc0..f9bc8a2 100644 --- a/ansible/roles/podman_services/tasks/mailcow.yml +++ b/ansible/roles/podman_services/tasks/mailcow.yml @@ -1,38 +1,39 @@ --- -- name: Deploy mailcow container with Podman - containers.podman.podman_container: - name: mailcow - image: "{{ mailcow_image }}" - state: started - restart_policy: always - volumes: - - "{{ mailcow_data_dir }}:/data:z" - env: - CONFIG_PATH: "/data/config" +- name: Ensure mailcow service directory exists + file: + path: "{{ service.compose_dir }}" + state: directory + mode: "0755" + owner: "{{ service.user | default('root') }}" + group: "{{ service.group | default('root') }}" -- name: Ensure systemd service is enabled for mailcow +- name: Deploy mailcow .env file from template + template: + src: mailcow/env.j2 + dest: "{{ service.compose_dir }}/.env" + owner: "{{ service.user | default('root') }}" + group: "{{ service.group | default('root') }}" + mode: "0600" + +- name: Copy mailcow podman-compose.yml copy: - dest: "/etc/systemd/system/podman-mailcow.service" - content: | - [Unit] - Description=Podman container for mailcow - Wants=network.target - After=network.target - - [Service] - ExecStart=/usr/bin/podman start -a mailcow - ExecStop=/usr/bin/podman stop -t 10 mailcow - Restart=always - - [Install] - WantedBy=multi-user.target - owner: root - group: root + src: mailcow/podman-compose.yml + dest: "{{ service.compose_dir }}/podman-compose.yml" + owner: "{{ service.user | default('root') }}" + group: "{{ service.group | default('root') }}" mode: "0644" -- name: Reload systemd and enable service for mailcow +- name: Copy mailcow systemd unit file + template: + src: mailcow/mailcow.service.j2 + dest: /etc/systemd/system/podman-mailcow.service + mode: "0644" + +- name: Reload systemd daemon + command: systemctl daemon-reexec + +- name: Enable and start mailcow container service systemd: - daemon_reload: yes name: podman-mailcow.service - enabled: yes + enabled: true state: started diff --git a/ansible/roles/podman_services/tasks/main.yml b/ansible/roles/podman_services/tasks/main.yml index a06490a..4a4f3eb 100644 --- a/ansible/roles/podman_services/tasks/main.yml +++ b/ansible/roles/podman_services/tasks/main.yml @@ -1,11 +1,24 @@ --- -- name: Ensure Podman is installed +- name: Ensure Podman and dependencies are installed apt: - name: podman + name: + - podman + - uidmap + - slirp4netns + - fuse-overlayfs state: present become: true -- name: Pull and run Podman services +- name: Confirm Podman version + command: podman --version + register: podman_version + changed_when: false + +- name: Debug Podman version for audit + debug: + msg: "Podman version is {{ podman_version.stdout }}" + +- name: Iterate over defined Podman services include_tasks: run_service.yml loop: "{{ podman_services }}" loop_control: diff --git a/ansible/roles/podman_services/tasks/run_service.yml b/ansible/roles/podman_services/tasks/run_service.yml index 5c25e82..95743cb 100644 --- a/ansible/roles/podman_services/tasks/run_service.yml +++ b/ansible/roles/podman_services/tasks/run_service.yml @@ -2,6 +2,11 @@ - name: Pull image for {{ service.name }} containers.podman.podman_image: name: "{{ service.image }}" + register: pulled_image + +- name: Debug pulled image result for {{ service.name }} + debug: + msg: "Image {{ service.image }} pulled: {{ pulled_image }}" - name: Create data directory for {{ service.name }} file: @@ -21,3 +26,8 @@ env: "{{ service.env | default({}) }}" volumes: "{{ service.volumes | default([]) }}" user: "{{ service.user | default(omit) }}" + register: podman_run + +- name: Debug container run status for {{ service.name }} + debug: + var: podman_run diff --git a/ansible/roles/podman_services/templates/mailcow/env.j2 b/ansible/roles/podman_services/templates/mailcow/env.j2 new file mode 100644 index 0000000..52bde7b --- /dev/null +++ b/ansible/roles/podman_services/templates/mailcow/env.j2 @@ -0,0 +1,20 @@ +# 🐮 Mailcow Environment File (Generated by Ansible) + +# Host Settings +HOSTNAME={{ mailcow_hostname }} +DOMAINNAME={{ mailcow_domain }} +MAILCOW_HOSTNAME={{ mailcow_fqdn }} + +# Admin Interface +MAILCOW_ADMIN_USER={{ mailcow_admin_user }} +MAILCOW_ADMIN_PASSWORD={{ mailcow_admin_password }} + +# Timezone +TZ={{ mailcow_timezone }} + +# DNS Resolver +RESOLVER=9.9.9.9 + +# Let's Encrypt +SKIP_LETS_ENCRYPT={{ 'n' if mailcow_use_letsencrypt == 'y' else 'y' }} +LETS_ENCRYPT_EMAIL={{ mailcow_letsencrypt_email }} diff --git a/ansible/roles/podman_services/templates/mailcow/mailcow.service.j2 b/ansible/roles/podman_services/templates/mailcow/mailcow.service.j2 new file mode 100644 index 0000000..3d6ce57 --- /dev/null +++ b/ansible/roles/podman_services/templates/mailcow/mailcow.service.j2 @@ -0,0 +1,15 @@ +[Unit] +Description=Mailcow Podman Compose Service +Wants=network.target +After=network.target + +[Service] +Type=oneshot +RemainAfterExit=yes +WorkingDirectory={{ service.compose_dir }} +ExecStart=/usr/bin/podman-compose -f {{ service.compose_dir }}/podman-compose.yml up -d +ExecStop=/usr/bin/podman-compose -f {{ service.compose_dir }}/podman-compose.yml down +TimeoutStartSec=0 + +[Install] +WantedBy=multi-user.target diff --git a/ansible/roles/podman_services/templates/mailcow/podman-compose.yml b/ansible/roles/podman_services/templates/mailcow/podman-compose.yml new file mode 100644 index 0000000..8756006 --- /dev/null +++ b/ansible/roles/podman_services/templates/mailcow/podman-compose.yml @@ -0,0 +1,28 @@ +version: "3.8" + +services: + mailcow: + image: mailcow/mailcow-dockerized:latest + container_name: mailcow + env_file: + - .env + restart: always + ports: + - "25:25" + - "80:80" + - "443:443" + - "587:587" + - "993:993" + volumes: + - mailcow_data:/var/lib/mailcow + - mailcow_conf:/etc/mailcow + networks: + - mailcow_net + +volumes: + mailcow_data: + mailcow_conf: + +networks: + mailcow_net: + driver: bridge