mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-08-17 17:26:42 +02:00
Solved bug existed due to difference between mailu domain and hostname difference. also refactored during this to find the bug
This commit is contained in:
parent
1bed83078e
commit
0de26fa6c7
19
filter_plugins/domain_tools.py
Normal file
19
filter_plugins/domain_tools.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# filter_plugins/domain_tools.py
|
||||||
|
# Returns the DNS zone (SLD.TLD) from a hostname.
|
||||||
|
# Pure-Python, no external deps; handles simple cases. For exotic TLDs use tldextract (see note).
|
||||||
|
from ansible.errors import AnsibleFilterError
|
||||||
|
|
||||||
|
def to_zone(hostname: str) -> str:
|
||||||
|
if not isinstance(hostname, str) or not hostname.strip():
|
||||||
|
raise AnsibleFilterError("to_zone: hostname must be a non-empty string")
|
||||||
|
parts = hostname.strip(".").split(".")
|
||||||
|
if len(parts) < 2:
|
||||||
|
raise AnsibleFilterError(f"to_zone: '{hostname}' has no TLD part")
|
||||||
|
# naive default: last two labels -> SLD.TLD
|
||||||
|
return ".".join(parts[-2:])
|
||||||
|
|
||||||
|
class FilterModule(object):
|
||||||
|
def filters(self):
|
||||||
|
return {
|
||||||
|
"to_zone": to_zone,
|
||||||
|
}
|
@ -44,7 +44,7 @@ defaults_networks:
|
|||||||
subnet: 192.168.102.0/28
|
subnet: 192.168.102.0/28
|
||||||
web-app-mailu:
|
web-app-mailu:
|
||||||
# Use one of the last container ips for dns resolving so that it isn't used
|
# Use one of the last container ips for dns resolving so that it isn't used
|
||||||
dns: 192.168.102.29
|
dns_resolver: 192.168.102.29
|
||||||
subnet: 192.168.102.16/28
|
subnet: 192.168.102.16/28
|
||||||
web-app-moodle:
|
web-app-moodle:
|
||||||
subnet: 192.168.102.32/28
|
subnet: 192.168.102.32/28
|
||||||
|
@ -8,37 +8,37 @@
|
|||||||
# @see https://en.wikipedia.org/wiki/OpenID_Connect
|
# @see https://en.wikipedia.org/wiki/OpenID_Connect
|
||||||
|
|
||||||
## Helper Variables:
|
## Helper Variables:
|
||||||
_oidc_client_realm: "{{ oidc.client.realm if oidc.client is defined and oidc.client.realm is defined else SOFTWARE_NAME | lower }}"
|
_oidc_client_realm: "{{ OIDC.CLIENT.ISSUER_URL if OIDC.CLIENT is defined and OIDC.CLIENT.ISSUER_URL is defined else SOFTWARE_NAME | lower }}"
|
||||||
_oidc_url: "{{
|
_oidc_url: "{{
|
||||||
(oidc.url
|
(OIDC.URL
|
||||||
if (oidc is defined and oidc.url is defined)
|
if (oidc is defined and OIDC.URL is defined)
|
||||||
else WEB_PROTOCOL ~ '://' ~ (domains | get_domain('web-app-keycloak'))
|
else WEB_PROTOCOL ~ '://' ~ (domains | get_domain('web-app-keycloak'))
|
||||||
)
|
)
|
||||||
}}"
|
}}"
|
||||||
_oidc_client_issuer_url: "{{ _oidc_url }}/realms/{{_oidc_client_realm}}"
|
_oidc_client_issuer_url: "{{ _oidc_url }}/realms/{{_oidc_client_realm}}"
|
||||||
_oidc_client_id: "{{ oidc.client.id if oidc.client is defined and oidc.client.id is defined else SOFTWARE_NAME | lower }}"
|
_oidc_client_id: "{{ OIDC.CLIENT.ID if OIDC.CLIENT is defined and OIDC.CLIENT.ID is defined else SOFTWARE_NAME | lower }}"
|
||||||
|
|
||||||
defaults_oidc:
|
defaults_oidc:
|
||||||
url: "{{ _oidc_url }}"
|
URL: "{{ _oidc_url }}"
|
||||||
client:
|
CLIENT:
|
||||||
id: "{{ _oidc_client_id }}" # Client identifier, typically matching your primary domain
|
ID: "{{ _oidc_client_id }}" # Client identifier, typically matching your primary domain
|
||||||
# secret: # Client secret for authenticating with the OIDC provider (set in the inventory file). Recommend greater then 32 characters
|
# secret: # Client secret for authenticating with the OIDC provider (set in the inventory file). Recommend greater then 32 characters
|
||||||
realm: "{{_oidc_client_realm}}" # The realm to which the client belongs in the OIDC provider
|
REALM: "{{_oidc_client_realm}}" # The realm to which the client belongs in the OIDC provider
|
||||||
issuer_url: "{{_oidc_client_issuer_url}}" # Base URL of the OIDC provider (issuer)
|
ISSUER_URL: "{{_oidc_client_issuer_url}}" # Base URL of the OIDC provider (issuer)
|
||||||
discovery_document: "{{_oidc_client_issuer_url}}/.well-known/openid-configuration" # URL for fetching the provider's configuration details
|
DISCOVERY_DOCUMENT: "{{_oidc_client_issuer_url}}/.well-known/openid-configuration" # URL for fetching the provider's configuration details
|
||||||
authorize_url: "{{_oidc_client_issuer_url}}/protocol/openid-connect/auth" # Endpoint to start the authorization process
|
AUTHORIZE_URL: "{{_oidc_client_issuer_url}}/protocol/openid-connect/auth" # Endpoint to start the authorization process
|
||||||
token_url: "{{_oidc_client_issuer_url}}/protocol/openid-connect/token" # Endpoint to exchange authorization codes for tokens (note: 'token_url' may be a typo for 'token_url')
|
TOKEN_URL: "{{_oidc_client_issuer_url}}/protocol/openid-connect/token" # Endpoint to exchange authorization codes for tokens (note: 'token_url' may be a typo for 'token_url')
|
||||||
user_info_url: "{{_oidc_client_issuer_url}}/protocol/openid-connect/userinfo" # Endpoint to retrieve user information
|
USER_INFO_URL: "{{_oidc_client_issuer_url}}/protocol/openid-connect/userinfo" # Endpoint to retrieve user information
|
||||||
logout_url: "{{_oidc_client_issuer_url}}/protocol/openid-connect/logout" # Endpoint to log out the user
|
LOGOUT_URL: "{{_oidc_client_issuer_url}}/protocol/openid-connect/logout" # Endpoint to log out the user
|
||||||
change_credentials: "{{_oidc_client_issuer_url}}account/account-security/signing-in" # URL for managing or changing user credentials
|
CHANGE_CREDENTIALS: "{{_oidc_client_issuer_url}}account/account-security/signing-in" # URL for managing or changing user credentials
|
||||||
certs: "{{_oidc_client_issuer_url}}/protocol/openid-connect/certs" # JSON Web Key Set (JWKS)
|
CERTS: "{{_oidc_client_issuer_url}}/protocol/openid-connect/certs" # JSON Web Key Set (JWKS)
|
||||||
reset_credentials: "{{_oidc_client_issuer_url}}/login-actions/reset-credentials?client_id={{ _oidc_client_id }}" # Password reset url
|
RESET_CREDENTIALS: "{{_oidc_client_issuer_url}}/login-actions/reset-credentials?client_id={{ _oidc_client_id }}" # Password reset url
|
||||||
button_text: "SSO Login ({{ PRIMARY_DOMAIN | upper }})" # Default button text
|
BUTTON_TEXT: "SSO Login ({{ PRIMARY_DOMAIN | upper }})" # Default button text
|
||||||
attributes:
|
ATTRIBUTES:
|
||||||
# Attribut to identify the user
|
# Attribut to identify the user
|
||||||
username: "preferred_username"
|
USERNAME: "preferred_username"
|
||||||
given_name: "givenName"
|
GIVEN_NAME: "givenName"
|
||||||
family_name: "surname"
|
FAMILY_NAME: "surname"
|
||||||
email: "email"
|
EMAIL: "email"
|
||||||
claims:
|
CLAIMS:
|
||||||
groups: "groups"
|
GROUPS: "groups"
|
||||||
|
@ -30,4 +30,4 @@ defaults_service_provider:
|
|||||||
legal:
|
legal:
|
||||||
editorial_responsible: "Johannes Gutenberg"
|
editorial_responsible: "Johannes Gutenberg"
|
||||||
source_code: "https://s.{{ SOFTWARE_NAME | lower }}/code"
|
source_code: "https://s.{{ SOFTWARE_NAME | lower }}/code"
|
||||||
imprint: "{{WEB_PROTOCOL}}://{{ domains | get_domain('web-svc-html') }}/imprint.html"
|
imprint: "{{ domains | get_url('web-svc-html', WEB_PROTOCOL) }}/imprint.html"
|
@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Keyboard Color Service
|
Description=Keyboard Color Service
|
||||||
OnFailure=sys-alm-compose.infinito@%n.service
|
OnFailure=sys-alm-compose.{{ SOFTWARE_NAME }}@%n.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
# Nginx Docker Cert Deploy Role
|
|
||||||
|
|
||||||
🎉 **Author**: [Kevin Veen-Birkenbach](https://www.veen.world)
|
|
||||||
|
|
||||||
This Ansible role simplifies the deployment of **Let's Encrypt certificates** into **Docker Compose** setups with Nginx. It supports both **individual certificates per subdomain** and a **single wildcard certificate** for all subdomains.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚀 **Features**
|
|
||||||
- Automatically deploys **Let's Encrypt certificates** to Docker Compose setups.
|
|
||||||
- Supports both **single-domain certificates** and **one wildcard certificate** for all subdomains.
|
|
||||||
- **Copies certificates** to the target directory inside the container.
|
|
||||||
- Automatically **reloads or restarts Nginx services** when certificates are updated.
|
|
||||||
- **Configures and manages a `systemd` service** for automated certificate deployment.
|
|
||||||
- **Includes a `systemd` timer** for scheduled renewals.
|
|
||||||
- **Handles dependent services** like `sys-alm-compose`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔧 **Tasks Overview**
|
|
||||||
|
|
||||||
### **1️⃣ Main Tasks**
|
|
||||||
1. **Add Deployment Script**
|
|
||||||
- Copies `srv-proxy-6-6-tls-deploy.sh` to the administrator scripts directory.
|
|
||||||
|
|
||||||
2. **Create Certificate Directory**
|
|
||||||
- Ensures `cert_mount_directory` exists with proper permissions.
|
|
||||||
|
|
||||||
3. **Configure `systemd` Service**
|
|
||||||
- Deploys a `systemd` service file for the deployment process.
|
|
||||||
|
|
||||||
4. **Include `sys-timer` Role**
|
|
||||||
- Schedules automatic certificate deployment using a `systemd` timer.
|
|
||||||
|
|
||||||
### **2️⃣ Handlers**
|
|
||||||
- **Restart Nginx Service**
|
|
||||||
- Restarts `srv-proxy-6-6-tls-deploy` whenever a certificate update occurs.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## **🔧 Deploying Certificates into Docker Containers**
|
|
||||||
The role **automates copying certificates** into Docker Compose setups.
|
|
||||||
|
|
||||||
### **1️⃣ Deployment Script (`srv-proxy-6-6-tls-deploy.sh`)**
|
|
||||||
This script:
|
|
||||||
- **Copies certificates** to the correct container directory.
|
|
||||||
- **Reloads Nginx** inside all running containers.
|
|
||||||
- **Restarts containers if needed**.
|
|
||||||
|
|
||||||
**Usage:**
|
|
||||||
```sh
|
|
||||||
sh srv-proxy-6-6-tls-deploy.sh PRIMARY_DOMAIN /path/to/docker/compose
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 **Summary**
|
|
||||||
| Feature | Description |
|
|
||||||
|---------|------------|
|
|
||||||
| **Single-domain & wildcard support** | Use individual certs or a wildcard certificate |
|
|
||||||
| **Automated renewal** | Cronjob or systemd timer ensures auto-renewals |
|
|
||||||
| **Docker-ready** | Deploys certificates directly into Docker containers |
|
|
||||||
| **Supports Nginx & Mailu** | Compatible with multiple services |
|
|
||||||
| **Systemd integration** | Automates deployment via `systemd` |
|
|
||||||
|
|
||||||
🚀 **Now your Nginx setup is fully automated and secured with Let's Encrypt!** 🎉
|
|
||||||
```
|
|
@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
- name: "restart srv-proxy-6-6-tls-deploy service"
|
|
||||||
systemd:
|
|
||||||
name: srv-proxy-6-6-tls-deploy.{{ application_id }}{{ SYS_SERVICE_SUFFIX }}
|
|
||||||
state: restarted
|
|
||||||
enabled: yes
|
|
||||||
daemon_reload: yes
|
|
@ -1,10 +0,0 @@
|
|||||||
- name: Include dependency 'sys-alm-compose'
|
|
||||||
include_role:
|
|
||||||
name: sys-alm-compose
|
|
||||||
when: run_once_sys_alm_compose is not defined
|
|
||||||
|
|
||||||
- name: add srv-proxy-6-6-tls-deploy.sh
|
|
||||||
template:
|
|
||||||
src: "srv-proxy-6-6-tls-deploy.sh.j2"
|
|
||||||
dest: "{{nginx_docker_cert_deploy_script}}"
|
|
||||||
notify: restart srv-proxy-6-6-tls-deploy service
|
|
@ -1,27 +0,0 @@
|
|||||||
- block:
|
|
||||||
- include_tasks: 01_core.yml
|
|
||||||
- set_fact:
|
|
||||||
run_once_srv_proxy_6_6_tls_deploy: true
|
|
||||||
when: run_once_srv_proxy_6_6_tls_deploy is not defined
|
|
||||||
|
|
||||||
- name: "create {{cert_mount_directory}}"
|
|
||||||
file:
|
|
||||||
path: "{{cert_mount_directory}}"
|
|
||||||
state: directory
|
|
||||||
mode: "0755"
|
|
||||||
notify: restart srv-proxy-6-6-tls-deploy service
|
|
||||||
|
|
||||||
- name: configure srv-proxy-6-6-tls-deploy service
|
|
||||||
template:
|
|
||||||
src: "srv-proxy-6-6-tls-deploy.service.j2"
|
|
||||||
dest: "/etc/systemd/system/srv-proxy-6-6-tls-deploy.{{ application_id }}{{ SYS_SERVICE_SUFFIX }}"
|
|
||||||
notify: restart srv-proxy-6-6-tls-deploy service
|
|
||||||
|
|
||||||
- name: "include role for sys-timer for {{ service_name }}"
|
|
||||||
include_role:
|
|
||||||
name: sys-timer
|
|
||||||
vars:
|
|
||||||
on_calendar: "{{on_calendar_deploy_certificates}}"
|
|
||||||
service_name: "srv-proxy-6-6-tls-deploy.{{ application_id }}"
|
|
||||||
persistent: "true"
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Let's Encrypt deploy to {{ docker_compose.directories.instance }}
|
|
||||||
OnFailure=sys-alm-compose.infinito@%n.service
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=oneshot
|
|
||||||
ExecStart=/usr/bin/bash {{ PATH_ADMINISTRATOR_SCRIPTS }}/srv-proxy-6-6-tls-deploy.sh {{ssl_cert_folder}} {{ docker_compose.directories.instance }}
|
|
@ -1 +0,0 @@
|
|||||||
nginx_docker_cert_deploy_script: "{{ PATH_ADMINISTRATOR_SCRIPTS }}srv-proxy-6-6-tls-deploy.sh"
|
|
@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Let's Encrypt renewal
|
Description=Let's Encrypt renewal
|
||||||
OnFailure=sys-alm-compose.infinito@%n.service
|
OnFailure=sys-alm-compose.{{ SOFTWARE_NAME }}@%n.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Backup to USB when mounted to {{ backup_to_usb_mount }}
|
Description=Backup to USB when mounted to {{ backup_to_usb_mount }}
|
||||||
Wants={{systemctl_mount_service_name}}
|
Wants={{systemctl_mount_service_name}}
|
||||||
OnFailure=sys-alm-compose.infinito@%n.service
|
OnFailure=sys-alm-compose.{{ SOFTWARE_NAME }}@%n.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=pull remote backups
|
Description=pull remote backups
|
||||||
OnFailure=sys-alm-compose.infinito@%n.service sys-cln-faild-bkps{{ SYS_SERVICE_SUFFIX }}
|
OnFailure=sys-alm-compose.{{ SOFTWARE_NAME }}@%n.service sys-cln-faild-bkps{{ SYS_SERVICE_SUFFIX }}
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Optimize storage paths
|
Description=Optimize storage paths
|
||||||
OnFailure=sys-alm-compose.infinito@%n.service
|
OnFailure=sys-alm-compose.{{ SOFTWARE_NAME }}@%n.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=backup all docker volumes to local folder
|
Description=backup all docker volumes to local folder
|
||||||
OnFailure=sys-alm-compose.infinito@%n.service sys-cln-faild-bkps{{ SYS_SERVICE_SUFFIX }}
|
OnFailure=sys-alm-compose.{{ SOFTWARE_NAME }}@%n.service sys-cln-faild-bkps{{ SYS_SERVICE_SUFFIX }}
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=backup docker volumes to local folder
|
Description=backup docker volumes to local folder
|
||||||
OnFailure=sys-alm-compose.infinito@%n.service sys-cln-faild-bkps{{ SYS_SERVICE_SUFFIX }}
|
OnFailure=sys-alm-compose.{{ SOFTWARE_NAME }}@%n.service sys-cln-faild-bkps{{ SYS_SERVICE_SUFFIX }}
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=delete old backups
|
Description=delete old backups
|
||||||
OnFailure=sys-alm-compose.infinito@%n.service
|
OnFailure=sys-alm-compose.{{ SOFTWARE_NAME }}@%n.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Detect, revoke, and delete unused Let's Encrypt certificates based on active NGINX configuration files.
|
Description=Detect, revoke, and delete unused Let's Encrypt certificates based on active NGINX configuration files.
|
||||||
OnFailure=sys-alm-compose.infinito@%n.service
|
OnFailure=sys-alm-compose.{{ SOFTWARE_NAME }}@%n.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=freeing disc space
|
Description=freeing disc space
|
||||||
OnFailure=sys-alm-compose.infinito@%n.service
|
OnFailure=sys-alm-compose.{{ SOFTWARE_NAME }}@%n.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Cleaning up failed docker volume backups
|
Description=Cleaning up failed docker volume backups
|
||||||
OnFailure=sys-alm-compose.infinito@%n.service
|
OnFailure=sys-alm-compose.{{ SOFTWARE_NAME }}@%n.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Check btrfs status
|
Description=Check btrfs status
|
||||||
OnFailure=sys-alm-compose.infinito@%n.service
|
OnFailure=sys-alm-compose.{{ SOFTWARE_NAME }}@%n.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Check for CSP-blocked resources via Puppeteer
|
Description=Check for CSP-blocked resources via Puppeteer
|
||||||
OnFailure=sys-alm-compose.infinito@%n.service
|
OnFailure=sys-alm-compose.{{ SOFTWARE_NAME }}@%n.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=checking disc space
|
Description=checking disc space
|
||||||
OnFailure=sys-alm-compose.infinito@%n.service
|
OnFailure=sys-alm-compose.{{ SOFTWARE_NAME }}@%n.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Checking docker health
|
Description=Checking docker health
|
||||||
OnFailure=sys-alm-compose.infinito@%n.service
|
OnFailure=sys-alm-compose.{{ SOFTWARE_NAME }}@%n.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Checking docker health
|
Description=Checking docker health
|
||||||
OnFailure=sys-alm-compose.infinito@%n.service
|
OnFailure=sys-alm-compose.{{ SOFTWARE_NAME }}@%n.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=checking journalctl health
|
Description=checking journalctl health
|
||||||
OnFailure=sys-alm-compose.infinito@%n.service
|
OnFailure=sys-alm-compose.{{ SOFTWARE_NAME }}@%n.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Check msmtp liveliness
|
Description=Check msmtp liveliness
|
||||||
OnFailure=sys-alm-telegram.infinito@%n.service
|
OnFailure=sys-alm-telegram.{{ SOFTWARE_NAME }}@%n.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Check nginx configuration status
|
Description=Check nginx configuration status
|
||||||
OnFailure=sys-alm-compose.infinito@%n.service
|
OnFailure=sys-alm-compose.{{ SOFTWARE_NAME }}@%n.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=auto balance btrfs
|
Description=auto balance btrfs
|
||||||
OnFailure=sys-alm-compose.infinito@%n.service
|
OnFailure=sys-alm-compose.{{ SOFTWARE_NAME }}@%n.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Restart Docker Instances
|
Description=Restart Docker Instances
|
||||||
OnFailure=sys-alm-compose.infinito@%n.service
|
OnFailure=sys-alm-compose.{{ SOFTWARE_NAME }}@%n.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=restart unhealthy docker containers
|
Description=restart unhealthy docker containers
|
||||||
OnFailure=sys-alm-compose.infinito@%n.service
|
OnFailure=sys-alm-compose.{{ SOFTWARE_NAME }}@%n.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
initLogoutPatch(
|
initLogoutPatch(
|
||||||
'{{ oidc.client.logout_url }}',
|
'{{ OIDC.CLIENT.LOGOUT_URL }}',
|
||||||
'{{ WEB_PROTOCOL }}',
|
'{{ WEB_PROTOCOL }}',
|
||||||
'{{ PRIMARY_DOMAIN }}'
|
'{{ PRIMARY_DOMAIN }}'
|
||||||
);
|
);
|
||||||
|
18
roles/sys-svc-cert-sync-docker/README.md
Normal file
18
roles/sys-svc-cert-sync-docker/README.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Docker Compose Certificate Sync Service
|
||||||
|
|
||||||
|
## Description
|
||||||
|
Keeps Docker Compose services updated with fresh Let’s Encrypt certificates via a systemd oneshot service and timer.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
Installs a small script and a systemd unit that copy certificates into your Compose project and trigger an Nginx hot-reload (fallback: restart) to minimize downtime.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- Automatic certificate sync into the Compose project
|
||||||
|
- Mailu-friendly filenames (`key.pem`, `cert.pem`)
|
||||||
|
- Nginx hot-reload if available, otherwise restart
|
||||||
|
- Runs on a schedule you define
|
||||||
|
|
||||||
|
## Further Resources
|
||||||
|
- [Wildcard Certificate Setup (SETUP.md)](./SETUP.md)
|
||||||
|
- [Role Documentation](https://s.infinito.nexus/code/tree/main/roles/sys-svc-cert-sync-docker)
|
||||||
|
- [Issue Tracker](https://s.infinito.nexus/issues)
|
@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
# Check if the necessary parameters are provided
|
# Check if the necessary parameters are provided
|
||||||
if [ "$#" -ne 2 ]; then
|
if [ "$#" -ne 3 ]; then
|
||||||
echo "Usage: $0 <ssl_cert_folder> <docker_compose_instance_directory>"
|
echo "Usage: $0 <ssl_cert_folder> <docker_compose_instance_directory>"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@ -9,14 +9,15 @@ fi
|
|||||||
# Assign parameters
|
# Assign parameters
|
||||||
ssl_cert_folder="$1"
|
ssl_cert_folder="$1"
|
||||||
docker_compose_instance_directory="$2"
|
docker_compose_instance_directory="$2"
|
||||||
|
letsencrypt_live_path="$3"
|
||||||
docker_compose_cert_directory="$docker_compose_instance_directory/volumes/certs"
|
docker_compose_cert_directory="$docker_compose_instance_directory/volumes/certs"
|
||||||
|
|
||||||
# Copy certificates
|
# Copy certificates
|
||||||
cp -RvL "{{ LETSENCRYPT_LIVE_PATH }}/$ssl_cert_folder/"* "$docker_compose_cert_directory" || exit 1
|
cp -RvL "$letsencrypt_live_path/$ssl_cert_folder/"* "$docker_compose_cert_directory" || exit 1
|
||||||
|
|
||||||
# This code is optimized for mailu
|
# This code is optimized for mailu
|
||||||
cp -v "{{ LETSENCRYPT_LIVE_PATH }}/$ssl_cert_folder/privkey.pem" "$docker_compose_cert_directory/key.pem" || exit 1
|
cp -v "$letsencrypt_live_path/$ssl_cert_folder/privkey.pem" "$docker_compose_cert_directory/key.pem" || exit 1
|
||||||
cp -v "{{ LETSENCRYPT_LIVE_PATH }}/$ssl_cert_folder/fullchain.pem" "$docker_compose_cert_directory/cert.pem" || exit 1
|
cp -v "$letsencrypt_live_path/$ssl_cert_folder/fullchain.pem" "$docker_compose_cert_directory/cert.pem" || exit 1
|
||||||
|
|
||||||
# Set correct reading rights
|
# Set correct reading rights
|
||||||
chmod a+r -v "$docker_compose_cert_directory/"*
|
chmod a+r -v "$docker_compose_cert_directory/"*
|
7
roles/sys-svc-cert-sync-docker/handlers/main.yml
Normal file
7
roles/sys-svc-cert-sync-docker/handlers/main.yml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
- name: "restart tls deploy to docker service"
|
||||||
|
systemd:
|
||||||
|
name: "{{ CERT_SYNC_DOCKER_SERVICE_NAME }}"
|
||||||
|
state: restarted
|
||||||
|
enabled: yes
|
||||||
|
daemon_reload: yes
|
@ -14,7 +14,7 @@ galaxy_info:
|
|||||||
- systemd
|
- systemd
|
||||||
repository: "https://s.infinito.nexus/code"
|
repository: "https://s.infinito.nexus/code"
|
||||||
issue_tracker_url: "https://s.infinito.nexus/issues"
|
issue_tracker_url: "https://s.infinito.nexus/issues"
|
||||||
documentation: "https://s.infinito.nexus/code/tree/main/roles/srv-proxy-6-6-tls-deploy"
|
documentation: "https://s.infinito.nexus/code/tree/main/roles/sys-svc-cert-sync-docker"
|
||||||
min_ansible_version: "2.9"
|
min_ansible_version: "2.9"
|
||||||
platforms:
|
platforms:
|
||||||
- name: Any
|
- name: Any
|
13
roles/sys-svc-cert-sync-docker/tasks/01_core.yml
Normal file
13
roles/sys-svc-cert-sync-docker/tasks/01_core.yml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
- name: Include dependency 'sys-alm-compose'
|
||||||
|
include_role:
|
||||||
|
name: sys-alm-compose
|
||||||
|
when: run_once_sys_alm_compose is not defined
|
||||||
|
|
||||||
|
- name: "Install '{{ CERT_SYNC_DOCKER_SCRIPT_FILE }}'"
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: "{{ CERT_SYNC_DOCKER_SCRIPT_FILE }}"
|
||||||
|
dest: "{{ CERT_SYNC_DOCKER_SCRIPT_PATH }}"
|
||||||
|
mode: "0755"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
notify: restart tls deploy to docker service
|
27
roles/sys-svc-cert-sync-docker/tasks/main.yml
Normal file
27
roles/sys-svc-cert-sync-docker/tasks/main.yml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
- block:
|
||||||
|
- include_tasks: 01_core.yml
|
||||||
|
- set_fact:
|
||||||
|
run_once_sys_svc_cert_sync_docker: true
|
||||||
|
when: run_once_sys_svc_cert_sync_docker is not defined
|
||||||
|
|
||||||
|
- name: "create {{ cert_mount_directory }}"
|
||||||
|
file:
|
||||||
|
path: "{{ cert_mount_directory }}"
|
||||||
|
state: directory
|
||||||
|
mode: "0755"
|
||||||
|
notify: restart tls deploy to docker service
|
||||||
|
|
||||||
|
- name: configure sys-svc-cert-sync-docker service
|
||||||
|
template:
|
||||||
|
src: "sys-svc-cert-sync-docker.service.j2"
|
||||||
|
dest: "/etc/systemd/system/{{ CERT_SYNC_DOCKER_SERVICE_NAME }}"
|
||||||
|
notify: restart tls deploy to docker service
|
||||||
|
|
||||||
|
- name: "include role for sys-timer for {{ service_name }}"
|
||||||
|
include_role:
|
||||||
|
name: sys-timer
|
||||||
|
vars:
|
||||||
|
on_calendar: "{{ on_calendar_deploy_certificates }}"
|
||||||
|
service_name: "{{ CERT_SYNC_DOCKER_SERVICE_NAME }}"
|
||||||
|
persistent: "true"
|
||||||
|
|
@ -0,0 +1,7 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Let's Encrypt deploy to {{ docker_compose.directories.instance }}
|
||||||
|
OnFailure=sys-alm-compose.{{ SOFTWARE_NAME }}@%n.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart={{ PATH_ADMINISTRATOR_SCRIPTS }}/{{ CERT_SYNC_DOCKER_SCRIPT_FILE }} {{ ssl_cert_folder }} {{ docker_compose.directories.instance }} {{ LETSENCRYPT_LIVE_PATH }}
|
3
roles/sys-svc-cert-sync-docker/vars/main.yml
Normal file
3
roles/sys-svc-cert-sync-docker/vars/main.yml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
CERT_SYNC_DOCKER_SCRIPT_FILE: "sys-svc-cert-sync-docker.sh"
|
||||||
|
CERT_SYNC_DOCKER_SCRIPT_PATH: "{{ PATH_ADMINISTRATOR_SCRIPTS }}{{ CERT_SYNC_DOCKER_SCRIPT_FILE }}"
|
||||||
|
CERT_SYNC_DOCKER_SERVICE_NAME: "sys-svc-cert-sync-docker.{{ application_id }}{{ SYS_SERVICE_SUFFIX }}"
|
@ -1,6 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Updates Docker Instances
|
Description=Updates Docker Instances
|
||||||
OnFailure=sys-alm-compose.infinito@%n.service
|
OnFailure=sys-alm-compose.{{ SOFTWARE_NAME }}@%n.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
@ -287,9 +287,9 @@ DEFAULT_REGISTRATION=invite
|
|||||||
### EXTERNAL AUTHENTICATION METHODS
|
### EXTERNAL AUTHENTICATION METHODS
|
||||||
# @See https://docs.bigbluebutton.org/greenlight/v3/external-authentication/
|
# @See https://docs.bigbluebutton.org/greenlight/v3/external-authentication/
|
||||||
#
|
#
|
||||||
OPENID_CONNECT_CLIENT_ID={{ oidc.client.id }}
|
OPENID_CONNECT_CLIENT_ID={{ OIDC.CLIENT.ID }}
|
||||||
OPENID_CONNECT_CLIENT_SECRET={{ oidc.client.secret }}
|
OPENID_CONNECT_CLIENT_SECRET={{ OIDC.CLIENT.SECRET }}
|
||||||
OPENID_CONNECT_ISSUER={{ oidc.client.issuer_url }}
|
OPENID_CONNECT_ISSUER={{ OIDC.CLIENT.ISSUER_URL }}
|
||||||
OPENID_CONNECT_REDIRECT={{ domains | get_url(application_id, WEB_PROTOCOL) }}
|
OPENID_CONNECT_REDIRECT={{ domains | get_url(application_id, WEB_PROTOCOL) }}
|
||||||
# OPENID_CONNECT_UID_FIELD=sub default
|
# OPENID_CONNECT_UID_FIELD=sub default
|
||||||
{% endif %}
|
{% endif %}
|
@ -136,9 +136,9 @@ run:
|
|||||||
|
|
||||||
# OIDC Activation
|
# OIDC Activation
|
||||||
- exec: rails r "SiteSetting.openid_connect_enabled = true"
|
- exec: rails r "SiteSetting.openid_connect_enabled = true"
|
||||||
- exec: rails r "SiteSetting.openid_connect_discovery_document = '{{oidc.client.discovery_document}}'"
|
- exec: rails r "SiteSetting.openid_connect_discovery_document = '{{OIDC.CLIENT.DISCOVERY_DOCUMENT}}'"
|
||||||
- exec: rails r "SiteSetting.openid_connect_client_id = '{{ oidc.client.id }}'"
|
- exec: rails r "SiteSetting.openid_connect_client_id = '{{ OIDC.CLIENT.ID }}'"
|
||||||
- exec: rails r "SiteSetting.openid_connect_client_secret = '{{ oidc.client.secret }}'"
|
- exec: rails r "SiteSetting.openid_connect_client_secret = '{{ OIDC.CLIENT.SECRET }}'"
|
||||||
- exec: rails r "SiteSetting.openid_connect_rp_initiated_logout_redirect = 'https://{{ domains | get_domain(application_id) }}'"
|
- exec: rails r "SiteSetting.openid_connect_rp_initiated_logout_redirect = 'https://{{ domains | get_domain(application_id) }}'"
|
||||||
- exec: rails r "SiteSetting.openid_connect_allow_association_change = false"
|
- exec: rails r "SiteSetting.openid_connect_allow_association_change = false"
|
||||||
- exec: rails r "SiteSetting.openid_connect_rp_initiated_logout = true"
|
- exec: rails r "SiteSetting.openid_connect_rp_initiated_logout = true"
|
||||||
|
@ -86,20 +86,20 @@ ESPOCRM_CONFIG_OIDC_ALLOW_ADMIN_USER=true
|
|||||||
ESPOCRM_CONFIG_AUTHENTICATION_METHOD=Oidc
|
ESPOCRM_CONFIG_AUTHENTICATION_METHOD=Oidc
|
||||||
ESPOCRM_CONFIG_OIDC_FALLBACK=false # set true if you want LDAP as fallback
|
ESPOCRM_CONFIG_OIDC_FALLBACK=false # set true if you want LDAP as fallback
|
||||||
|
|
||||||
ESPOCRM_CONFIG_OIDC_CLIENT_ID={{ oidc.client.id }}
|
ESPOCRM_CONFIG_OIDC_CLIENT_ID={{ OIDC.CLIENT.ID }}
|
||||||
ESPOCRM_CONFIG_OIDC_CLIENT_SECRET={{ oidc.client.secret }}
|
ESPOCRM_CONFIG_OIDC_CLIENT_SECRET={{ OIDC.CLIENT.SECRET }}
|
||||||
|
|
||||||
ESPOCRM_CONFIG_OIDC_AUTHORIZATION_ENDPOINT={{ oidc.client.authorize_url }}
|
ESPOCRM_CONFIG_OIDC_AUTHORIZATION_ENDPOINT={{ OIDC.CLIENT.AUTHORIZE_URL }}
|
||||||
ESPOCRM_CONFIG_OIDC_TOKEN_ENDPOINT={{ oidc.client.token_url }}
|
ESPOCRM_CONFIG_OIDC_TOKEN_ENDPOINT={{ OIDC.CLIENT.TOKEN_URL }}
|
||||||
ESPOCRM_CONFIG_OIDC_USER_INFO_ENDPOINT={{ oidc.client.user_info_url }}
|
ESPOCRM_CONFIG_OIDC_USER_INFO_ENDPOINT={{ OIDC.CLIENT.USER_INFO_URL }}
|
||||||
ESPOCRM_CONFIG_OIDC_JWKS_ENDPOINT={{ oidc.client.certs }}
|
ESPOCRM_CONFIG_OIDC_JWKS_ENDPOINT={{ OIDC.CLIENT.CERTS }}
|
||||||
|
|
||||||
ESPOCRM_CONFIG_OIDC_AUTHORIZATION_REDIRECT_URI={{ espocrm_url }}/oidc/callback
|
ESPOCRM_CONFIG_OIDC_AUTHORIZATION_REDIRECT_URI={{ espocrm_url }}/oidc/callback
|
||||||
#ESPOCRM_CONFIG_OIDC_SCOPES=openid,profile,email # Defined in main.yml
|
#ESPOCRM_CONFIG_OIDC_SCOPES=openid,profile,email # Defined in main.yml
|
||||||
|
|
||||||
ESPOCRM_CONFIG_OIDC_CREATE_USER=true
|
ESPOCRM_CONFIG_OIDC_CREATE_USER=true
|
||||||
ESPOCRM_CONFIG_OIDC_SYNC=true
|
ESPOCRM_CONFIG_OIDC_SYNC=true
|
||||||
ESPOCRM_CONFIG_OIDC_USERNAME_CLAIM={{oidc.attributes.username}}
|
ESPOCRM_CONFIG_OIDC_USERNAME_CLAIM={{OIDC.ATTRIBUTES.USERNAME}}
|
||||||
# ESPOCRM_CONFIG_OIDC_SYNC_TEAMS=true
|
# ESPOCRM_CONFIG_OIDC_SYNC_TEAMS=true
|
||||||
# ESPOCRM_CONFIG_OIDC_GROUP_CLAIM=group
|
# ESPOCRM_CONFIG_OIDC_GROUP_CLAIM=group
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
shell: |
|
shell: |
|
||||||
docker exec -i --user {{ gitea_user }} {{ gitea_container }} \
|
docker exec -i --user {{ gitea_user }} {{ gitea_container }} \
|
||||||
gitea admin auth list \
|
gitea admin auth list \
|
||||||
| awk -v name="{{ oidc.button_text }}" '$0 ~ name {print $1; exit}'
|
| awk -v name="{{ OIDC.BUTTON_TEXT }}" '$0 ~ name {print $1; exit}'
|
||||||
args:
|
args:
|
||||||
chdir: "{{ docker_compose.directories.instance }}"
|
chdir: "{{ docker_compose.directories.instance }}"
|
||||||
register: oidc_source_id_raw
|
register: oidc_source_id_raw
|
||||||
|
@ -3,10 +3,10 @@
|
|||||||
docker exec -i --user {{ gitea_user }} {{ gitea_container }} \
|
docker exec -i --user {{ gitea_user }} {{ gitea_container }} \
|
||||||
gitea admin auth add-oauth \
|
gitea admin auth add-oauth \
|
||||||
--provider openidConnect \
|
--provider openidConnect \
|
||||||
--name "{{ oidc.button_text }}" \
|
--name "{{ OIDC.BUTTON_TEXT }}" \
|
||||||
--key "{{ oidc.client.id }}" \
|
--key "{{ OIDC.CLIENT.ID }}" \
|
||||||
--secret "{{ oidc.client.secret }}" \
|
--secret "{{ OIDC.CLIENT.SECRET }}" \
|
||||||
--auto-discover-url "{{ oidc.client.discovery_document }}" \
|
--auto-discover-url "{{ OIDC.CLIENT.DISCOVERY_DOCUMENT }}" \
|
||||||
--scopes "openid profile email"
|
--scopes "openid profile email"
|
||||||
args:
|
args:
|
||||||
chdir: "{{ docker_compose.directories.instance }}"
|
chdir: "{{ docker_compose.directories.instance }}"
|
||||||
@ -18,7 +18,7 @@
|
|||||||
docker exec -i --user {{ gitea_user }} {{ gitea_container }} \
|
docker exec -i --user {{ gitea_user }} {{ gitea_container }} \
|
||||||
/app/gitea/gitea admin auth list \
|
/app/gitea/gitea admin auth list \
|
||||||
| tail -n +2 \
|
| tail -n +2 \
|
||||||
| grep -F "{{ oidc.button_text }}" \
|
| grep -F "{{ OIDC.BUTTON_TEXT }}" \
|
||||||
| awk '{print $1; exit}'
|
| awk '{print $1; exit}'
|
||||||
args:
|
args:
|
||||||
chdir: "{{ docker_compose.directories.instance }}"
|
chdir: "{{ docker_compose.directories.instance }}"
|
||||||
@ -38,10 +38,10 @@
|
|||||||
gitea admin auth update-oauth \
|
gitea admin auth update-oauth \
|
||||||
--id {{ oidc_source_id }}\
|
--id {{ oidc_source_id }}\
|
||||||
--provider openidConnect \
|
--provider openidConnect \
|
||||||
--name "{{ oidc.button_text }}" \
|
--name "{{ OIDC.BUTTON_TEXT }}" \
|
||||||
--key "{{ oidc.client.id }}" \
|
--key "{{ OIDC.CLIENT.ID }}" \
|
||||||
--secret "{{ oidc.client.secret }}" \
|
--secret "{{ OIDC.CLIENT.SECRET }}" \
|
||||||
--auto-discover-url "{{ oidc.client.discovery_document }}" \
|
--auto-discover-url "{{ OIDC.CLIENT.DISCOVERY_DOCUMENT }}" \
|
||||||
--scopes "openid profile email"
|
--scopes "openid profile email"
|
||||||
args:
|
args:
|
||||||
chdir: "{{ docker_compose.directories.instance }}"
|
chdir: "{{ docker_compose.directories.instance }}"
|
||||||
|
@ -832,7 +832,7 @@
|
|||||||
"enabled": true,
|
"enabled": true,
|
||||||
"alwaysDisplayInConsole": false,
|
"alwaysDisplayInConsole": false,
|
||||||
"clientAuthenticatorType": "desktop-secret",
|
"clientAuthenticatorType": "desktop-secret",
|
||||||
"secret": "{{ oidc.client.secret }}",
|
"secret": "{{ OIDC.CLIENT.SECRET }}",
|
||||||
{# The following line should be covered by 02_update_client_redirects.yml #}
|
{# The following line should be covered by 02_update_client_redirects.yml #}
|
||||||
"redirectUris": {{ domains | redirect_uris(applications, WEB_PROTOCOL) | tojson }},
|
"redirectUris": {{ domains | redirect_uris(applications, WEB_PROTOCOL) | tojson }},
|
||||||
"webOrigins": [
|
"webOrigins": [
|
||||||
@ -1261,7 +1261,7 @@
|
|||||||
"id.token.claim": "true",
|
"id.token.claim": "true",
|
||||||
"lightweight.claim": "false",
|
"lightweight.claim": "false",
|
||||||
"access.token.claim": "true",
|
"access.token.claim": "true",
|
||||||
"claim.name": "{{ oidc.claims.groups }}"
|
"claim.name": "{{ OIDC.CLAIMS.GROUPS }}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -1520,7 +1520,7 @@
|
|||||||
"user.attribute": "username",
|
"user.attribute": "username",
|
||||||
"id.token.claim": "true",
|
"id.token.claim": "true",
|
||||||
"access.token.claim": "true",
|
"access.token.claim": "true",
|
||||||
"claim.name": "{{oidc.attributes.username}}",
|
"claim.name": "{{OIDC.ATTRIBUTES.USERNAME}}",
|
||||||
"jsonType.label": "String"
|
"jsonType.label": "String"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -5,7 +5,7 @@ database_type: "postgres"
|
|||||||
# Keycloak
|
# Keycloak
|
||||||
keycloak_container: "{{ applications | get_app_conf(application_id, 'docker.services.keycloak.name') }}" # Name of the keycloak docker container
|
keycloak_container: "{{ applications | get_app_conf(application_id, 'docker.services.keycloak.name') }}" # Name of the keycloak docker container
|
||||||
keycloak_docker_import_directory: "/opt/keycloak/data/import/" # Directory in which keycloak import files are placed in the running docker container
|
keycloak_docker_import_directory: "/opt/keycloak/data/import/" # Directory in which keycloak import files are placed in the running docker container
|
||||||
keycloak_realm: "{{ oidc.client.realm }}" # This is the name of the default realm which is used by the applications
|
keycloak_realm: "{{ OIDC.CLIENT.ISSUER_URL }}" # This is the name of the default realm which is used by the applications
|
||||||
keycloak_master_api_user: "{{ applications | get_app_conf(application_id, 'users.administrator') }}" # Master Administrator
|
keycloak_master_api_user: "{{ applications | get_app_conf(application_id, 'users.administrator') }}" # Master Administrator
|
||||||
keycloak_master_api_user_name: "{{ keycloak_master_api_user.username }}" # Master Administrator Username
|
keycloak_master_api_user_name: "{{ keycloak_master_api_user.username }}" # Master Administrator Username
|
||||||
keycloak_master_api_user_password: "{{ keycloak_master_api_user.password }}" # Master Administrator Password
|
keycloak_master_api_user_password: "{{ keycloak_master_api_user.password }}" # Master Administrator Password
|
||||||
@ -17,7 +17,7 @@ keycloak_image: "{{ applications | get_app_conf(application_
|
|||||||
keycloak_version: "{{ applications | get_app_conf(application_id, 'docker.services.keycloak.version') }}" # Keycloak docker version
|
keycloak_version: "{{ applications | get_app_conf(application_id, 'docker.services.keycloak.version') }}" # Keycloak docker version
|
||||||
keycloak_debug_enabled: "{{ MODE_DEBUG }}"
|
keycloak_debug_enabled: "{{ MODE_DEBUG }}"
|
||||||
keycloak_redirect_features: ["features.oauth2","features.oidc"]
|
keycloak_redirect_features: ["features.oauth2","features.oidc"]
|
||||||
keycloak_client_id: "{{ oidc.client.id }}"
|
keycloak_client_id: "{{ OIDC.CLIENT.ID }}"
|
||||||
keycloak_ldap_component_name: "{{ ldap.server.domain }}" # Name of the LDAP User Federation component in Keycloak (as shown in UI)
|
keycloak_ldap_component_name: "{{ ldap.server.domain }}" # Name of the LDAP User Federation component in Keycloak (as shown in UI)
|
||||||
keycloak_import_realm: "{{ applications | get_app_conf(application_id, 'actions.import_realm') }}" # Activate realm import
|
keycloak_import_realm: "{{ applications | get_app_conf(application_id, 'actions.import_realm') }}" # Activate realm import
|
||||||
keycloak_update_ldap_bind: "{{ applications | get_app_conf(application_id, 'actions.update_ldap_bind') }}" # Toggle the LDAP bind update step
|
keycloak_update_ldap_bind: "{{ applications | get_app_conf(application_id, 'actions.update_ldap_bind') }}" # Toggle the LDAP bind update step
|
||||||
|
@ -35,9 +35,9 @@
|
|||||||
oidc_settings_json: >-
|
oidc_settings_json: >-
|
||||||
{{ {
|
{{ {
|
||||||
"enabled": True,
|
"enabled": True,
|
||||||
"client_id": oidc.client.id,
|
"client_id": OIDC.CLIENT.ID,
|
||||||
"provider_url": oidc.client.issuer_url,
|
"provider_url": OIDC.CLIENT.ISSUER_URL,
|
||||||
"client_secret": oidc.client.secret
|
"client_secret": OIDC.CLIENT.SECRET
|
||||||
} | to_json }}
|
} | to_json }}
|
||||||
|
|
||||||
- name: Update administrator email and password login in Listmonk
|
- name: Update administrator email and password login in Listmonk
|
||||||
|
@ -24,9 +24,9 @@ LISTMONK_SETTINGS:
|
|||||||
value: >-
|
value: >-
|
||||||
{{ {
|
{{ {
|
||||||
"enabled": True,
|
"enabled": True,
|
||||||
"client_id": oidc.client.id,
|
"client_id": OIDC.CLIENT.ID,
|
||||||
"provider_url": oidc.client.issuer_url,
|
"provider_url": OIDC.CLIENT.ISSUER_URL,
|
||||||
"client_secret": oidc.client.secret
|
"client_secret": OIDC.CLIENT.SECRET
|
||||||
} | to_json }}
|
} | to_json }}
|
||||||
when: applications | get_app_conf(application_id, 'features.oidc', False)
|
when: applications | get_app_conf(application_id, 'features.oidc', False)
|
||||||
|
|
||||||
|
@ -1,10 +1,23 @@
|
|||||||
- name: "load docker, db and proxy for {{ application_id }}"
|
- name: Ensure MAILU_HOSTNAMES is a list with max 1 entry
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- MAILU_HOSTNAMES is iterable
|
||||||
|
- MAILU_HOSTNAMES is sequence
|
||||||
|
- MAILU_HOSTNAMES | length <= 1
|
||||||
|
fail_msg: "MAILU_HOSTNAMES must be a list with at most one entry (only one host is supported). You can set the other ones as alias."
|
||||||
|
success_msg: "MAILU_HOSTNAMES is valid."
|
||||||
|
|
||||||
|
- name: "Mailu Docker and Webserver Setup"
|
||||||
|
block:
|
||||||
|
- name: "load docker, db and proxy for {{ application_id }}"
|
||||||
include_role:
|
include_role:
|
||||||
name: cmp-db-docker-proxy
|
name: cmp-db-docker-proxy
|
||||||
|
|
||||||
- name: "Include the srv-proxy-6-6-tls-deploy role"
|
- name: "Include the sys-svc-cert-sync-docker role"
|
||||||
include_role:
|
include_role:
|
||||||
name: srv-proxy-6-6-tls-deploy
|
name: sys-svc-cert-sync-docker
|
||||||
|
vars:
|
||||||
|
domain: "{{ MAILU_HOSTNAME }}"
|
||||||
|
|
||||||
- name: Flush docker service handlers
|
- name: Flush docker service handlers
|
||||||
meta: flush_handlers
|
meta: flush_handlers
|
||||||
@ -12,10 +25,8 @@
|
|||||||
- name: "Create Mailu accounts"
|
- name: "Create Mailu accounts"
|
||||||
include_tasks: 02_create-mailu-user.yml
|
include_tasks: 02_create-mailu-user.yml
|
||||||
vars:
|
vars:
|
||||||
mailu_compose_dir: "{{ docker_compose.directories.instance }}"
|
MAILU_DOCKER_DIR: "{{ docker_compose.directories.instance }}"
|
||||||
mailu_domain: "{{ PRIMARY_DOMAIN }}"
|
|
||||||
mailu_api_base_url: "http://127.0.0.1:8080/api/v1"
|
mailu_api_base_url: "http://127.0.0.1:8080/api/v1"
|
||||||
mailu_global_api_token: "{{ applications | get_app_conf(application_id, 'credentials.api_token') }}"
|
|
||||||
mailu_action: >-
|
mailu_action: >-
|
||||||
{{
|
{{
|
||||||
(
|
(
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
- name: "Ensure Mailu user '{{ mailu_user_key }};{{ mailu_user_name }}@{{ mailu_domain }}'' exists"
|
- name: "Ensure Mailu user '{{ mailu_user_key }};{{ mailu_user_name }}@{{ MAILU_DOMAIN }}'' exists"
|
||||||
command: >
|
command: >
|
||||||
docker compose exec admin flask mailu {{ mailu_action }}
|
docker compose exec admin flask mailu {{ mailu_action }}
|
||||||
{{ mailu_user_name }} {{ mailu_domain }} '{{ mailu_password }}'
|
{{ mailu_user_name }} {{ MAILU_DOMAIN }} '{{ mailu_password }}'
|
||||||
args:
|
args:
|
||||||
chdir: "{{ mailu_compose_dir }}"
|
chdir: "{{ MAILU_DOCKER_DIR }}"
|
||||||
register: mailu_user_result
|
register: mailu_user_result
|
||||||
failed_when: >
|
failed_when: >
|
||||||
mailu_user_result.rc != 0 and
|
mailu_user_result.rc != 0 and
|
||||||
@ -15,12 +15,12 @@
|
|||||||
when: "'mail-bot' in item.value.roles or 'administrator' in item.value.roles"
|
when: "'mail-bot' in item.value.roles or 'administrator' in item.value.roles"
|
||||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||||
|
|
||||||
- name: "Change password for user '{{ mailu_user_key }};{{ mailu_user_name }}@{{ mailu_domain }}'"
|
- name: "Change password for user '{{ mailu_user_key }};{{ mailu_user_name }}@{{ MAILU_DOMAIN }}'"
|
||||||
command: >
|
command: >
|
||||||
docker compose exec admin flask mailu password
|
docker compose exec admin flask mailu password
|
||||||
{{ mailu_user_name }} {{ mailu_domain }} '{{ mailu_password }}'
|
{{ mailu_user_name }} {{ MAILU_DOMAIN }} '{{ mailu_password }}'
|
||||||
args:
|
args:
|
||||||
chdir: "{{ mailu_compose_dir }}"
|
chdir: "{{ MAILU_DOCKER_DIR }}"
|
||||||
when: "'mail-bot' in item.value.roles or 'administrator' in item.value.roles"
|
when: "'mail-bot' in item.value.roles or 'administrator' in item.value.roles"
|
||||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||||
|
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
command: >-
|
command: >-
|
||||||
docker compose exec -T admin \
|
docker compose exec -T admin \
|
||||||
curl -s -X GET {{ mailu_api_base_url }}/token \
|
curl -s -X GET {{ mailu_api_base_url }}/token \
|
||||||
-H "Authorization: Bearer {{ mailu_global_api_token }}"
|
-H "Authorization: Bearer {{ MAILU_API_TOKEN }}"
|
||||||
args:
|
args:
|
||||||
chdir: "{{ mailu_compose_dir }}"
|
chdir: "{{ MAILU_DOCKER_DIR }}"
|
||||||
register: mailu_tokens_cli
|
register: mailu_tokens_cli
|
||||||
changed_when: false
|
changed_when: false
|
||||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||||
@ -25,9 +25,9 @@
|
|||||||
command: >-
|
command: >-
|
||||||
docker compose exec -T admin \
|
docker compose exec -T admin \
|
||||||
curl -s -X DELETE {{ mailu_api_base_url }}/token/{{ mailu_user_existing_token.id }} \
|
curl -s -X DELETE {{ mailu_api_base_url }}/token/{{ mailu_user_existing_token.id }} \
|
||||||
-H "Authorization: Bearer {{ mailu_global_api_token }}"
|
-H "Authorization: Bearer {{ MAILU_API_TOKEN }}"
|
||||||
args:
|
args:
|
||||||
chdir: "{{ mailu_compose_dir }}"
|
chdir: "{{ MAILU_DOCKER_DIR }}"
|
||||||
when:
|
when:
|
||||||
- users[mailu_user_key].mailu_token is not defined
|
- users[mailu_user_key].mailu_token is not defined
|
||||||
- mailu_user_existing_token is not none
|
- mailu_user_existing_token is not none
|
||||||
@ -40,7 +40,7 @@
|
|||||||
command: >-
|
command: >-
|
||||||
docker compose exec -T admin \
|
docker compose exec -T admin \
|
||||||
curl -s -X POST {{ mailu_api_base_url }}/token \
|
curl -s -X POST {{ mailu_api_base_url }}/token \
|
||||||
-H "Authorization: Bearer {{ mailu_global_api_token }}" \
|
-H "Authorization: Bearer {{ MAILU_API_TOKEN }}" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{{ {
|
-d '{{ {
|
||||||
"comment": mailu_user_key ~ " - ansible.infinito",
|
"comment": mailu_user_key ~ " - ansible.infinito",
|
||||||
@ -48,7 +48,7 @@
|
|||||||
"ip": mailu_token_ip
|
"ip": mailu_token_ip
|
||||||
} | to_json }}'
|
} | to_json }}'
|
||||||
args:
|
args:
|
||||||
chdir: "{{ mailu_compose_dir }}"
|
chdir: "{{ MAILU_DOCKER_DIR }}"
|
||||||
when: users[mailu_user_key].mailu_token is not defined
|
when: users[mailu_user_key].mailu_token is not defined
|
||||||
register: mailu_token_creation
|
register: mailu_token_creation
|
||||||
changed_when: mailu_token_creation.rc == 0
|
changed_when: mailu_token_creation.rc == 0
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
- name: "Load Mailu DNS variables"
|
|
||||||
include_vars: vars/mailu-dns.yml
|
|
||||||
|
|
||||||
- name: Generate DKIM public key
|
- name: Generate DKIM public key
|
||||||
include_tasks: 05_generate-and-read-dkim.yml
|
include_tasks: 05_generate-and-read-dkim.yml
|
||||||
|
|
||||||
- name: "Set A record for mail server"
|
# A/AAAA record for the mail host in the **Hostname Zone**
|
||||||
|
- name: "Set A record for Mailu host"
|
||||||
community.general.cloudflare_dns:
|
community.general.cloudflare_dns:
|
||||||
api_token: "{{ cloudflare_record_api_token }}"
|
api_token: "{{ MAILU_CLOUDFLARE_API_TOKEN }}"
|
||||||
zone: "{{ mailu_dns_zone }}"
|
zone: "{{ MAILU_HOSTNAME_DNS_ZONE }}"
|
||||||
type: A
|
type: A
|
||||||
name: "{{ domain }}"
|
name: "{{ MAILU_HOSTNAME }}" # Fully Qualified Domain Name of the mail host
|
||||||
content: "{{ mailu_dns_ip }}"
|
content: "{{ MAILU_IP4_PUBLIC }}"
|
||||||
proxied: false
|
proxied: false
|
||||||
ttl: 1
|
ttl: 1
|
||||||
state: present
|
state: present
|
||||||
@ -18,13 +16,29 @@
|
|||||||
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
|
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
|
||||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||||
|
|
||||||
|
- name: "Set AAAA record for Mailu host"
|
||||||
|
community.general.cloudflare_dns:
|
||||||
|
api_token: "{{ MAILU_CLOUDFLARE_API_TOKEN }}"
|
||||||
|
zone: "{{ MAILU_HOSTNAME_DNS_ZONE }}"
|
||||||
|
type: AAAA
|
||||||
|
name: "{{ MAILU_HOSTNAME }}"
|
||||||
|
content: "{{ MAILU_IP6_PUBLIC }}"
|
||||||
|
proxied: false
|
||||||
|
ttl: 1
|
||||||
|
state: present
|
||||||
|
when: MAILU_IP6_PUBLIC is defined and MAILU_IP6_PUBLIC | length > 0
|
||||||
|
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
|
||||||
|
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
|
||||||
|
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||||
|
|
||||||
|
# Autoconfig CNAME record in the **Mail Domain Zone**
|
||||||
- name: "Set CNAME record for autoconfig"
|
- name: "Set CNAME record for autoconfig"
|
||||||
community.general.cloudflare_dns:
|
community.general.cloudflare_dns:
|
||||||
api_token: "{{ cloudflare_record_api_token }}"
|
api_token: "{{ MAILU_CLOUDFLARE_API_TOKEN }}"
|
||||||
zone: "{{ mailu_dns_zone }}"
|
zone: "{{ MAILU_DOMAIN_DNS_ZONE }}"
|
||||||
type: CNAME
|
type: CNAME
|
||||||
name: "autoconfig.{{ mailu_dns_zone }}"
|
name: "autoconfig.{{ MAILU_DOMAIN_DNS_ZONE }}"
|
||||||
value: "{{ domain }}"
|
value: "{{ MAILU_HOSTNAME }}" # Points to the Mailu host FQDN
|
||||||
proxied: false
|
proxied: false
|
||||||
ttl: 1
|
ttl: 1
|
||||||
state: present
|
state: present
|
||||||
@ -32,13 +46,14 @@
|
|||||||
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
|
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
|
||||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||||
|
|
||||||
|
# MX record in the **Mail Domain Zone**
|
||||||
- name: "Set MX record"
|
- name: "Set MX record"
|
||||||
community.general.cloudflare_dns:
|
community.general.cloudflare_dns:
|
||||||
api_token: "{{ cloudflare_record_api_token }}"
|
api_token: "{{ MAILU_CLOUDFLARE_API_TOKEN }}"
|
||||||
zone: "{{ mailu_dns_zone }}"
|
zone: "{{ MAILU_DOMAIN_DNS_ZONE }}"
|
||||||
type: MX
|
type: MX
|
||||||
name: "{{ mailu_dns_zone }}"
|
name: "{{ MAILU_DOMAIN }}" # Root mail domain
|
||||||
value: "{{ domain }}"
|
value: "{{ MAILU_HOSTNAME }}" # Points to the Mailu host
|
||||||
priority: 10
|
priority: 10
|
||||||
ttl: 1
|
ttl: 1
|
||||||
state: present
|
state: present
|
||||||
@ -46,60 +61,62 @@
|
|||||||
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
|
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
|
||||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||||
|
|
||||||
|
# SRV records in the **Mail Domain Zone**
|
||||||
- name: "Set SRV records"
|
- name: "Set SRV records"
|
||||||
community.general.cloudflare_dns:
|
community.general.cloudflare_dns:
|
||||||
api_token: "{{ cloudflare_record_api_token }}"
|
api_token: "{{ MAILU_CLOUDFLARE_API_TOKEN }}"
|
||||||
zone: "{{ mailu_dns_zone }}"
|
zone: "{{ MAILU_DOMAIN_DNS_ZONE }}"
|
||||||
type: SRV
|
type: SRV
|
||||||
service: "_{{ item.key }}"
|
service: "_{{ item.key }}"
|
||||||
proto: "_tcp"
|
proto: "_tcp"
|
||||||
priority: "{{ item.value.priority }}"
|
priority: "{{ item.value.priority }}"
|
||||||
weight: "{{ item.value.weight }}"
|
weight: "{{ item.value.weight }}"
|
||||||
port: "{{ item.value.port }}"
|
port: "{{ item.value.port }}"
|
||||||
value: "{{ domain }}"
|
value: "{{ MAILU_HOSTNAME }}" # Target = Mailu host FQDN
|
||||||
ttl: 1
|
ttl: 1
|
||||||
state: present
|
state: present
|
||||||
loop: "{{ mailu_dns_srv_records | dict2items }}"
|
name: "{{ MAILU_DOMAIN }}"
|
||||||
|
loop: "{{ MAILU_DNS_SRV_RECORDS | dict2items }}"
|
||||||
ignore_errors: true
|
ignore_errors: true
|
||||||
#register: srv_result
|
|
||||||
#failed_when: srv_result.rc != 0 and ("An identical record already exists" not in srv_result.stdout)
|
|
||||||
#changed_when: srv_result.rc == 0 and ("An identical record already exists" not in srv_result.stdout)
|
|
||||||
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
|
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
|
||||||
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
|
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
|
||||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||||
|
|
||||||
|
# SPF TXT record in the **Mail Domain Zone**
|
||||||
- name: "Set SPF TXT record"
|
- name: "Set SPF TXT record"
|
||||||
community.general.cloudflare_dns:
|
community.general.cloudflare_dns:
|
||||||
api_token: "{{ cloudflare_record_api_token }}"
|
api_token: "{{ MAILU_CLOUDFLARE_API_TOKEN }}"
|
||||||
zone: "{{ mailu_dns_zone }}"
|
zone: "{{ MAILU_DOMAIN_DNS_ZONE }}"
|
||||||
type: TXT
|
type: TXT
|
||||||
name: "{{ mailu_dns_zone }}"
|
name: "{{ MAILU_DOMAIN }}"
|
||||||
value: "v=spf1 mx a:{{ domain }} ~all"
|
value: "v=spf1 mx a:{{ MAILU_HOSTNAME }} ~all"
|
||||||
ttl: 1
|
ttl: 1
|
||||||
state: present
|
state: present
|
||||||
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
|
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
|
||||||
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
|
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
|
||||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||||
|
|
||||||
|
# DMARC TXT record in the **Mail Domain Zone**
|
||||||
- name: "Set DMARC TXT record"
|
- name: "Set DMARC TXT record"
|
||||||
community.general.cloudflare_dns:
|
community.general.cloudflare_dns:
|
||||||
api_token: "{{ cloudflare_record_api_token }}"
|
api_token: "{{ MAILU_CLOUDFLARE_API_TOKEN }}"
|
||||||
zone: "{{ mailu_dns_zone }}"
|
zone: "{{ MAILU_DOMAIN_DNS_ZONE }}"
|
||||||
type: TXT
|
type: TXT
|
||||||
name: "_dmarc.{{ mailu_dns_zone }}"
|
name: "_dmarc.{{ MAILU_DOMAIN_DNS_ZONE }}"
|
||||||
value: "v=DMARC1; p=reject; ruf=mailto:{{ mailu_dmarc_ruf }}; adkim=s; aspf=s"
|
value: "v=DMARC1; p=reject; ruf=mailto:{{ MAILU_DMARC_RUF }}; adkim=s; aspf=s"
|
||||||
ttl: 1
|
ttl: 1
|
||||||
state: present
|
state: present
|
||||||
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
|
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
|
||||||
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
|
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
|
||||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||||
|
|
||||||
|
# DKIM TXT record in the **Mail Domain Zone**
|
||||||
- name: "Set DKIM TXT record"
|
- name: "Set DKIM TXT record"
|
||||||
community.general.cloudflare_dns:
|
community.general.cloudflare_dns:
|
||||||
api_token: "{{ cloudflare_record_api_token }}"
|
api_token: "{{ MAILU_CLOUDFLARE_API_TOKEN }}"
|
||||||
zone: "{{ mailu_dns_zone }}"
|
zone: "{{ MAILU_DOMAIN_DNS_ZONE }}"
|
||||||
type: TXT
|
type: TXT
|
||||||
name: "dkim._domainkey.{{ mailu_dns_zone }}"
|
name: "dkim._domainkey.{{ MAILU_DOMAIN_DNS_ZONE }}"
|
||||||
value: "{{ mailu_dkim_public_key }}"
|
value: "{{ mailu_dkim_public_key }}"
|
||||||
ttl: 1
|
ttl: 1
|
||||||
state: present
|
state: present
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
- name: Check if DKIM private key file exists in the antispam container
|
- name: Check if DKIM private key file exists in the antispam container
|
||||||
command: >
|
command: >
|
||||||
docker compose exec -T antispam
|
docker compose exec -T antispam
|
||||||
test -f {{ mailu_dkim_key_path }}
|
test -f {{ MAILU_DKIM_KEY_PATH }}
|
||||||
register: dkim_key_file_stat
|
register: dkim_key_file_stat
|
||||||
failed_when: false
|
failed_when: false
|
||||||
changed_when: false
|
changed_when: false
|
||||||
@ -11,7 +11,7 @@
|
|||||||
- name: Generate DKIM key
|
- name: Generate DKIM key
|
||||||
command: >
|
command: >
|
||||||
docker compose exec -T antispam
|
docker compose exec -T antispam
|
||||||
rspamadm dkim_keygen -s dkim -d {{ applications | get_app_conf(application_id, 'domain', True) }} -k {{ mailu_dkim_key_path }}
|
rspamadm dkim_keygen -s dkim -d {{ MAILU_DOMAIN }} -k {{ MAILU_DKIM_KEY_PATH }}
|
||||||
register: dkim_keygen_output
|
register: dkim_keygen_output
|
||||||
when: dkim_key_file_stat.rc != 0
|
when: dkim_key_file_stat.rc != 0
|
||||||
args:
|
args:
|
||||||
@ -21,7 +21,7 @@
|
|||||||
- name: Fetch DKIM private key from antispam container
|
- name: Fetch DKIM private key from antispam container
|
||||||
shell: >
|
shell: >
|
||||||
docker compose exec -T antispam
|
docker compose exec -T antispam
|
||||||
cat {{ mailu_dkim_key_path }}
|
cat {{ MAILU_DKIM_KEY_PATH }}
|
||||||
args:
|
args:
|
||||||
chdir: "{{ docker_compose.directories.instance }}"
|
chdir: "{{ docker_compose.directories.instance }}"
|
||||||
register: dkim_priv_content
|
register: dkim_priv_content
|
||||||
|
@ -2,29 +2,29 @@
|
|||||||
|
|
||||||
# Core services
|
# Core services
|
||||||
resolver:
|
resolver:
|
||||||
image: {{docker_source}}/unbound:{{ mailu_version }}
|
image: {{ MAILU_DOCKER_FLAVOR }}/unbound:{{ MAILU_VERSION }}
|
||||||
container_name: {{mailu_name}}_resolver
|
container_name: {{ MAILU_CONTAINER }}_resolver
|
||||||
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
||||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||||
ipv4_address: {{networks.local['web-app-mailu'].dns}}
|
ipv4_address: {{ MAILU_DNS_RESOLVER }}
|
||||||
|
|
||||||
front:
|
front:
|
||||||
container_name: {{mailu_name}}_front
|
container_name: {{ MAILU_CONTAINER }}_front
|
||||||
image: {{docker_source}}/nginx:{{ mailu_version }}
|
image: {{ MAILU_DOCKER_FLAVOR }}/nginx:{{ MAILU_VERSION }}
|
||||||
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:{{ports.localhost.http[application_id]}}:80"
|
- "127.0.0.1:{{ports.localhost.http[application_id]}}:80"
|
||||||
- "{{ networks.internet.ip4 }}:25:25"
|
- "{{ MAILU_IP4_PUBLIC }}:25:25"
|
||||||
- "{{ networks.internet.ip4 }}:465:465"
|
- "{{ MAILU_IP4_PUBLIC }}:465:465"
|
||||||
- "{{ networks.internet.ip4 }}:587:587"
|
- "{{ MAILU_IP4_PUBLIC }}:587:587"
|
||||||
- "{{ networks.internet.ip4 }}:110:110"
|
- "{{ MAILU_IP4_PUBLIC }}:110:110"
|
||||||
- "{{ networks.internet.ip4 }}:995:995"
|
- "{{ MAILU_IP4_PUBLIC }}:995:995"
|
||||||
- "{{ networks.internet.ip4 }}:143:143"
|
- "{{ MAILU_IP4_PUBLIC }}:143:143"
|
||||||
- "{{ networks.internet.ip4 }}:993:993"
|
- "{{ MAILU_IP4_PUBLIC }}:993:993"
|
||||||
- "{{ networks.internet.ip4 }}:4190:4190"
|
- "{{ MAILU_IP4_PUBLIC }}:4190:4190"
|
||||||
volumes:
|
volumes:
|
||||||
- "{{docker_compose.directories.volumes}}overrides/nginx:/overrides:ro"
|
- "{{docker_compose.directories.volumes}}overrides/nginx:/overrides:ro"
|
||||||
- "{{cert_mount_directory}}:/certs:ro"
|
- "{{ cert_mount_directory }}:/certs:ro"
|
||||||
{% include 'roles/docker-container/templates/depends_on/dmbs_incl.yml.j2' %}
|
{% include 'roles/docker-container/templates/depends_on/dmbs_incl.yml.j2' %}
|
||||||
resolver:
|
resolver:
|
||||||
condition: service_started
|
condition: service_started
|
||||||
@ -32,11 +32,11 @@
|
|||||||
webmail:
|
webmail:
|
||||||
radicale:
|
radicale:
|
||||||
dns:
|
dns:
|
||||||
- {{networks.local['web-app-mailu'].dns}}
|
- {{ MAILU_DNS_RESOLVER }}
|
||||||
|
|
||||||
admin:
|
admin:
|
||||||
container_name: {{mailu_name}}_admin
|
container_name: {{ MAILU_CONTAINER }}_admin
|
||||||
image: {{docker_source}}/admin:{{ mailu_version }}
|
image: {{ MAILU_DOCKER_FLAVOR }}/admin:{{ MAILU_VERSION }}
|
||||||
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
||||||
volumes:
|
volumes:
|
||||||
- "admin_data:/data"
|
- "admin_data:/data"
|
||||||
@ -47,12 +47,12 @@
|
|||||||
front:
|
front:
|
||||||
condition: service_started
|
condition: service_started
|
||||||
dns:
|
dns:
|
||||||
- {{networks.local['web-app-mailu'].dns}}
|
- {{ MAILU_DNS_RESOLVER }}
|
||||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||||
|
|
||||||
imap:
|
imap:
|
||||||
container_name: {{mailu_name}}_imap
|
container_name: {{ MAILU_CONTAINER }}_imap
|
||||||
image: {{docker_source}}/dovecot:{{ mailu_version }}
|
image: {{ MAILU_DOCKER_FLAVOR }}/dovecot:{{ MAILU_VERSION }}
|
||||||
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
||||||
volumes:
|
volumes:
|
||||||
- "dovecot_mail:/mail"
|
- "dovecot_mail:/mail"
|
||||||
@ -61,12 +61,12 @@
|
|||||||
- front
|
- front
|
||||||
- resolver
|
- resolver
|
||||||
dns:
|
dns:
|
||||||
- {{networks.local['web-app-mailu'].dns}}
|
- {{ MAILU_DNS_RESOLVER }}
|
||||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||||
|
|
||||||
smtp:
|
smtp:
|
||||||
container_name: {{mailu_name}}_smtp
|
container_name: {{ MAILU_CONTAINER }}_smtp
|
||||||
image: {{docker_source}}/postfix:{{ mailu_version }}
|
image: {{ MAILU_DOCKER_FLAVOR }}/postfix:{{ MAILU_VERSION }}
|
||||||
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
||||||
volumes:
|
volumes:
|
||||||
- "{{docker_compose.directories.volumes}}overrides:/overrides:ro"
|
- "{{docker_compose.directories.volumes}}overrides:/overrides:ro"
|
||||||
@ -75,24 +75,24 @@
|
|||||||
- front
|
- front
|
||||||
- resolver
|
- resolver
|
||||||
dns:
|
dns:
|
||||||
- {{networks.local['web-app-mailu'].dns}}
|
- {{ MAILU_DNS_RESOLVER }}
|
||||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||||
|
|
||||||
oletools:
|
oletools:
|
||||||
container_name: {{mailu_name}}_oletools
|
container_name: {{ MAILU_CONTAINER }}_oletools
|
||||||
image: {{docker_source}}/oletools:{{ mailu_version }}
|
image: {{ MAILU_DOCKER_FLAVOR }}/oletools:{{ MAILU_VERSION }}
|
||||||
hostname: oletools
|
hostname: oletools
|
||||||
restart: {{ DOCKER_RESTART_POLICY }}
|
restart: {{ DOCKER_RESTART_POLICY }}
|
||||||
depends_on:
|
depends_on:
|
||||||
- resolver
|
- resolver
|
||||||
dns:
|
dns:
|
||||||
- {{networks.local['web-app-mailu'].dns}}
|
- {{ MAILU_DNS_RESOLVER }}
|
||||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||||
noinet:
|
noinet:
|
||||||
|
|
||||||
antispam:
|
antispam:
|
||||||
container_name: {{mailu_name}}_antispam
|
container_name: {{ MAILU_CONTAINER }}_antispam
|
||||||
image: {{docker_source}}/rspamd:{{ mailu_version }}
|
image: {{ MAILU_DOCKER_FLAVOR }}/rspamd:{{ MAILU_VERSION }}
|
||||||
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
||||||
volumes:
|
volumes:
|
||||||
- "filter:/var/lib/rspamd"
|
- "filter:/var/lib/rspamd"
|
||||||
@ -104,14 +104,14 @@
|
|||||||
- antivirus
|
- antivirus
|
||||||
- resolver
|
- resolver
|
||||||
dns:
|
dns:
|
||||||
- {{networks.local['web-app-mailu'].dns}}
|
- {{ MAILU_DNS_RESOLVER }}
|
||||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||||
noinet:
|
noinet:
|
||||||
|
|
||||||
|
|
||||||
# Optional services
|
# Optional services
|
||||||
antivirus:
|
antivirus:
|
||||||
container_name: {{mailu_name}}_antivirus
|
container_name: {{ MAILU_CONTAINER }}_antivirus
|
||||||
image: clamav/clamav-debian:latest
|
image: clamav/clamav-debian:latest
|
||||||
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
||||||
volumes:
|
volumes:
|
||||||
@ -119,25 +119,25 @@
|
|||||||
depends_on:
|
depends_on:
|
||||||
- resolver
|
- resolver
|
||||||
dns:
|
dns:
|
||||||
- {{networks.local['web-app-mailu'].dns}}
|
- {{ MAILU_DNS_RESOLVER }}
|
||||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||||
|
|
||||||
webdav:
|
webdav:
|
||||||
container_name: {{mailu_name}}_webdav
|
container_name: {{ MAILU_CONTAINER }}_webdav
|
||||||
image: {{docker_source}}/radicale:{{ mailu_version }}
|
image: {{ MAILU_DOCKER_FLAVOR }}/radicale:{{ MAILU_VERSION }}
|
||||||
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
||||||
volumes:
|
volumes:
|
||||||
- "webdav_data:/data"
|
- "webdav_data:/data"
|
||||||
depends_on:
|
depends_on:
|
||||||
- resolver
|
- resolver
|
||||||
dns:
|
dns:
|
||||||
- {{networks.local['web-app-mailu'].dns}}
|
- {{ MAILU_DNS_RESOLVER }}
|
||||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||||
radicale:
|
radicale:
|
||||||
|
|
||||||
fetchmail:
|
fetchmail:
|
||||||
container_name: {{mailu_name}}_fetchmail
|
container_name: {{ MAILU_CONTAINER }}_fetchmail
|
||||||
image: {{docker_source}}/fetchmail:{{ mailu_version }}
|
image: {{ MAILU_DOCKER_FLAVOR }}/fetchmail:{{ MAILU_VERSION }}
|
||||||
volumes:
|
volumes:
|
||||||
- "admin_data:/data"
|
- "admin_data:/data"
|
||||||
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
||||||
@ -147,12 +147,12 @@
|
|||||||
- imap
|
- imap
|
||||||
- resolver
|
- resolver
|
||||||
dns:
|
dns:
|
||||||
- {{networks.local['web-app-mailu'].dns}}
|
- {{ MAILU_DNS_RESOLVER }}
|
||||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||||
|
|
||||||
webmail:
|
webmail:
|
||||||
container_name: {{mailu_name}}_webmail
|
container_name: {{ MAILU_CONTAINER }}_webmail
|
||||||
image: {{docker_source}}/webmail:{{ mailu_version }}
|
image: {{ MAILU_DOCKER_FLAVOR }}/webmail:{{ MAILU_VERSION }}
|
||||||
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
||||||
volumes:
|
volumes:
|
||||||
- "webmail_data:/data"
|
- "webmail_data:/data"
|
||||||
@ -162,25 +162,25 @@
|
|||||||
- front
|
- front
|
||||||
- resolver
|
- resolver
|
||||||
dns:
|
dns:
|
||||||
- {{networks.local['web-app-mailu'].dns}}
|
- {{ MAILU_DNS_RESOLVER }}
|
||||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||||
webmail:
|
webmail:
|
||||||
|
|
||||||
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
||||||
smtp_queue:
|
smtp_queue:
|
||||||
name: {{ mailu_smtp_queue }}
|
name: {{ MAILU_SMTP_QUEUE_VOLUME }}
|
||||||
admin_data:
|
admin_data:
|
||||||
name: {{ mailu_admin_data }}
|
name: {{ MAILU_ADMIN_DATA_VOLUME }}
|
||||||
webdav_data:
|
webdav_data:
|
||||||
name: {{ mailu_webdav_data }}
|
name: {{ MAILU_WEBDAV_DATA }}
|
||||||
webmail_data:
|
webmail_data:
|
||||||
name: {{ mailu_webmail_data }}
|
name: {{ MAILU_WEBMAIL_DATA }}
|
||||||
filter:
|
filter:
|
||||||
name: {{ mailu_filter }}
|
name: {{ MAILU_FILTER_VOLUME }}
|
||||||
dkim:
|
dkim:
|
||||||
name: {{ mailu_dkim }}
|
name: {{ MAILU_DKIM_VOLUME }}
|
||||||
dovecot_mail:
|
dovecot_mail:
|
||||||
name: {{ mailu_dovecot_mail }}
|
name: {{ MAILU_DOVECOT_MAIL_VOLUME }}
|
||||||
|
|
||||||
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
||||||
radicale:
|
radicale:
|
||||||
|
@ -11,16 +11,16 @@
|
|||||||
LD_PRELOAD=/usr/lib/libhardened_malloc.so
|
LD_PRELOAD=/usr/lib/libhardened_malloc.so
|
||||||
|
|
||||||
# Set to a randomly generated 16 bytes string
|
# Set to a randomly generated 16 bytes string
|
||||||
SECRET_KEY={{applications | get_app_conf(application_id,'credentials.secret_key')}}
|
SECRET_KEY={{ MAILU_SECRET_KEY }}
|
||||||
|
|
||||||
# Subnet of the docker network. This should not conflict with any networks to which your system is connected. (Internal and external!)
|
# Subnet of the docker network. This should not conflict with any networks to which your system is connected. (Internal and external!)
|
||||||
SUBNET={{networks.local['web-app-mailu'].subnet}}
|
SUBNET={{ MAILU_SUBNET }}
|
||||||
|
|
||||||
# Main mail domain
|
# Main mail domain
|
||||||
DOMAIN={{ applications | get_app_conf(application_id,'domain') }}
|
DOMAIN={{ MAILU_DOMAIN }}
|
||||||
|
|
||||||
# Hostnames for this server, separated with comas
|
# Hostnames for this server, separated with comas
|
||||||
HOSTNAMES={{ domains[application_id] | join(',') }}
|
HOSTNAMES={{ MAILU_HOSTNAMES | join(',') }}
|
||||||
|
|
||||||
# Postmaster local part (will append the main mail domain)
|
# Postmaster local part (will append the main mail domain)
|
||||||
POSTMASTER=admin
|
POSTMASTER=admin
|
||||||
@ -105,7 +105,7 @@ WEB_WEBMAIL=/webmail
|
|||||||
SITENAME=Mailservices
|
SITENAME=Mailservices
|
||||||
|
|
||||||
# Linked Website URL
|
# Linked Website URL
|
||||||
WEBSITE={{ domains | get_url(application_id, WEB_PROTOCOL) }}
|
WEBSITE={{ MAILU_WEBSITE }}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -151,34 +151,34 @@ SQLALCHEMY_DATABASE_URI=mysql+mysqlconnector://{{ database_username }}:{{ databa
|
|||||||
API=true
|
API=true
|
||||||
WEB_API=/api
|
WEB_API=/api
|
||||||
# Configures the authentication token. The minimum length is 3 characters. This token must be passed as request header to the API as authentication token. This is a mandatory setting for using the RESTful API.
|
# Configures the authentication token. The minimum length is 3 characters. This token must be passed as request header to the API as authentication token. This is a mandatory setting for using the RESTful API.
|
||||||
API_TOKEN={{ applications | get_app_conf(application_id, 'credentials.api_token')}}
|
API_TOKEN={{ MAILU_API_TOKEN}}
|
||||||
|
|
||||||
|
|
||||||
# Activated https://mailu.io/master/configuration.html#advanced-settings
|
# Activated https://mailu.io/master/configuration.html#advanced-settings
|
||||||
AUTH_REQUIRE_TOKENS=True
|
AUTH_REQUIRE_TOKENS=True
|
||||||
|
|
||||||
|
|
||||||
{% if applications | get_app_conf(application_id, 'features.oidc', False) %}
|
{% if MAILU_OIDC_ENABLED | bool %}
|
||||||
###################################
|
###################################
|
||||||
# OpenID Connect settings
|
# OpenID Connect settings
|
||||||
###################################
|
###################################
|
||||||
# @see https://github.com/heviat/Mailu-OIDC/tree/master
|
# @see https://github.com/heviat/Mailu-OIDC/tree/master
|
||||||
|
|
||||||
# Enable OpenID Connect. Possible values: True, False
|
# Enable OpenID Connect. Possible values: True, False
|
||||||
OIDC_ENABLED={{ applications | get_app_conf(application_id, 'features.oidc', False) | string | capitalize }}
|
OIDC_ENABLED={{ MAILU_OIDC_ENABLED | string | capitalize }}
|
||||||
|
|
||||||
# OpenID Connect provider configuration URL
|
# OpenID Connect provider configuration URL
|
||||||
OIDC_PROVIDER_INFO_URL={{ oidc.client.issuer_url }}
|
OIDC_PROVIDER_INFO_URL={{ OIDC.CLIENT.ISSUER_URL }}
|
||||||
|
|
||||||
|
|
||||||
# OpenID Connect Client ID for Mailu
|
# OpenID Connect Client ID for Mailu
|
||||||
OIDC_CLIENT_ID={{ oidc.client.id }}
|
OIDC_CLIENT_ID={{ OIDC.CLIENT.ID }}
|
||||||
|
|
||||||
# OpenID Connect Client secret for Mailu
|
# OpenID Connect Client secret for Mailu
|
||||||
OIDC_CLIENT_SECRET={{ oidc.client.secret }}
|
OIDC_CLIENT_SECRET={{ OIDC.CLIENT.SECRET }}
|
||||||
|
|
||||||
# Label text for OpenID Connect login button. Default: OpenID Connect
|
# Label text for OpenID Connect login button. Default: OpenID Connect
|
||||||
OIDC_BUTTON_NAME={{ oidc.button_text }}
|
OIDC_BUTTON_NAME={{ OIDC.BUTTON_TEXT }}
|
||||||
|
|
||||||
# Disable TLS certificate verification for the OIDC client. Possible values: True, False
|
# Disable TLS certificate verification for the OIDC client. Possible values: True, False
|
||||||
OIDC_VERIFY_SSL=True
|
OIDC_VERIFY_SSL=True
|
||||||
@ -187,17 +187,17 @@ OIDC_VERIFY_SSL=True
|
|||||||
OIDC_CHANGE_PASSWORD_REDIRECT_ENABLED=True
|
OIDC_CHANGE_PASSWORD_REDIRECT_ENABLED=True
|
||||||
|
|
||||||
# Redirect URL for password change. Defaults to provider issuer url appended by /.well-known/change-password
|
# Redirect URL for password change. Defaults to provider issuer url appended by /.well-known/change-password
|
||||||
OIDC_CHANGE_PASSWORD_REDIRECT_URL={{oidc.client.change_credentials}}
|
OIDC_CHANGE_PASSWORD_REDIRECT_URL={{ OIDC.CLIENT.CHANGE_CREDENTIALS }}
|
||||||
|
|
||||||
{% if applications | get_app_conf(application_id, 'oidc.email_by_username', True) | bool %}
|
{% if MAILU_OIDC_EMAIL_BY_USERNAME_ENABLED | bool %}
|
||||||
|
|
||||||
# The OIDC claim used as the username. If the selected claim contains an email address, it will be used as is. If it is not an email (e.g., sub), the email address will be constructed as <OIDC_USERNAME_CLAIM>@<OIDC_USER_DOMAIN>. Defaults to email.
|
# The OIDC claim used as the username. If the selected claim contains an email address, it will be used as is. If it is not an email (e.g., sub), the email address will be constructed as <OIDC_USERNAME_CLAIM>@<OIDC_USER_DOMAIN>. Defaults to email.
|
||||||
OIDC_USERNAME_CLAIM={{oidc.attributes.username}}
|
OIDC_USERNAME_CLAIM={{ OIDC.ATTRIBUTES.USERNAME }}
|
||||||
|
|
||||||
# The domain used when constructing an email from a non-email username (e.g., when OIDC_USERNAME_CLAIM=sub). Ignored if OIDC_USERNAME_CLAIM is already an email. Defaults to the value of DOMAIN.
|
# The domain used when constructing an email from a non-email username (e.g., when OIDC_USERNAME_CLAIM=sub). Ignored if OIDC_USERNAME_CLAIM is already an email. Defaults to the value of DOMAIN.
|
||||||
OIDC_USER_DOMAIN={{ PRIMARY_DOMAIN }}
|
OIDC_USER_DOMAIN={{ MAILU_DOMAIN }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
# If enabled, users who authenticate successfully but do not yet have an account will have one created for them. If disabled, only existing users can log in, and authentication will fail for users without a pre-existing account. Defaults to True.
|
# If enabled, users who authenticate successfully but do not yet have an account will have one created for them. If disabled, only existing users can log in, and authentication will fail for users without a pre-existing account. Defaults to True.
|
||||||
OIDC_ENABLE_USER_CREATION={{ applications | get_app_conf(application_id, 'oidc.enable_user_creation', True) | string | capitalize }}
|
OIDC_ENABLE_USER_CREATION={{ MAILU_OIDC_ENABLE_USER_CREATION }}
|
||||||
{% endif %}
|
{% endif %}
|
@ -1,41 +0,0 @@
|
|||||||
# vars/mailu-dns.yml
|
|
||||||
|
|
||||||
mailu_dns_zone: "{{ applications | get_app_conf(application_id, 'domain', True) }}"
|
|
||||||
mailu_dns_ip: "{{ networks.internet.ip4 }}"
|
|
||||||
|
|
||||||
cloudflare_record_api_token: "{{ CERTBOT_DNS_API_TOKEN }}"
|
|
||||||
|
|
||||||
mailu_dmarc_ruf: "{{ applications | get_app_conf(application_id, 'users.administrator.email', True) }}"
|
|
||||||
|
|
||||||
mailu_dkim_key_file: "{{ applications | get_app_conf(application_id, 'domain', True) }}.dkim.key"
|
|
||||||
mailu_dkim_key_path: "/dkim/{{ mailu_dkim_key_file }}"
|
|
||||||
|
|
||||||
mailu_dns_srv_records:
|
|
||||||
submission:
|
|
||||||
port: 587
|
|
||||||
priority: 20
|
|
||||||
weight: 1
|
|
||||||
submissions:
|
|
||||||
port: 465
|
|
||||||
priority: 20
|
|
||||||
weight: 1
|
|
||||||
imaps:
|
|
||||||
port: 993
|
|
||||||
priority: 20
|
|
||||||
weight: 1
|
|
||||||
imap:
|
|
||||||
port: 143
|
|
||||||
priority: 20
|
|
||||||
weight: 1
|
|
||||||
pop3s:
|
|
||||||
port: 995
|
|
||||||
priority: 20
|
|
||||||
weight: 1
|
|
||||||
pop3:
|
|
||||||
port: 110
|
|
||||||
priority: 20
|
|
||||||
weight: 1
|
|
||||||
autodiscover:
|
|
||||||
port: "{{ WEB_PORT }}"
|
|
||||||
priority: 20
|
|
||||||
weight: 1
|
|
@ -1,26 +1,70 @@
|
|||||||
# General
|
# General
|
||||||
application_id: "web-app-mailu"
|
application_id: "web-app-mailu"
|
||||||
domain: "{{ domains | get_domain(application_id) }}"
|
|
||||||
http_port: "{{ ports.localhost.http[application_id] }}"
|
http_port: "{{ ports.localhost.http[application_id] }}"
|
||||||
proxy_extra_configuration: "client_max_body_size 31M;"
|
proxy_extra_configuration: "client_max_body_size 31M;"
|
||||||
|
|
||||||
# Database Configuration
|
# Database Configuration
|
||||||
database_password: "{{ applications | get_app_conf(application_id, ' credentials.database_password') }}"
|
database_password: "{{ applications | get_app_conf(application_id, 'credentials.database_password') }}"
|
||||||
database_type: "mariadb"
|
database_type: "mariadb"
|
||||||
|
|
||||||
|
# Cert Mount
|
||||||
cert_mount_directory: "{{ docker_compose.directories.volumes }}certs/"
|
cert_mount_directory: "{{ docker_compose.directories.volumes }}certs/"
|
||||||
|
|
||||||
|
# Mailu
|
||||||
|
|
||||||
|
## Meta
|
||||||
|
MAILU_WEBSITE: "{{ domains | get_url(application_id, WEB_PROTOCOL) }}"
|
||||||
|
|
||||||
|
## Domains
|
||||||
|
MAILU_DOMAIN: "{{ applications | get_app_conf(application_id, 'domain') }}"
|
||||||
|
MAILU_DOMAIN_DNS_ZONE: "{{ MAILU_DOMAIN | to_zone }}"
|
||||||
|
MAILU_HOSTNAMES: "{{ domains[application_id] }}"
|
||||||
|
MAILU_HOSTNAME: "{{ domains | get_domain(application_id) }}"
|
||||||
|
MAILU_HOSTNAME_DNS_ZONE: "{{ MAILU_HOSTNAME | to_zone }}"
|
||||||
|
|
||||||
|
## Docker
|
||||||
|
MAILU_VERSION: "{{ applications | get_app_conf(application_id, 'docker.services.mailu.version') }}"
|
||||||
|
MAILU_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.mailu.name') }}"
|
||||||
|
|
||||||
|
## Volumes
|
||||||
|
MAILU_SMTP_QUEUE_VOLUME: "mailu_smtp_queue"
|
||||||
|
MAILU_ADMIN_DATA_VOLUME: "mailu_admin_data"
|
||||||
|
MAILU_WEBDAV_DATA: "mailu_webdav_data"
|
||||||
|
MAILU_WEBMAIL_DATA: "mailu_webmail_data"
|
||||||
|
MAILU_FILTER_VOLUME: "mailu_filter"
|
||||||
|
MAILU_DKIM_VOLUME: "mailu_dkim"
|
||||||
|
MAILU_DOVECOT_MAIL_VOLUME: "mailu_dovecot_mail"
|
||||||
|
|
||||||
|
## Network
|
||||||
|
MAILU_DNS_RESOLVER: "{{ networks.local['web-app-mailu'].dns_resolver }}"
|
||||||
|
MAILU_IP4_PUBLIC: "{{ networks.internet.ip4 }}"
|
||||||
|
MAILU_IP6_PUBLIC: false #Deactivated atm. but cloudflare logic present
|
||||||
|
MAILU_SUBNET: "{{ networks.local['web-app-mailu'].subnet }}"
|
||||||
|
|
||||||
|
## Credentials
|
||||||
|
MAILU_SECRET_KEY: "{{ applications | get_app_conf(application_id,'credentials.secret_key') }}"
|
||||||
|
MAILU_CLOUDFLARE_API_TOKEN: "{{ CERTBOT_DNS_API_TOKEN }}"
|
||||||
|
MAILU_API_TOKEN: "{{ applications | get_app_conf(application_id, 'credentials.api_token') }}"
|
||||||
|
|
||||||
|
## OIDC
|
||||||
|
MAILU_OIDC_ENABLED: "{{ applications | get_app_conf(application_id, 'features.oidc', False) }}"
|
||||||
|
MAILU_OIDC_EMAIL_BY_USERNAME_ENABLED: "{{ applications | get_app_conf(application_id, 'oidc.email_by_username') }}"
|
||||||
|
MAILU_OIDC_ENABLE_USER_CREATION: "{{ applications | get_app_conf(application_id, 'oidc.enable_user_creation') | string | capitalize }}"
|
||||||
|
|
||||||
# Use dedicated source for oidc if activated
|
# Use dedicated source for oidc if activated
|
||||||
# @see https://github.com/heviat/Mailu-OIDC/tree/2024.06
|
# @see https://github.com/heviat/Mailu-OIDC/tree/2024.06
|
||||||
docker_source: "{{ 'ghcr.io/heviat' if applications | get_app_conf(application_id, 'features.oidc', False) else 'ghcr.io/mailu' }}"
|
MAILU_DOCKER_FLAVOR: "{{ 'ghcr.io/heviat' if MAILU_OIDC_ENABLED | bool else 'ghcr.io/mailu' }}"
|
||||||
|
|
||||||
# Mailu Specific
|
MAILU_DMARC_RUF: "{{ applications | get_app_conf(application_id, 'users.administrator.email') }}"
|
||||||
mailu_version: "{{ applications | get_app_conf(application_id, 'docker.services.mailu.version', True) }}"
|
|
||||||
mailu_name: "{{ applications | get_app_conf(application_id, 'docker.services.mailu.name', True) }}"
|
MAILU_DKIM_KEY_FILE: "{{ MAILU_DOMAIN }}.dkim.key"
|
||||||
mailu_smtp_queue: "mailu_smtp_queue"
|
MAILU_DKIM_KEY_PATH: "/dkim/{{ MAILU_DKIM_KEY_FILE }}"
|
||||||
mailu_admin_data: "mailu_admin_data"
|
|
||||||
mailu_webdav_data: "mailu_webdav_data"
|
MAILU_DNS_SRV_RECORDS:
|
||||||
mailu_webmail_data: "mailu_webmail_data"
|
submission: { port: 587, priority: 20, weight: 1 }
|
||||||
mailu_filter: "mailu_filter"
|
submissions: { port: 465, priority: 20, weight: 1 }
|
||||||
mailu_dkim: "mailu_dkim"
|
imaps: { port: 993, priority: 20, weight: 1 }
|
||||||
mailu_dovecot_mail: "mailu_dovecot_mail"
|
imap: { port: 143, priority: 20, weight: 1 }
|
||||||
|
pop3s: { port: 995, priority: 20, weight: 1 }
|
||||||
|
pop3: { port: 110, priority: 20, weight: 1 }
|
||||||
|
autodiscover: { port: 443, priority: 20, weight: 1 }
|
@ -60,16 +60,16 @@ SMTP_FROM_ADDRESS=Mastodon <{{ users['no-reply'].email }}>
|
|||||||
# @see https://stackoverflow.com/questions/72081776/how-mastodon-configured-login-using-sso
|
# @see https://stackoverflow.com/questions/72081776/how-mastodon-configured-login-using-sso
|
||||||
|
|
||||||
OIDC_ENABLED={{ applications | get_app_conf(application_id, 'features.oidc', False) | string | lower }}
|
OIDC_ENABLED={{ applications | get_app_conf(application_id, 'features.oidc', False) | string | lower }}
|
||||||
OIDC_DISPLAY_NAME="{{ oidc.button_text }}"
|
OIDC_DISPLAY_NAME="{{ OIDC.BUTTON_TEXT }}"
|
||||||
OIDC_ISSUER={{ oidc.client.issuer_url }}
|
OIDC_ISSUER={{ OIDC.CLIENT.ISSUER_URL }}
|
||||||
OIDC_DISCOVERY=true
|
OIDC_DISCOVERY=true
|
||||||
OIDC_SCOPE="openid,profile,email"
|
OIDC_SCOPE="openid,profile,email"
|
||||||
# @see https://stackoverflow.com/questions/72108087/how-to-set-the-username-of-mastodon-by-log-in-via-keycloak
|
# @see https://stackoverflow.com/questions/72108087/how-to-set-the-username-of-mastodon-by-log-in-via-keycloak
|
||||||
OIDC_UID_FIELD={{oidc.attributes.username}}
|
OIDC_UID_FIELD={{OIDC.ATTRIBUTES.USERNAME}}
|
||||||
OIDC_CLIENT_ID={{ oidc.client.id }}
|
OIDC_CLIENT_ID={{ OIDC.CLIENT.ID }}
|
||||||
OIDC_REDIRECT_URI=https://{{ domains | get_domain(application_id) }}/auth/auth/openid_connect/callback
|
OIDC_REDIRECT_URI=https://{{ domains | get_domain(application_id) }}/auth/auth/openid_connect/callback
|
||||||
OIDC_SECURITY_ASSUME_EMAIL_IS_VERIFIED=true
|
OIDC_SECURITY_ASSUME_EMAIL_IS_VERIFIED=true
|
||||||
OIDC_CLIENT_SECRET={{ oidc.client.secret }}
|
OIDC_CLIENT_SECRET={{ OIDC.CLIENT.SECRET }}
|
||||||
# uncomment to only use OIDC for login / registration buttons
|
# uncomment to only use OIDC for login / registration buttons
|
||||||
OMNIAUTH_ONLY=true
|
OMNIAUTH_ONLY=true
|
||||||
ALLOW_UNSAFE_AUTH_PROVIDER_REATTACH=true
|
ALLOW_UNSAFE_AUTH_PROVIDER_REATTACH=true
|
||||||
|
@ -50,14 +50,14 @@ email:
|
|||||||
# @See https://matrix-org.github.io/synapse/latest/openid.html
|
# @See https://matrix-org.github.io/synapse/latest/openid.html
|
||||||
oidc_providers:
|
oidc_providers:
|
||||||
- idp_id: keycloak
|
- idp_id: keycloak
|
||||||
idp_name: "{{ oidc.button_text }}"
|
idp_name: "{{ OIDC.BUTTON_TEXT }}"
|
||||||
issuer: "{{ oidc.client.issuer_url }}"
|
issuer: "{{ OIDC.CLIENT.ISSUER_URL }}"
|
||||||
client_id: "{{ oidc.client.id }}"
|
client_id: "{{ OIDC.CLIENT.ID }}"
|
||||||
client_secret: "{{ oidc.client.secret }}"
|
client_secret: "{{ OIDC.CLIENT.SECRET }}"
|
||||||
scopes: ["openid", "profile"]
|
scopes: ["openid", "profile"]
|
||||||
user_mapping_provider:
|
user_mapping_provider:
|
||||||
config:
|
config:
|
||||||
localpart_template: "{% raw %}{{ user.{% endraw %}{{oidc.attributes.username}}{% raw %}}}{% endraw %}"
|
localpart_template: "{% raw %}{{ user.{% endraw %}{{OIDC.ATTRIBUTES.USERNAME}}{% raw %}}}{% endraw %}"
|
||||||
display_name_template: "{% raw %}{{ user.name }}{% endraw %}"
|
display_name_template: "{% raw %}{{ user.name }}{% endraw %}"
|
||||||
backchannel_logout_enabled: true
|
backchannel_logout_enabled: true
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -264,15 +264,15 @@ config :ueberauth,
|
|||||||
|
|
||||||
config :mobilizon, :auth,
|
config :mobilizon, :auth,
|
||||||
oauth_consumer_strategies: [
|
oauth_consumer_strategies: [
|
||||||
{:keycloak, "{{ oidc.button_text }}"}
|
{:keycloak, "{{ OIDC.BUTTON_TEXT }}"}
|
||||||
]
|
]
|
||||||
|
|
||||||
config :ueberauth, Ueberauth.Strategy.Keycloak.OAuth,
|
config :ueberauth, Ueberauth.Strategy.Keycloak.OAuth,
|
||||||
client_id: "{{ oidc.client.id }}",
|
client_id: "{{ OIDC.CLIENT.ID }}",
|
||||||
client_secret: "{{ oidc.client.secret }}",
|
client_secret: "{{ OIDC.CLIENT.SECRET }}",
|
||||||
site: "{{ oidc.url }}",
|
site: "{{ OIDC.URL }}",
|
||||||
authorize_url: "{{ oidc.client.authorize_url }}",
|
authorize_url: "{{ OIDC.CLIENT.AUTHORIZE_URL }}",
|
||||||
token_url: "{{ oidc.client.token_url }}",
|
token_url: "{{ OIDC.CLIENT.TOKEN_URL }}",
|
||||||
userinfo_url: "{{ oidc.client.user_info_url }}",
|
userinfo_url: "{{ OIDC.CLIENT.USER_INFO_URL }}",
|
||||||
token_method: :post
|
token_method: :post
|
||||||
{% endif %}
|
{% endif %}
|
@ -23,19 +23,19 @@
|
|||||||
loop:
|
loop:
|
||||||
- { name: "idptype", value: 3 }
|
- { name: "idptype", value: 3 }
|
||||||
- { name: "clientauthmethod", value: 1 }
|
- { name: "clientauthmethod", value: 1 }
|
||||||
- { name: "clientid", value: "{{ oidc.client.id }}" }
|
- { name: "clientid", value: "{{ OIDC.CLIENT.ID }}" }
|
||||||
- { name: "clientsecret", value: "{{ oidc.client.secret }}" }
|
- { name: "clientsecret", value: "{{ OIDC.CLIENT.SECRET }}" }
|
||||||
- { name: "opname", value: "{{ oidc.button_text }}" }
|
- { name: "opname", value: "{{ OIDC.BUTTON_TEXT }}" }
|
||||||
- { name: "oidcscope", value: "openid profile email" }
|
- { name: "oidcscope", value: "openid profile email" }
|
||||||
- { name: "authendpoint", value: "{{ oidc.client.authorize_url }}" }
|
- { name: "authendpoint", value: "{{ OIDC.CLIENT.AUTHORIZE_URL }}" }
|
||||||
- { name: "tokenendpoint", value: "{{ oidc.client.token_url }}" }
|
- { name: "tokenendpoint", value: "{{ OIDC.CLIENT.TOKEN_URL }}" }
|
||||||
- { name: "bindingusernameclaim", value: "{{ oidc.attributes.username }}" }
|
- { name: "bindingusernameclaim", value: "{{ OIDC.ATTRIBUTES.USERNAME }}" }
|
||||||
- { name: "single_sign_off", value: 1 } # Logs the user out from the IDP
|
- { name: "single_sign_off", value: 1 } # Logs the user out from the IDP
|
||||||
- { name: "logouturi", value: "{{ oidc.client.logout_url }}" }
|
- { name: "logouturi", value: "{{ OIDC.CLIENT.LOGOUT_URL }}" }
|
||||||
- { name: "icon", value: "moodle:t/lock" }
|
- { name: "icon", value: "moodle:t/lock" }
|
||||||
- { name: "field_map_firstname", value: "{{ oidc.attributes.given_name }}" }
|
- { name: "field_map_firstname", value: "{{ OIDC.ATTRIBUTES.GIVEN_NAME }}" }
|
||||||
- { name: "field_lock_firstname", value: "locked" }
|
- { name: "field_lock_firstname", value: "locked" }
|
||||||
- { name: "field_map_lastname", value: "{{ oidc.attributes.family_name }}" }
|
- { name: "field_map_lastname", value: "{{ OIDC.ATTRIBUTES.FAMILY_NAME }}" }
|
||||||
- { name: "field_lock_lastname", value: "locked" }
|
- { name: "field_lock_lastname", value: "locked" }
|
||||||
- { name: "field_map_email", value: "locked" }
|
- { name: "field_map_email", value: "locked" }
|
||||||
#- { name: "showloginform", value: 0 } # Deactivate if OIDC is active
|
#- { name: "showloginform", value: 0 } # Deactivate if OIDC is active
|
||||||
|
@ -11,11 +11,11 @@ return array (
|
|||||||
'lost_password_link' => 'disabled',
|
'lost_password_link' => 'disabled',
|
||||||
|
|
||||||
// URL of provider. All other URLs are auto-discovered from .well-known
|
// URL of provider. All other URLs are auto-discovered from .well-known
|
||||||
'oidc_login_provider_url' => '{{ oidc.client.issuer_url }}',
|
'oidc_login_provider_url' => '{{ OIDC.CLIENT.ISSUER_URL }}',
|
||||||
|
|
||||||
// Client ID and secret registered with the provider
|
// Client ID and secret registered with the provider
|
||||||
'oidc_login_client_id' => '{{ oidc.client.id }}',
|
'oidc_login_client_id' => '{{ OIDC.CLIENT.ID }}',
|
||||||
'oidc_login_client_secret' => '{{ oidc.client.secret }}',
|
'oidc_login_client_secret' => '{{ OIDC.CLIENT.SECRET }}',
|
||||||
|
|
||||||
// Automatically redirect the login page to the provider
|
// Automatically redirect the login page to the provider
|
||||||
'oidc_login_auto_redirect' => true,
|
'oidc_login_auto_redirect' => true,
|
||||||
@ -36,7 +36,7 @@ return array (
|
|||||||
'oidc_login_default_quota' => '{{applications | get_app_conf(application_id, 'default_quota', True)}}',
|
'oidc_login_default_quota' => '{{applications | get_app_conf(application_id, 'default_quota', True)}}',
|
||||||
|
|
||||||
// Login button text
|
// Login button text
|
||||||
'oidc_login_button_text' => '{{ oidc.button_text }}',
|
'oidc_login_button_text' => '{{ OIDC.BUTTON_TEXT }}',
|
||||||
|
|
||||||
// Hide the NextCloud password change form.
|
// Hide the NextCloud password change form.
|
||||||
'oidc_login_hide_password_form' => true,
|
'oidc_login_hide_password_form' => true,
|
||||||
@ -102,7 +102,7 @@ return array (
|
|||||||
'mail' => 'email',
|
'mail' => 'email',
|
||||||
'quota' => '{{ ldap.user.attributes.nextcloud_quota }}',
|
'quota' => '{{ ldap.user.attributes.nextcloud_quota }}',
|
||||||
# 'home' => 'homeDirectory', # Not implemented yet
|
# 'home' => 'homeDirectory', # Not implemented yet
|
||||||
'ldap_uid' => '{{oidc.attributes.username}}',
|
'ldap_uid' => '{{OIDC.ATTRIBUTES.USERNAME}}',
|
||||||
# 'groups' => 'ownCloudGroups', # Not implemented yet
|
# 'groups' => 'ownCloudGroups', # Not implemented yet
|
||||||
# 'login_filter' => 'realm_access_roles',
|
# 'login_filter' => 'realm_access_roles',
|
||||||
// 'photoURL' => 'picture',
|
// 'photoURL' => 'picture',
|
||||||
|
@ -28,13 +28,13 @@ plugin_configuration:
|
|||||||
- name: "{{ domains | get_domain('web-app-keycloak') }}"
|
- name: "{{ domains | get_domain('web-app-keycloak') }}"
|
||||||
title: "keycloak"
|
title: "keycloak"
|
||||||
style: "keycloak"
|
style: "keycloak"
|
||||||
authorizeUrl: "{{ oidc.client.authorize_url }}"
|
authorizeUrl: "{{ OIDC.CLIENT.AUTHORIZE_URL }}"
|
||||||
tokenUrl: "{{ oidc.client.token_url }}"
|
tokenUrl: "{{ OIDC.CLIENT.TOKEN_URL }}"
|
||||||
displayNameClaim: ""
|
displayNameClaim: ""
|
||||||
userInfoUrl: "{{ oidc.client.user_info_url }}"
|
userInfoUrl: "{{ OIDC.CLIENT.USER_INFO_URL }}"
|
||||||
logoutUrl: "{{ oidc.client.logout_url }}"
|
logoutUrl: "{{ OIDC.CLIENT.LOGOUT_URL }}"
|
||||||
clientId: "{{ oidc.client.id }}"
|
clientId: "{{ OIDC.CLIENT.ID }}"
|
||||||
clientSecret: "{{ oidc.client.secret }}"
|
clientSecret: "{{ OIDC.CLIENT.SECRET }}"
|
||||||
scope: "openid"
|
scope: "openid"
|
||||||
groupsClaim: ""
|
groupsClaim: ""
|
||||||
defaultGroup: ""
|
defaultGroup: ""
|
||||||
|
@ -6,17 +6,17 @@ cookie_domains = ["{{ domains | get_domain(oauth2_proxy_application_i
|
|||||||
whitelist_domains = [".{{ PRIMARY_DOMAIN }}"] # Required to allow redirection back to original requested target.
|
whitelist_domains = [".{{ PRIMARY_DOMAIN }}"] # Required to allow redirection back to original requested target.
|
||||||
|
|
||||||
# keycloak provider
|
# keycloak provider
|
||||||
client_secret = "{{ oidc.client.secret }}"
|
client_secret = "{{ OIDC.CLIENT.SECRET }}"
|
||||||
client_id = "{{ oidc.client.id }}"
|
client_id = "{{ OIDC.CLIENT.ID }}"
|
||||||
redirect_url = "{{ WEB_PROTOCOL }}://{{ domains | get_domain(oauth2_proxy_application_id) }}/oauth2/callback"
|
redirect_url = "{{ WEB_PROTOCOL }}://{{ domains | get_domain(oauth2_proxy_application_id) }}/oauth2/callback"
|
||||||
oidc_issuer_url = "{{ oidc.client.issuer_url }}"
|
oidc_issuer_url = "{{ OIDC.CLIENT.ISSUER_URL }}"
|
||||||
provider = "oidc"
|
provider = "oidc"
|
||||||
provider_display_name = "{{ oidc.button_text }}"
|
provider_display_name = "{{ OIDC.BUTTON_TEXT }}"
|
||||||
|
|
||||||
{% if applications | get_app_conf(oauth2_proxy_application_id, 'oauth2_proxy.allowed_groups', False) %}
|
{% if applications | get_app_conf(oauth2_proxy_application_id, 'oauth2_proxy.allowed_groups', False) %}
|
||||||
{# role based restrictions #}
|
{# role based restrictions #}
|
||||||
scope = "openid email profile {{ oidc.claims.groups }}"
|
scope = "openid email profile {{ OIDC.CLAIMS.GROUPS }}"
|
||||||
oidc_groups_claim = "{{ oidc.claims.groups }}"
|
oidc_groups_claim = "{{ OIDC.CLAIMS.GROUPS }}"
|
||||||
allowed_groups = {{ applications | get_app_conf(oauth2_proxy_application_id, 'oauth2_proxy.allowed_groups', True) | tojson }}
|
allowed_groups = {{ applications | get_app_conf(oauth2_proxy_application_id, 'oauth2_proxy.allowed_groups', True) | tojson }}
|
||||||
email_domains = ["*"]
|
email_domains = ["*"]
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
oidc_settings: |
|
oidc_settings: |
|
||||||
{
|
{
|
||||||
"scope": "openid email profile",
|
"scope": "openid email profile",
|
||||||
"desk-id": "{{ oidc.client.id }}",
|
"desk-id": "{{ OIDC.CLIENT.ID }}",
|
||||||
"discover-url": "{{ oidc.client.discovery_document }}",
|
"discover-url": "{{ OIDC.CLIENT.DISCOVERY_DOCUMENT }}",
|
||||||
"desk-secret": "{{ oidc.client.secret }}",
|
"desk-secret": "{{ OIDC.CLIENT.SECRET }}",
|
||||||
"mail-property": "email",
|
"mail-property": "email",
|
||||||
"auth-display-name": "{{ oidc.button_text }}",
|
"auth-display-name": "{{ OIDC.BUTTON_TEXT }}",
|
||||||
"username-property": "{{ oidc.attributes.username }}",
|
"username-property": "{{ OIDC.ATTRIBUTES.USERNAME }}",
|
||||||
"signature-algorithm": "RS256",
|
"signature-algorithm": "RS256",
|
||||||
"display-name-property": "{{ oidc.attributes.username }}"
|
"display-name-property": "{{ OIDC.ATTRIBUTES.USERNAME }}"
|
||||||
}
|
}
|
||||||
|
@ -141,14 +141,14 @@ ENABLE_CONFIG_CACHE=true
|
|||||||
# @see https://github.com/pixelfed/pixelfed/commit/b3c27815788e4b47e7eb3fca727d817512cf26c2#diff-66e408190a301e81b5f1c079463487c54a6452c4944dc5ae80770f50101283ff
|
# @see https://github.com/pixelfed/pixelfed/commit/b3c27815788e4b47e7eb3fca727d817512cf26c2#diff-66e408190a301e81b5f1c079463487c54a6452c4944dc5ae80770f50101283ff
|
||||||
|
|
||||||
PF_OIDC_ENABLED={{ applications | get_app_conf(application_id, 'features.oidc', False) | string | lower }}
|
PF_OIDC_ENABLED={{ applications | get_app_conf(application_id, 'features.oidc', False) | string | lower }}
|
||||||
PF_OIDC_AUTHORIZE_URL="{{oidc.client.authorize_url}}"
|
PF_OIDC_AUTHORIZE_URL="{{ OIDC.CLIENT.AUTHORIZE_URL }}"
|
||||||
PF_OIDC_TOKEN_URL="{{oidc.client.token_url}}"
|
PF_OIDC_TOKEN_URL="{{OIDC.CLIENT.TOKEN_URL}}"
|
||||||
PF_OIDC_PROFILE_URL="{{ oidc.client.user_info_url }}"
|
PF_OIDC_PROFILE_URL="{{ OIDC.CLIENT.USER_INFO_URL }}"
|
||||||
PF_OIDC_LOGOUT_URL="{{oidc.client.logout_url}}"
|
PF_OIDC_LOGOUT_URL="{{OIDC.CLIENT.LOGOUT_URL}}"
|
||||||
PF_OIDC_USERNAME_FIELD="{{oidc.attributes.username}}"
|
PF_OIDC_USERNAME_FIELD="{{OIDC.ATTRIBUTES.USERNAME}}"
|
||||||
PF_OIDC_FIELD_ID="{{oidc.attributes.username}}"
|
PF_OIDC_FIELD_ID="{{OIDC.ATTRIBUTES.USERNAME}}"
|
||||||
PF_OIDC_CLIENT_SECRET={{ oidc.client.secret }}
|
PF_OIDC_CLIENT_SECRET={{ OIDC.CLIENT.SECRET }}
|
||||||
PF_OIDC_CLIENT_ID={{ oidc.client.id }}
|
PF_OIDC_CLIENT_ID={{ OIDC.CLIENT.ID }}
|
||||||
PF_OIDC_SCOPES="openid profile email"
|
PF_OIDC_SCOPES="openid profile email"
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
@ -38,13 +38,13 @@ applications:
|
|||||||
description: Update your personal admin settings
|
description: Update your personal admin settings
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-user-gear
|
class: fa-solid fa-user-gear
|
||||||
url: {{ keycloak_url }}/realms/{{ oidc.client.id }}/account
|
url: {{ keycloak_url }}/realms/{{ OIDC.CLIENT.ID }}/account
|
||||||
iframe: {{ applications | get_app_conf( 'web-app-keycloak', 'features.desktop', False) }}
|
iframe: {{ applications | get_app_conf( 'web-app-keycloak', 'features.desktop', False) }}
|
||||||
- name: Logout
|
- name: Logout
|
||||||
description: End your admin session securely
|
description: End your admin session securely
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-right-from-bracket
|
class: fa-solid fa-right-from-bracket
|
||||||
url: {{ keycloak_url }}/realms/{{ oidc.client.id }}/protocol/openid-connect/logout
|
url: {{ keycloak_url }}/realms/{{ OIDC.CLIENT.ID }}/protocol/openid-connect/logout
|
||||||
iframe: false
|
iframe: false
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
$s->ad_domain = "";
|
$s->ad_domain = "";
|
||||||
$s->ldap_default_group = "";
|
$s->ldap_default_group = "";
|
||||||
$s->ldap_email = "{{ ldap.user.attributes.mail }}";
|
$s->ldap_email = "{{ ldap.user.attributes.mail }}";
|
||||||
$s->custom_forgot_pass_url = "{{ oidc.client.reset_credentials }}";
|
$s->custom_forgot_pass_url = "{{ OIDC.CLIENT.RESET_CREDENTIALS }}";
|
||||||
$s->save();
|
$s->save();
|
||||||
EOF'
|
EOF'
|
||||||
args:
|
args:
|
||||||
|
@ -54,14 +54,14 @@ ENABLE_TELEMETRY = True
|
|||||||
# OIDC via taigaio official contrib
|
# OIDC via taigaio official contrib
|
||||||
# @See https://github.com/taigaio/taiga-contrib-oidc-auth
|
# @See https://github.com/taigaio/taiga-contrib-oidc-auth
|
||||||
|
|
||||||
OIDC_RP_CLIENT_ID="{{ oidc.client.id }}"
|
OIDC_RP_CLIENT_ID="{{ OIDC.CLIENT.ID }}"
|
||||||
OIDC_RP_CLIENT_SECRET="{{ oidc.client.secret }}"
|
OIDC_RP_CLIENT_SECRET="{{ OIDC.CLIENT.SECRET }}"
|
||||||
OIDC_OP_AUTHORIZATION_ENDPOINT="{{ oidc.client.authorize_url }}"
|
OIDC_OP_AUTHORIZATION_ENDPOINT="{{ OIDC.CLIENT.AUTHORIZE_URL }}"
|
||||||
OIDC_OP_TOKEN_ENDPOINT="{{ oidc.client.token_url }}"
|
OIDC_OP_TOKEN_ENDPOINT="{{ OIDC.CLIENT.TOKEN_URL }}"
|
||||||
OIDC_OP_USER_ENDPOINT="{{ oidc.client.user_info_url }}"
|
OIDC_OP_USER_ENDPOINT="{{ OIDC.CLIENT.USER_INFO_URL }}"
|
||||||
OIDC_RP_SIGN_ALGO="RS256"
|
OIDC_RP_SIGN_ALGO="RS256"
|
||||||
OIDC_RP_SCOPES="openid profile email"
|
OIDC_RP_SCOPES="openid profile email"
|
||||||
OIDC_OP_JWKS_ENDPOINT="{{ oidc.client.certs }}"
|
OIDC_OP_JWKS_ENDPOINT="{{ OIDC.CLIENT.CERTS }}"
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
@ -70,13 +70,13 @@ OIDC_OP_JWKS_ENDPOINT="{{ oidc.client.certs }}"
|
|||||||
# OIDC via robrotheram
|
# OIDC via robrotheram
|
||||||
# @see https://github.com/robrotheram/taiga-contrib-openid-auth
|
# @see https://github.com/robrotheram/taiga-contrib-openid-auth
|
||||||
ENABLE_OPENID=True
|
ENABLE_OPENID=True
|
||||||
OPENID_URL="{{oidc.client.authorize_url}}"
|
OPENID_URL="{{ OIDC.CLIENT.AUTHORIZE_URL }}"
|
||||||
OPENID_USER_URL="{{oidc.client.user_info_url}}"
|
OPENID_USER_URL="{{OIDC.CLIENT.USER_INFO_URL}}"
|
||||||
OPENID_TOKEN_URL="{{oidc.client.token_url}}"
|
OPENID_TOKEN_URL="{{OIDC.CLIENT.TOKEN_URL}}"
|
||||||
OPENID_CLIENT_ID="{{ oidc.client.id }}"
|
OPENID_CLIENT_ID="{{ OIDC.CLIENT.ID }}"
|
||||||
OPENID_CLIENT_SECRET="{{ oidc.client.secret }}"
|
OPENID_CLIENT_SECRET="{{ OIDC.CLIENT.SECRET }}"
|
||||||
OPENID_NAME="{{ oidc.button_text }}"
|
OPENID_NAME="{{ OIDC.BUTTON_TEXT }}"
|
||||||
OPENID_USERNAME_FIELD="{{oidc.attributes.username}}"
|
OPENID_USERNAME_FIELD="{{OIDC.ATTRIBUTES.USERNAME}}"
|
||||||
# Optional:
|
# Optional:
|
||||||
# OPENID_ID_FIELD="sub"
|
# OPENID_ID_FIELD="sub"
|
||||||
# OPENID_FULLNAME_FIELD="name"
|
# OPENID_FULLNAME_FIELD="name"
|
||||||
|
@ -3,12 +3,12 @@
|
|||||||
# @see https://github.com/oidc-wp/openid-connect-generic/blob/develop/includes/openid-connect-dev-option-settings.php
|
# @see https://github.com/oidc-wp/openid-connect-generic/blob/develop/includes/openid-connect-dev-option-settings.php
|
||||||
|
|
||||||
oidc_settings:
|
oidc_settings:
|
||||||
client_id: "{{ oidc.client.id }}" # The client ID that identifies WordPress as the OIDC client.
|
client_id: "{{ OIDC.CLIENT.ID }}" # The client ID that identifies WordPress as the OIDC client.
|
||||||
client_secret: "{{ oidc.client.secret }}" # The secret key used by WordPress to authenticate to the OIDC provider.
|
client_secret: "{{ OIDC.CLIENT.SECRET }}" # The secret key used by WordPress to authenticate to the OIDC provider.
|
||||||
endpoint_login: "{{ oidc.client.authorize_url }}" # URL of the authorization endpoint to initiate the login flow.
|
endpoint_login: "{{ OIDC.CLIENT.AUTHORIZE_URL }}" # URL of the authorization endpoint to initiate the login flow.
|
||||||
endpoint_token: "{{ oidc.client.token_url }}" # URL of the token endpoint for exchanging authorization codes for tokens.
|
endpoint_token: "{{ OIDC.CLIENT.TOKEN_URL }}" # URL of the token endpoint for exchanging authorization codes for tokens.
|
||||||
endpoint_userinfo: "{{ oidc.client.user_info_url }}" # URL of the userinfo endpoint to retrieve user profile data.
|
endpoint_userinfo: "{{ OIDC.CLIENT.USER_INFO_URL }}" # URL of the userinfo endpoint to retrieve user profile data.
|
||||||
endpoint_end_session: "{{ oidc.client.logout_url }}" # URL of the end-session endpoint to log users out of the IDP.
|
endpoint_end_session: "{{ OIDC.CLIENT.LOGOUT_URL }}" # URL of the end-session endpoint to log users out of the IDP.
|
||||||
login_type: "auto" # Determines how the login interface is rendered (e.g., button or form).
|
login_type: "auto" # Determines how the login interface is rendered (e.g., button or form).
|
||||||
scope: "openid profile email" # Scopes requested from the OIDC provider during authentication.
|
scope: "openid profile email" # Scopes requested from the OIDC provider during authentication.
|
||||||
create_if_does_not_exist: true # Auto-create a new WP user if one doesn’t exist.
|
create_if_does_not_exist: true # Auto-create a new WP user if one doesn’t exist.
|
||||||
@ -16,14 +16,14 @@ oidc_settings:
|
|||||||
link_existing_users: true # Link OIDC login to existing WP users by matching email.
|
link_existing_users: true # Link OIDC login to existing WP users by matching email.
|
||||||
redirect_on_logout: true # Redirect users after logout to the login screen or homepage.
|
redirect_on_logout: true # Redirect users after logout to the login screen or homepage.
|
||||||
redirect_user_back: true # Return users to their original URL after successful login.
|
redirect_user_back: true # Return users to their original URL after successful login.
|
||||||
#acr_values: "{{ oidc.client.acr_values | default('') }}" # ACR values defining required authentication context (e.g., MFA level).
|
#acr_values: "{{ OIDC.CLIENT.acr_values | default('') }}" # ACR values defining required authentication context (e.g., MFA level).
|
||||||
enable_logging: "{{ MODE_DEBUG }}" # Enable detailed plugin logging for debugging and auditing.
|
enable_logging: "{{ MODE_DEBUG }}" # Enable detailed plugin logging for debugging and auditing.
|
||||||
# log_limit: "{{ oidc.client.log_limit | default('') }}" # Maximum number of log entries to retain before pruning.
|
# log_limit: "{{ OIDC.CLIENT.log_limit | default('') }}" # Maximum number of log entries to retain before pruning.
|
||||||
no_sslverify: false # The flag to enable/disable SSL verification during authorization.
|
no_sslverify: false # The flag to enable/disable SSL verification during authorization.
|
||||||
http_request_timeout: 5 # The timeout for requests made to the IDP. Default value is 5.
|
http_request_timeout: 5 # The timeout for requests made to the IDP. Default value is 5.
|
||||||
identity_key: "{{ oidc.attributes.username }}" # The key in the user claim array to find the user's identification data.
|
identity_key: "{{ OIDC.ATTRIBUTES.USERNAME }}" # The key in the user claim array to find the user's identification data.
|
||||||
nickname_key: "{{ oidc.attributes.username }}" # The key in the user claim array to find the user's nickname.
|
nickname_key: "{{ OIDC.ATTRIBUTES.USERNAME }}" # The key in the user claim array to find the user's nickname.
|
||||||
email_format: "{{ oidc.attributes.email }}" # The key(s) in the user claim array to formulate the user's email address.
|
email_format: "{{ OIDC.ATTRIBUTES.EMAIL }}" # The key(s) in the user claim array to formulate the user's email address.
|
||||||
displayname_format: "{{ oidc.attributes.given_name }} {{ oidc.attributes.family_name }}" # The key(s) in the user claim array to formulate the user's display name.
|
displayname_format: "{{ OIDC.ATTRIBUTES.GIVEN_NAME }} {{ OIDC.ATTRIBUTES.FAMILY_NAME }}" # The key(s) in the user claim array to formulate the user's display name.
|
||||||
identify_with_username: true # The flag which indicates how the user's identity will be determined.
|
identify_with_username: true # The flag which indicates how the user's identity will be determined.
|
||||||
state_time_limit: 180 # The valid time limit of the state, in seconds. Defaults to 180 seconds.
|
state_time_limit: 180 # The valid time limit of the state, in seconds. Defaults to 180 seconds.
|
@ -83,9 +83,9 @@
|
|||||||
set_fact:
|
set_fact:
|
||||||
networks: "{{ defaults_networks | combine(networks | default({}, true), recursive=True) }}"
|
networks: "{{ defaults_networks | combine(networks | default({}, true), recursive=True) }}"
|
||||||
|
|
||||||
- name: Merge oidc configuration
|
- name: Merge OIDC configuration
|
||||||
set_fact:
|
set_fact:
|
||||||
oidc: "{{ defaults_oidc | combine(oidc | default({}, true), recursive=True) }}"
|
OIDC: "{{ defaults_oidc | combine(OIDC | default({}, true), recursive=True) }}"
|
||||||
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
||||||
|
|
||||||
- name: Merge design configuration
|
- name: Merge design configuration
|
||||||
|
49
tests/unit/filter_plugins/test_domain_tools.py
Normal file
49
tests/unit/filter_plugins/test_domain_tools.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
# Make project root importable so "filter_plugins.domain_tools" works no matter where tests run from
|
||||||
|
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
PROJECT_ROOT = os.path.abspath(os.path.join(THIS_DIR, '..', '..', '..'))
|
||||||
|
if PROJECT_ROOT not in sys.path:
|
||||||
|
sys.path.insert(0, PROJECT_ROOT)
|
||||||
|
|
||||||
|
from ansible.errors import AnsibleFilterError # noqa: E402
|
||||||
|
from filter_plugins.domain_tools import to_zone, FilterModule # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
class TestDomainTools(unittest.TestCase):
|
||||||
|
def test_to_zone_basic(self):
|
||||||
|
self.assertEqual(to_zone("example.com"), "example.com")
|
||||||
|
self.assertEqual(to_zone("mail.example.com"), "example.com")
|
||||||
|
self.assertEqual(to_zone("a.b.c.example.com"), "example.com")
|
||||||
|
|
||||||
|
def test_to_zone_trailing_and_leading_dots(self):
|
||||||
|
self.assertEqual(to_zone("example.com."), "example.com")
|
||||||
|
self.assertEqual(to_zone(".mail.example.com."), "example.com")
|
||||||
|
|
||||||
|
def test_to_zone_keeps_two_last_labels(self):
|
||||||
|
# Naive behavior by design: last two labels only
|
||||||
|
self.assertEqual(to_zone("service.co.uk"), "co.uk")
|
||||||
|
self.assertEqual(to_zone("mx.mail.service.co.uk"), "co.uk")
|
||||||
|
self.assertEqual(to_zone("uni.edu.pl"), "edu.pl")
|
||||||
|
|
||||||
|
def test_to_zone_invalid_inputs(self):
|
||||||
|
with self.assertRaises(AnsibleFilterError):
|
||||||
|
to_zone("") # empty
|
||||||
|
with self.assertRaises(AnsibleFilterError):
|
||||||
|
to_zone(" ") # whitespace
|
||||||
|
with self.assertRaises(AnsibleFilterError):
|
||||||
|
to_zone("localhost") # no TLD part
|
||||||
|
with self.assertRaises(AnsibleFilterError):
|
||||||
|
to_zone(None) # type: ignore
|
||||||
|
|
||||||
|
def test_filtermodule_exports(self):
|
||||||
|
fm = FilterModule()
|
||||||
|
filters = fm.filters()
|
||||||
|
self.assertIn("to_zone", filters)
|
||||||
|
self.assertIs(filters["to_zone"], to_zone)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
Loading…
x
Reference in New Issue
Block a user