Compare commits

...

16 Commits

Author SHA1 Message Date
7f42462514 Fixed reload button bug 2025-08-13 23:50:35 +02:00
41cd6b7702 Replaced get_domain with get_url 2025-08-13 23:33:49 +02:00
a40d48bb03 Refactor srv-web-7-7-inj-port-ui-desktop to use CDN-served JS file with inline initializer
- Added vars/main.yml to define iframe-handler.js file name and destination
- Implemented 01_deploy.yml to deploy iframe-handler.js to CDN and set mtime-based version fact
- Split original iframe logic into:
  • iframe-handler.js (full logic, served from CDN)
  • iframe-init_one_liner.js.j2 (small inline bootstrap, CSP-hashed)
- Updated head_sub.j2 to load script from CDN instead of embedding full code
- Added body_sub.j2 for inline init code
- Updated iframe-handler.js.j2 with initIframeHandler() function and global exposure
- Activated role earlier in inj-compose with public: true so vars are available for templates
- Included 'port-ui-desktop' in body_snippets loop in location.lua.j2
- Disabled 'port-ui-desktop' feature in web-svc-cdn config by default

https://chatgpt.com/share/689d03a8-4c28-800f-8b06-58ce2807b075
2025-08-13 23:29:32 +02:00
2fba32d384 Solved listmonk path bug 2025-08-13 22:39:43 +02:00
f2a765d69a Removed unused ansible matrix role 2025-08-13 22:01:09 +02:00
c729edb525 Refactor async task handling
- Standardize async/poll usage with 'ASYNC_ENABLED | bool'
- Add async/poll parameters to Cloudflare, Nginx, Mailu, MIG, Nextcloud, and OpenLDAP tasks
- Update async configuration in 'group_vars/all/00_general.yml' to ensure boolean evaluation
- Allow CAA, cache, and DNS tasks to run asynchronously when enabled

https://chatgpt.com/share/689cd8cc-7fbc-800f-bd06-a667561573bf
2025-08-13 21:56:26 +02:00
597e9d5222 Refactor async execution handling across LDAP and Nextcloud roles
- Introduce global async configuration in group_vars/all/00_general.yml:
  - ASYNC_ENABLED (disabled in debug mode)
  - ASYNC_TIME (default 300s, omitted if async disabled)
  - ASYNC_POLL (0 for async fire-and-forget, 10 for sync mode)
- Replace hardcoded async/poll values with global vars in:
  - svc-db-openldap (03_users.yml, 04_update.yml)
  - web-app-mig (02_build_data.yml)
  - web-app-nextcloud (03_admin.yml, 04_system_config.yml, 05_plugin.yml,
    06_plugin_routines.yml, 07_plugin_enable_and_configure.yml)
- Guard changed_when and failed_when conditions to only evaluate in synchronous
  mode to avoid accessing undefined rc/stdout/stderr in async runs

  https://chatgpt.com/share/689cd8cc-7fbc-800f-bd06-a667561573bf
2025-08-13 20:26:40 +02:00
db0e030900 Renamed general and mode constants and implemented a check to verify that constants are just defined ones over the whole repository 2025-08-13 19:11:14 +02:00
004507e233 Optimized handler flushing 2025-08-13 18:17:05 +02:00
e2014b9b59 nextcloud(role): remove async → use batched shell; more robust changed_when/failed_when; fix quoting; refactor plugin routines; clean up vars
• 02_add_missing_indices.yml: switched to shell (+ansible_command_timeout), removed async/poll.

• 04_system_config.yml: batch OCC calls (set -euo pipefail, /bin/bash), safer quoting, change detection via ' set to '.

• 05_plugin.yml: disable task with stricter failed_when/changed_when (combine stdout+stderr).

• 06_plugin_routines.yml: disable incompatible plugins in a single batch; no async_status; robust changed_when.

• 07_plugin_enable_and_configure.yml: batch config:app:set, safe quoting, clear changed_when/failed_when.

• config/main.yml & vars/main.yml: removed performance.async.wait_for and nextcloud_wait_for_async_enabled.
2025-08-13 18:15:50 +02:00
567b1365c0 Nextcloud: async overhaul & task refactor (conditional wait, faster polling)
• Add config.performance.async.wait_for and expose as nextcloud_wait_for_async_enabled to toggle waiting for async jobs.

• Split system/admin/index maintenance into separate tasks: 02_add_missing_indices.yml, 03_admin.yml, 04_system_config.yml.

• Refactor plugin flow: rename 02_plugin→05_plugin, 03_plugin_routines→06_plugin_routines, 04_plugin_enable_and_configure→07_plugin_enable_and_configure; remove old 03_plugin_routines and 05_system.

• Harden async handling: filter async_status loops by ansible_job_id; conditionally wait only when nextcloud_wait_for_async_enabled; reduce delay to 1s.

• Reorder main.yml to run system steps before plugin setup; keep handlers flush earlier.

• env.j2: simplify get_app_conf lookups (drop extra True flag).

• vars/main.yml: add nextcloud_host_nginx_path and nextcloud_wait_for_async_enabled.

https://chatgpt.com/share/689c9d4a-1748-800f-b490-06a5a48dd831
2025-08-13 16:13:00 +02:00
e99fa77b91 Optimized docker handlers for espocrm and wordpress 2025-08-13 13:34:12 +02:00
80dad1a5ed Removed proxy_extra_configuration fact 2025-08-13 06:32:52 +02:00
03290eafe1 feat(proxy,bigbluebutton): use parameterized HTML location template & add build retry
- proxy(html.conf.j2):
  * Make proxy_pass more robust (strip '=', '^~' prefixes; ignore @/~ match locations)
  * Switch WS header to $connection_upgrade
  * Unify timeouts (proxy_connect_timeout 5s)
  * Lua optional: include only when proxy_lua_enabled=true; unset Accept-Encoding only then
  * Buffering via flag: proxy_buffering/proxy_request_buffering 'on' with Lua, otherwise 'off'
- proxy(media.conf.j2): minor formatting/spacing fix
- inj-css(head_sub.j2): consistent spacing for global_css_version
- bigbluebutton(tasks/main.yml):
  * Render HTML location block once before include_role (location='^~ /html5client', OAuth2/Lua disabled)
  * Pass rendered snippet via proxy_extra_configuration to the vHost
  * Cleanup afterwards: proxy_extra_configuration = undef()
- docker-compose(handlers):
  * Build with retry: if 'docker compose build' fails -> retry with '--no-cache --pull'
  * Enable BuildKit (DOCKER_BUILDKIT=1, COMPOSE_DOCKER_CLI_BUILD=1)
- vars: trailing newline / minor formatting

Motivation:
- BBB HTML5 client (^~ /html5client) needs a separate location without Lua/buffering.
- More resilient CI/CD builds via automatic no-cache retry.
- Cleaner headers/proxy defaults and fewer side effects.

Files:
- roles/docker-compose/handlers/main.yml
- roles/srv-proxy-7-4-core/templates/location/html.conf.j2
- roles/srv-proxy-7-4-core/templates/location/media.conf.j2
- roles/srv-web-7-7-inj-css/templates/head_sub.j2
- roles/web-app-bigbluebutton/tasks/main.yml
- roles/web-app-bigbluebutton/vars/main.yml
2025-08-13 06:01:50 +02:00
58c64bd7c6 Placed docker compose flush more specific 2025-08-13 03:53:13 +02:00
e497c001d6 keycloak: robust LDAP bind and connectionUrl update via kcadm (argv + JSON); strict ldap.*; idempotent
Switch to command:argv to avoid shell quoting and argument splitting issues.

Pass -s config values as JSON arrays via to_json, fixing previous errors: Cannot parse the JSON / failed at splitting arguments.

Also reconcile config.connectionUrl from ldap.server.uri.

Source desired values strictly from ldap.* (no computed defaults) and assert their presence.

Keep operation idempotent by reading current values and updating only on change.

Minor refactor: build reusable kcadm_argv_base and expand client state extraction.

Touch: roles/web-app-keycloak/tasks/03_update-ldap-bind.yml

https://chatgpt.com/share/689bea84-7188-800f-ba51-830a0735f24c
2025-08-13 03:30:14 +02:00
231 changed files with 979 additions and 954 deletions

View File

@@ -189,7 +189,7 @@ def parse_args():
def main():
args = parse_args()
primary_domain = '{{ primary_domain }}'
primary_domain = '{{ PRIMARY_DOMAIN }}'
become_pwd = '{{ lookup("password", "/dev/null length=42 chars=ascii_letters,digits") }}'
try:

View File

@@ -191,13 +191,13 @@ def main():
validate_application_ids(args.inventory, args.id)
modes = {
"mode_reset": args.reset,
"mode_test": args.test,
"mode_update": args.update,
"mode_backup": args.backup,
"mode_cleanup": args.cleanup,
"mode_logs": args.logs,
"enable_debug": args.debug,
"MODE_RESET": args.reset,
"MODE_TEST": args.test,
"MODE_UPDATE": args.update,
"MODE_BACKUP": args.backup,
"MODE_CLEANUP": args.cleanup,
"MODE_LOGS": args.logs,
"MODE_DEBUG": args.debug,
"host_type": args.host_type
}

View File

@@ -4,7 +4,7 @@ class FilterModule(object):
def filters(self):
return {'alias_domains_map': self.alias_domains_map}
def alias_domains_map(self, apps, primary_domain):
def alias_domains_map(self, apps, PRIMARY_DOMAIN):
"""
Build a map of application IDs to their alias domains.
@@ -42,7 +42,7 @@ class FilterModule(object):
domains_cfg = cfg.get('server',{}).get('domains',{})
entry = domains_cfg.get('canonical')
if entry is None:
canonical_map[app_id] = [default_domain(app_id, primary_domain)]
canonical_map[app_id] = [default_domain(app_id, PRIMARY_DOMAIN)]
elif isinstance(entry, dict):
canonical_map[app_id] = list(entry.values())
elif isinstance(entry, list):
@@ -69,7 +69,7 @@ class FilterModule(object):
# otherwise, compute aliases
aliases = parse_entry(domains_cfg, 'aliases', app_id) or []
default = default_domain(app_id, primary_domain)
default = default_domain(app_id, PRIMARY_DOMAIN)
has_aliases = 'aliases' in domains_cfg
has_canon = 'canonical' in domains_cfg

View File

@@ -9,7 +9,7 @@ class FilterModule(object):
def filters(self):
return {'canonical_domains_map': self.canonical_domains_map}
def canonical_domains_map(self, apps, primary_domain):
def canonical_domains_map(self, apps, PRIMARY_DOMAIN):
"""
Maps applications to their canonical domains, checking for conflicts
and ensuring all domains are valid and unique across applications.
@@ -30,7 +30,7 @@ class FilterModule(object):
domains_cfg = cfg.get('server',{}).get('domains',{})
if not domains_cfg or 'canonical' not in domains_cfg:
self._add_default_domain(app_id, primary_domain, seen_domains, result)
self._add_default_domain(app_id, PRIMARY_DOMAIN, seen_domains, result)
continue
canonical_domains = domains_cfg['canonical']
@@ -38,13 +38,13 @@ class FilterModule(object):
return result
def _add_default_domain(self, app_id, primary_domain, seen_domains, result):
def _add_default_domain(self, app_id, PRIMARY_DOMAIN, seen_domains, result):
"""
Add the default domain for an application if no canonical domains are defined.
Ensures the domain is unique across applications.
"""
entity_name = get_entity_name(app_id)
default_domain = f"{entity_name}.{primary_domain}"
default_domain = f"{entity_name}.{PRIMARY_DOMAIN}"
if default_domain in seen_domains:
raise AnsibleFilterError(
f"Domain '{default_domain}' is already configured for "

View File

@@ -7,7 +7,7 @@ class FilterModule(object):
def filters(self):
return {'domain_mappings': self.domain_mappings}
def domain_mappings(self, apps, primary_domain):
def domain_mappings(self, apps, PRIMARY_DOMAIN):
"""
Build a flat list of redirect mappings for all apps:
- source: each alias domain
@@ -43,7 +43,7 @@ class FilterModule(object):
domains_cfg = cfg.get('server',{}).get('domains',{})
entry = domains_cfg.get('canonical')
if entry is None:
canonical_map[app_id] = [default_domain(app_id, primary_domain)]
canonical_map[app_id] = [default_domain(app_id, PRIMARY_DOMAIN)]
elif isinstance(entry, dict):
canonical_map[app_id] = list(entry.values())
elif isinstance(entry, list):
@@ -61,11 +61,11 @@ class FilterModule(object):
alias_map[app_id] = []
continue
if isinstance(domains_cfg, dict) and not domains_cfg:
alias_map[app_id] = [default_domain(app_id, primary_domain)]
alias_map[app_id] = [default_domain(app_id, PRIMARY_DOMAIN)]
continue
aliases = parse_entry(domains_cfg, 'aliases', app_id) or []
default = default_domain(app_id, primary_domain)
default = default_domain(app_id, PRIMARY_DOMAIN)
has_aliases = 'aliases' in domains_cfg
has_canonical = 'canonical' in domains_cfg
@@ -84,7 +84,7 @@ class FilterModule(object):
mappings = []
for app_id, sources in alias_map.items():
canon_list = canonical_map.get(app_id, [])
target = canon_list[0] if canon_list else default_domain(app_id, primary_domain)
target = canon_list[0] if canon_list else default_domain(app_id, PRIMARY_DOMAIN)
for src in sources:
if src == target:
# skip self-redirects

View File

@@ -19,7 +19,7 @@ class FilterModule(object):
Usage in Jinja:
{{ redirect_list
| add_redirect_if_group('lam',
'ldap.' ~ primary_domain,
'ldap.' ~ PRIMARY_DOMAIN,
domains | get_domain('web-app-lam'),
group_names) }}
"""

View File

@@ -1,4 +1,4 @@
INFINITO_ENVIRONMENT: "production" # Possible values: production, development
ENVIRONMENT: "production" # Possible values: production, development
# If true, sensitive credentials will be masked or hidden from all Ansible task logs
# Recommendet to set to true
@@ -19,54 +19,56 @@ HOST_THOUSAND_SEPARATOR: "."
HOST_DECIMAL_MARK: ","
# Deployment mode
deployment_mode: "single" # Use single, if you deploy on one server. Use cluster if you setup in cluster mode.
DEPLOYMENT_MODE: "single" # Use single, if you deploy on one server. Use cluster if you setup in cluster mode.
# Web
WEB_PROTOCOL: "https" # Web protocol type. Use https or http. If you run local you need to change it to http
WEB_PORT: "{{ 443 if WEB_PROTOCOL == 'https' else 80 }}" # Default port web applications will listen to
## Domain
primary_domain_tld: "localhost" # Top Level Domain of the server
primary_domain_sld: "infinito" # Second Level Domain of the server
primary_domain: "{{primary_domain_sld}}.{{primary_domain_tld}}" # Primary Domain of the server
# Domain
PRIMARY_DOMAIN: "localhost" # Primary Domain of the server
PRIMARY_DOMAIN_tld: "{{ (PRIMARY_DOMAIN == 'localhost') | ternary('localhost', PRIMARY_DOMAIN.split('.')[-1]) }}" # Top Level Domain of the server
PRIMARY_DOMAIN_SLD: "{{ (PRIMARY_DOMAIN == 'localhost') | ternary('localhost', PRIMARY_DOMAIN.split('.')[-2]) }}" # Second Level Domain of the server
# Server Tact Variables
## Ours in which the server is "awake" (100% working). Rest of the time is reserved for maintanance
hours_server_awake: "0..23"
HOURS_SERVER_AWAKE: "0..23"
## Random delay for systemd timers to avoid peak loads.
randomized_delay_sec: "5min"
RANDOMIZED_DELAY_SEC: "5min"
# Runtime Variables for Process Control
activate_all_timers: false # Activates all timers, independend if the handlers had been triggered
ACTIVATE_ALL_TIMERS: false # Activates all timers, independend if the handlers had been triggered
# This enables debugging in ansible and in the apps
# You SHOULD NOT enable this on production servers
enable_debug: false
dns_provider: cloudflare # The DNS Provider\Registrar for the domain
DNS_PROVIDER: cloudflare # The DNS Provider\Registrar for the domain
# Which ACME method to use: webroot, cloudflare, or hetzner
certbot_acme_challenge_method: "cloudflare"
certbot_credentials_dir: /etc/certbot
certbot_credentials_file: "{{ certbot_credentials_dir }}/{{ certbot_acme_challenge_method }}.ini"
certbot_dns_api_token: "" # Define in inventory file: More information here: group_vars/all/docs/CLOUDFLARE_API_TOKEN.md
certbot_dns_propagation_wait_seconds: 300 # How long should the script wait for DNS propagation before continuing
certbot_flavor: san # Possible options: san (recommended, with a dns flavor like cloudflare, or hetzner), wildcard(doesn't function with www redirect), dedicated
CERTBOT_ACME_CHALLENGE_METHOD: "cloudflare"
CERTBOT_CREDENTIALS_DIR: /etc/certbot
CERTBOT_CREDENTIALS_FILE: "{{ CERTBOT_CREDENTIALS_DIR }}/{{ CERTBOT_ACME_CHALLENGE_METHOD }}.ini"
CERTBOT_DNS_API_TOKEN: "" # Define in inventory file: More information here: group_vars/all/docs/CLOUDFLARE_API_TOKEN.md
CERTBOT_DNS_PROPAGATION_WAIT_SECONDS: 300 # How long should the script wait for DNS propagation before continuing
CERTBOT_FLAVOR: san # Possible options: san (recommended, with a dns flavor like cloudflare, or hetzner), wildcard(doesn't function with www redirect), dedicated
# Path where Certbot stores challenge webroot files
letsencrypt_webroot_path: "/var/lib/letsencrypt/"
LETSENCRYPT_WEBROOT_PATH: "/var/lib/letsencrypt/"
# Base directory containing Certbot configuration, account data, and archives
letsencrypt_base_path: "/etc/letsencrypt/"
LETSENCRYPT_BASE_PATH: "/etc/letsencrypt/"
# Symlink directory for the current active certificate and private key
letsencrypt_live_path: "{{ letsencrypt_base_path }}live/"
LETSENCRYPT_LIVE_PATH: "{{ LETSENCRYPT_BASE_PATH }}live/"
## Docker Role Specific Parameters
DOCKER_RESTART_POLICY: "unless-stopped"
DOCKER_VARS_FILE: "{{ playbook_dir }}/roles/docker-compose/vars/docker-compose.yml"
# Asyn Confitguration
ASYNC_ENABLED: "{{ not MODE_DEBUG | bool }}" # Activate async, deactivated for debugging
ASYNC_TIME: "{{ 300 if ASYNC_ENABLED | bool else omit }}" # Run for mnax 5min
ASYNC_POLL: "{{ 0 if ASYNC_ENABLED | bool else 10 }}" # Don't wait for task
# default value if not set via CLI (-e) or in playbook vars
allowed_applications: []

View File

@@ -1,8 +1,9 @@
# Mode
# The following modes can be combined with each other
mode_reset: false # Cleans up all Infinito.Nexus files. It's necessary to run to whole playbook and not particial roles when using this function.
mode_test: false # Executes test routines instead of productive routines
mode_update: true # Executes updates
mode_backup: true # Activates the backup before the update procedure
mode_cleanup: true # Cleanup unused files and configurations
MODE_RESET: false # Cleans up all Infinito.Nexus files. It's necessary to run to whole playbook and not particial roles when using this function.
MODE_TEST: false # Executes test routines instead of productive routines
MODE_UPDATE: true # Executes updates
MODE_BACKUP: true # Activates the backup before the update procedure
MODE_CLEANUP: true # Cleanup unused files and configurations
MODE_DEBUG: false # This enables debugging in ansible and in the apps, You SHOULD NOT enable this on production servers

View File

@@ -1,7 +1,7 @@
# Email Configuration
default_system_email:
domain: "{{primary_domain}}"
host: "mail.{{primary_domain}}"
domain: "{{PRIMARY_DOMAIN}}"
host: "mail.{{PRIMARY_DOMAIN}}"
port: 465
tls: true # true for TLS and false for SSL
start_tls: false

View File

@@ -23,4 +23,5 @@ nginx:
cache:
general: "/tmp/cache_nginx_general/" # Directory which nginx uses to cache general data
image: "/tmp/cache_nginx_image/" # Directory which nginx uses to cache images
user: "http" # Default nginx user in ArchLinux
user: "http" # Default nginx user in ArchLinux

View File

@@ -3,10 +3,10 @@
on_calendar_health_btrfs: "*-*-* 00:00:00" # Check once per day the btrfs for errors
on_calendar_health_journalctl: "*-*-* 00:00:00" # Check once per day the journalctl for errors
on_calendar_health_disc_space: "*-*-* 06,12,18,00:00:00" # Check four times per day if there is sufficient disc space
on_calendar_health_docker_container: "*-*-* {{ hours_server_awake }}:00:00" # Check once per hour if the docker containers are healthy
on_calendar_health_docker_volumes: "*-*-* {{ hours_server_awake }}:15:00" # Check once per hour if the docker volumes are healthy
on_calendar_health_csp_crawler: "*-*-* {{ hours_server_awake }}:30:00" # Check once per hour if all CSP are fullfilled available
on_calendar_health_nginx: "*-*-* {{ hours_server_awake }}:45:00" # Check once per hour if all webservices are available
on_calendar_health_docker_container: "*-*-* {{ HOURS_SERVER_AWAKE }}:00:00" # Check once per hour if the docker containers are healthy
on_calendar_health_docker_volumes: "*-*-* {{ HOURS_SERVER_AWAKE }}:15:00" # Check once per hour if the docker volumes are healthy
on_calendar_health_csp_crawler: "*-*-* {{ HOURS_SERVER_AWAKE }}:30:00" # Check once per hour if all CSP are fullfilled available
on_calendar_health_nginx: "*-*-* {{ HOURS_SERVER_AWAKE }}:45:00" # Check once per hour if all webservices are available
on_calendar_health_msmtp: "*-*-* 00:00:00" # Check once per day SMTP Server
## Schedule for Cleanup Tasks
@@ -19,7 +19,7 @@ on_calendar_backup_docker_to_local: "*-*-* 03:30:00"
on_calendar_backup_remote_to_local: "*-*-* 21:30:00"
## Schedule for Maintenance Tasks
on_calendar_heal_docker: "*-*-* {{ hours_server_awake }}:30:00" # Heal unhealthy docker instances once per hour
on_calendar_heal_docker: "*-*-* {{ HOURS_SERVER_AWAKE }}:30:00" # Heal unhealthy docker instances once per hour
on_calendar_renew_lets_encrypt_certificates: "*-*-* 12,00:30:00" # Renew Mailu certificates twice per day
on_calendar_deploy_certificates: "*-*-* 13,01:30:00" # Deploy letsencrypt certificates twice per day to docker containers
on_calendar_msi_keyboard_color: "*-*-* *:*:00" # Change the keyboard color every minute

View File

@@ -8,7 +8,7 @@
# @see https://en.wikipedia.org/wiki/OpenID_Connect
## Helper Variables:
_oidc_client_realm: "{{ oidc.client.realm if oidc.client is defined and oidc.client.realm is defined else primary_domain }}"
_oidc_client_realm: "{{ oidc.client.realm if oidc.client is defined and oidc.client.realm is defined else PRIMARY_DOMAIN }}"
_oidc_url: "{{
(oidc.url
if (oidc is defined and oidc.url is defined)
@@ -16,7 +16,7 @@ _oidc_url: "{{
)
}}"
_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 primary_domain }}"
_oidc_client_id: "{{ oidc.client.id if oidc.client is defined and oidc.client.id is defined else PRIMARY_DOMAIN }}"
defaults_oidc:
url: "{{ _oidc_url }}"
@@ -33,7 +33,7 @@ defaults_oidc:
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)
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:
# Attribut to identify the user
username: "preferred_username"

View File

@@ -5,12 +5,12 @@
# Helper Variables:
# Keep in mind to mapp this variables if there is ever the possibility for the user to define them in the inventory
_ldap_dn_base: "dc={{primary_domain_sld}},dc={{primary_domain_tld}}"
_ldap_dn_base: "dc={{PRIMARY_DOMAIN_SLD}},dc={{PRIMARY_DOMAIN_tld}}"
_ldap_docker_network_enabled: "{{ applications | get_app_conf('svc-db-openldap', 'network.docker') }}"
_ldap_protocol: "{{ 'ldap' if _ldap_docker_network_enabled else 'ldaps' }}"
_ldap_server_port: "{{ ports.localhost[_ldap_protocol]['svc-db-openldap'] }}"
_ldap_name: "{{ applications | get_app_conf('svc-db-openldap', 'docker.services.openldap.name') }}"
_ldap_domain: "{{ primary_domain }}" # LDAP is jsut listening to a port not to a dedicated domain, so primary domain should be sufficient
_ldap_domain: "{{ PRIMARY_DOMAIN }}" # LDAP is jsut listening to a port not to a dedicated domain, so primary domain should be sufficient
_ldap_user_id: "uid"
_ldap_filters_users_all: "(|(objectclass=inetOrgPerson))"

View File

@@ -19,7 +19,7 @@ defaults_service_provider:
web-app-bluesky: >-
{{ ('@' ~ users.contact.username ~ '.' ~ domains['web-app-bluesky'].api)
if 'web-app-bluesky' in group_names else '' }}
email: "{{ users.contact.username ~ '@' ~ primary_domain if 'web-app-mailu' in group_names else '' }}"
email: "{{ users.contact.username ~ '@' ~ PRIMARY_DOMAIN if 'web-app-mailu' in group_names else '' }}"
mastodon: "{{ '@' ~ users.contact.username ~ '@' ~ domains | get_domain('web-app-mastodon') if 'web-app-mastodon' in group_names else '' }}"
matrix: "{{ '@' ~ users.contact.username ~ ':' ~ domains['web-app-matrix'].synapse if 'web-app-matrix' in group_names else '' }}"
peertube: "{{ '@' ~ users.contact.username ~ '@' ~ domains | get_domain('web-app-peertube') if 'web-app-peertube' in group_names else '' }}"

View File

@@ -1,10 +1,10 @@
# Cloudflare API Token for Ansible (`certbot_dns_api_token`)
# Cloudflare API Token for Ansible (`CERTBOT_DNS_API_TOKEN`)
This document explains how to generate and use a Cloudflare API Token for DNS automation and certificate operations in Ansible (e.g., with Certbot).
## Purpose
The `certbot_dns_api_token` variable must contain a valid Cloudflare API Token.
The `CERTBOT_DNS_API_TOKEN` variable must contain a valid Cloudflare API Token.
This token is used for all DNS operations and ACME (SSL/TLS certificate) challenges that require access to your Cloudflare-managed domains.
**Never commit your API token to a public repository. Always keep it secure!**
@@ -58,4 +58,4 @@ Add the following permissions:
Set the token in your Ansible inventory or secrets file:
```yaml
certbot_dns_api_token: "cf_your_generated_token_here"
CERTBOT_DNS_API_TOKEN: "cf_your_generated_token_here"

View File

@@ -0,0 +1 @@
docker_compose_flush_handlers: false # Set to true in the vars/main.yml of the including role to autoflush after docker compose routine

View File

@@ -1,6 +1,6 @@
# run_once_cmp_docker_proxy: deactivated
# To load the proxy first is just implemented due to some issues with BBB
# Load the proxy first, so that openresty handlers are flushed before the main docker compose
- name: "For '{{ application_id }}': include role srv-proxy-6-6-domain"
include_role:
name: srv-proxy-6-6-domain

View File

@@ -15,7 +15,7 @@
- name: Warn if repo is not reachable
debug:
msg: "Warning: Repository is not reachable."
when: git_result.failed and enable_debug | bool
when: git_result.failed and MODE_DEBUG | bool
- name: Ensure systemd user directory exists
file:

View File

@@ -8,7 +8,7 @@ Refer to the [Docker Compose documentation](https://docs.docker.com/compose/), t
## Overview
This role creates a flexible directory layout for managing Docker Compose projects across environments. It ensures directories are initialized, optionally reset, and kept clean using internal flags like `mode_reset` or `mode_cleanup`.
This role creates a flexible directory layout for managing Docker Compose projects across environments. It ensures directories are initialized, optionally reset, and kept clean using internal flags like `MODE_RESET` or `MODE_CLEANUP`.
## Purpose
@@ -17,7 +17,7 @@ To offer a centralized, extensible system for managing containerized application
## Features
- **Dynamic Directory Structure:** Creates per-application instance folders for Compose setups.
- **Reset Logic:** Cleans previous Compose project files and data when `mode_reset` is enabled.
- **Reset Logic:** Cleans previous Compose project files and data when `MODE_RESET` is enabled.
- **Handlers for Runtime Control:** Automatically builds, sets up, or restarts containers based on handlers.
- **Template-ready Service Files:** Predefined service base and health check templates.
- **Integration Support:** Compatible with `srv-proxy-7-4-core` and other Infinito.Nexus service roles.

View File

@@ -11,14 +11,23 @@
- docker compose restart
- docker compose just up
- name: Build docker
command:
cmd: docker compose build
- name: Build docker compose
shell: |
set -euo pipefail
docker compose build || {
echo "Retrying without cache and pulling bases...";
docker compose build --no-cache --pull;
}
args:
chdir: "{{ docker_compose.directories.instance }}"
executable: /bin/bash
environment:
COMPOSE_HTTP_TIMEOUT: 600
DOCKER_CLIENT_TIMEOUT: 600
listen:
# Faster build
DOCKER_BUILDKIT: "1"
COMPOSE_DOCKER_CLI_BUILD: "1"
listen:
- docker compose build
- name: docker compose up

View File

@@ -10,7 +10,7 @@
- name: "reset (if enabled)"
include_tasks: 01_reset.yml
when: mode_reset | bool
when: MODE_RESET | bool
# This could lead to problems in docker-compose directories which are based on a git repository
# @todo Verify that this isn't the case. E.g. in accounting

View File

@@ -16,7 +16,7 @@
url: "{{ cf_api_url }}?name={{ domain | to_primary_domain }}"
method: GET
headers:
Authorization: "Bearer {{ certbot_dns_api_token }}"
Authorization: "Bearer {{ CERTBOT_DNS_API_TOKEN }}"
Content-Type: "application/json"
return_content: yes
register: cf_zone_lookup_dev
@@ -43,8 +43,8 @@
- name: activate cloudflare cache development mode
include_tasks: "cloudflare/02_enable_cf_dev_mode.yml"
when: (INFINITO_ENVIRONMENT | lower) == 'development'
when: (ENVIRONMENT | lower) == 'development'
- name: purge cloudflare domain cache
include_tasks: "cloudflare/01_cleanup.yml"
when: mode_cleanup | bool
when: MODE_CLEANUP | bool

View File

@@ -3,10 +3,11 @@
url: "https://api.cloudflare.com/client/v4/zones/{{ cf_zone_id }}/purge_cache"
method: POST
headers:
Authorization: "Bearer {{ certbot_dns_api_token }}"
Authorization: "Bearer {{ CERTBOT_DNS_API_TOKEN }}"
Content-Type: "application/json"
body:
purge_everything: true
body_format: json
return_content: yes
register: cf_purge
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"

View File

@@ -1,7 +1,7 @@
# roles/srv-proxy-6-6-domain/tasks/02_enable_cf_dev_mode.yml
---
# Enables Cloudflare Development Mode (bypasses cache for ~3 hours).
# Uses the same auth token as in 01_cleanup.yml: certbot_dns_api_token
# Uses the same auth token as in 01_cleanup.yml: CERTBOT_DNS_API_TOKEN
# Assumes `domain` and (optionally) `cf_zone_id` are available.
# Safe to run repeatedly; only changes when the mode is not already "on".
@@ -10,17 +10,18 @@
url: "https://api.cloudflare.com/client/v4/zones/{{ cf_zone_id }}/settings/development_mode"
method: GET
headers:
Authorization: "Bearer {{ certbot_dns_api_token }}"
Authorization: "Bearer {{ CERTBOT_DNS_API_TOKEN }}"
Content-Type: "application/json"
return_content: yes
register: cf_dev_mode_current
when: ASYNC_ENABLED | bool
- name: "Enable Cloudflare Development Mode"
ansible.builtin.uri:
url: "https://api.cloudflare.com/client/v4/zones/{{ cf_zone_id }}/settings/development_mode"
method: PATCH
headers:
Authorization: "Bearer {{ certbot_dns_api_token }}"
Authorization: "Bearer {{ CERTBOT_DNS_API_TOKEN }}"
Content-Type: "application/json"
body:
value: "on"
@@ -28,5 +29,8 @@
return_content: yes
register: cf_dev_mode_enable
changed_when: >
ASYNC_ENABLED | bool and
cf_dev_mode_current.json.result.value is defined and
cf_dev_mode_current.json.result.value != 'on'
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"

View File

@@ -7,7 +7,7 @@
when: run_once_srv_proxy_6_6_domain is not defined
- include_tasks: "01_cloudflare.yml"
when: dns_provider == "cloudflare"
when: DNS_PROVIDER == "cloudflare"
- include_tasks: "{{ playbook_dir }}/tasks/utils/load_handlers.yml"
vars:

View File

@@ -49,7 +49,7 @@ This script:
**Usage:**
```sh
sh srv-proxy-6-6-tls-deploy.sh primary_domain /path/to/docker/compose
sh srv-proxy-6-6-tls-deploy.sh PRIMARY_DOMAIN /path/to/docker/compose
```
---

View File

@@ -4,33 +4,33 @@ If you enabled `enable_wildcard_certificate`, follow these steps to manually req
### **1⃣ Run the Certbot Command 🖥️**
```sh
certbot certonly --manual --preferred-challenges=dns --agree-tos \
--email administrator@primary_domain -d primary_domain -d "*.primary_domain"
--email administrator@PRIMARY_DOMAIN -d PRIMARY_DOMAIN -d "*.PRIMARY_DOMAIN"
```
### **2⃣ Add DNS TXT Record for Validation 📜**
Certbot will prompt you to add a DNS TXT record:
```
Please create a TXT record under the name:
_acme-challenge.primary_domain.
_acme-challenge.PRIMARY_DOMAIN.
with the following value:
9oVizYIYVGlZ3VtWQIKRS5UghyXiqGoUNlCtIE7LiA
```
**Go to your DNS provider** and create a new **TXT record**:
- **Host:** `_acme-challenge.primary_domain`
- **Host:** `_acme-challenge.PRIMARY_DOMAIN`
- **Value:** `"9oVizYIYVGlZ3VtWQIKRS5UghyXiqGoUNlCtIE7LiA"`
- **TTL:** Set to **300 seconds (or lowest possible)**
**Verify the DNS record** before continuing:
```sh
dig TXT _acme-challenge.primary_domain @8.8.8.8
dig TXT _acme-challenge.PRIMARY_DOMAIN @8.8.8.8
```
### **3⃣ Complete the Certificate Request ✅**
Once the DNS changes have propagated, **press Enter** in the Certbot terminal.
If successful, Certbot will save the certificates under:
```
/etc/letsencrypt/live/primary_domain/
/etc/letsencrypt/live/PRIMARY_DOMAIN/
```
- **fullchain.pem** → The certificate
- **privkey.pem** → The private key

View File

@@ -12,11 +12,11 @@ docker_compose_instance_directory="$2"
docker_compose_cert_directory="$docker_compose_instance_directory/volumes/certs"
# 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
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/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
# Set correct reading rights
chmod a+r -v "$docker_compose_cert_directory/"*

View File

@@ -6,7 +6,8 @@ location {{location}}
{% include 'roles/web-app-oauth2-proxy/templates/following_directives.conf.j2'%}
{% endif %}
proxy_pass http://127.0.0.1:{{ http_port }}{{ location if not location.startswith('@') else '' }};
{% set _loc = location|trim %}
proxy_pass http://127.0.0.1:{{ http_port }}{{ (_loc|regex_replace('^(?:=|\\^~)\\s*','')) if not (_loc is match('^(@|~)')) else '' }};
# headers
proxy_set_header Host $host;
@@ -14,25 +15,27 @@ location {{location}}
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port {{ WEB_PORT }};
proxy_set_header Accept-Encoding "";
{% include 'roles/srv-proxy-7-4-core/templates/headers/content_security_policy.conf.j2' %}
# WebSocket specific header
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Activate buffering
# Needs to be enabled, so that lua can do str replaces
proxy_buffering on;
proxy_request_buffering on;
proxy_set_header Connection $connection_upgrade;
# timeouts
proxy_connect_timeout 1s;
proxy_send_timeout 900s;
proxy_read_timeout 900s;
send_timeout 900s;
proxy_connect_timeout 5s;
proxy_send_timeout 900s;
proxy_read_timeout 900s;
send_timeout 900s;
{% set proxy_lua_enabled = proxy_lua_enabled | default(true) | bool %}
# Buffering needs to be activ, so that lua can do str replaces
proxy_buffering {{ 'on' if proxy_lua_enabled else 'off' }};
proxy_request_buffering {{ 'on' if proxy_lua_enabled else 'off' }};
{% if proxy_lua_enabled %}
proxy_set_header Accept-Encoding "";
{% include 'roles/srv-web-7-7-inj-compose/templates/location.lua.j2'%}
{% endif %}
}

View File

@@ -4,7 +4,7 @@ location ~* \.(jpg|jpeg|png|gif|webp|ico|svg)$ {
add_header Cache-Control "public, max-age=2592000, immutable";
# Cache on reverse proxy side
proxy_pass http://127.0.0.1:{{http_port}};
proxy_pass http://127.0.0.1:{{ http_port }};
proxy_cache imgcache;
proxy_cache_valid 200 302 60m;
proxy_cache_valid 404 1m;

View File

@@ -24,7 +24,7 @@ The Nginx HTTPS Certificate Retrieval role ensures that your Nginx-served domain
- **ACME Challenge Selection:** Supports DNS plugins or webroot method automatically.
- **Wildcard Certificate Management:** Issues wildcard certificates when configured, saving effort for subdomain-heavy deployments.
- **Safe Cleanup:** Ensures that no unused certificates are left behind.
- **Flexible Control:** Supports `mode_test` for staging environment testing and `mode_cleanup` for cert cleanup operations.
- **Flexible Control:** Supports `MODE_TEST` for staging environment testing and `MODE_CLEANUP` for cert cleanup operations.
## 🔗 Learn More

View File

@@ -1,7 +1,7 @@
- name: "Check if certificate already exists for {{ domain }}"
cert_check_exists:
domain: "{{ domain }}"
cert_base_path: "{{ letsencrypt_live_path }}"
cert_base_path: "{{ LETSENCRYPT_LIVE_PATH }}"
register: cert_check
- name: "receive certificate for {{ domain }}"
@@ -10,21 +10,21 @@
--agree-tos
--email {{ users.administrator.email }}
--non-interactive
{% if certbot_acme_challenge_method != "webroot" %}
--dns-{{ certbot_acme_challenge_method }}
--dns-{{ certbot_acme_challenge_method }}-credentials {{ certbot_credentials_file }}
--dns-{{ certbot_acme_challenge_method }}-propagation-seconds {{ certbot_dns_propagation_wait_seconds }}
{% if CERTBOT_ACME_CHALLENGE_METHOD != "webroot" %}
--dns-{{ CERTBOT_ACME_CHALLENGE_METHOD }}
--dns-{{ CERTBOT_ACME_CHALLENGE_METHOD }}-credentials {{ CERTBOT_CREDENTIALS_FILE }}
--dns-{{ CERTBOT_ACME_CHALLENGE_METHOD }}-propagation-seconds {{ CERTBOT_DNS_PROPAGATION_WAIT_SECONDS }}
{% else %}
--webroot
-w {{ letsencrypt_webroot_path }}
-w {{ LETSENCRYPT_WEBROOT_PATH }}
{% endif %}
{% if wildcard_domain is defined and ( wildcard_domain | bool ) %}
-d {{ primary_domain }}
-d *.{{ primary_domain }}
-d {{ PRIMARY_DOMAIN }}
-d *.{{ PRIMARY_DOMAIN }}
{% else %}
-d {{ domain }}
{% endif %}
{{ '--test-cert' if mode_test | bool else '' }}
{{ '--test-cert' if MODE_TEST | bool else '' }}
register: certbot_result
changed_when: "'Certificate not yet due for renewal' not in certbot_result.stdout"
when: not cert_check.exists

View File

@@ -10,15 +10,15 @@
certbundle
--domains "{{ current_play_domains_all | join(',') }}"
--certbot-email "{{ users.administrator.email }}"
--certbot-acme-challenge-method "{{ certbot_acme_challenge_method }}"
--certbot-acme-challenge-method "{{ CERTBOT_ACME_CHALLENGE_METHOD }}"
--chunk-size 100
{% if certbot_acme_challenge_method != 'webroot' %}
--certbot-credentials-file "{{ certbot_credentials_file }}"
--certbot-dns-propagation-seconds "{{ certbot_dns_propagation_wait_seconds }}"
{% if CERTBOT_ACME_CHALLENGE_METHOD != 'webroot' %}
--certbot-credentials-file "{{ CERTBOT_CREDENTIALS_FILE }}"
--certbot-dns-propagation-seconds "{{ CERTBOT_DNS_PROPAGATION_WAIT_SECONDS }}"
{% else %}
--letsencrypt-webroot-path "{{ letsencrypt_webroot_path }}"
--letsencrypt-webroot-path "{{ LETSENCRYPT_WEBROOT_PATH }}"
{% endif %}
{{ '--mode-test' if mode_test | bool else '' }}
{{ '--mode-test' if MODE_TEST | bool else '' }}
register: certbundle_result
changed_when: "'Certificate not yet due for renewal' not in certbundle_result.stdout"
failed_when: >

View File

@@ -3,7 +3,7 @@
vars:
wildcard_domain: true
when:
- domain.split('.') | length == (primary_domain.split('.') | length + 1) and domain.endswith(primary_domain)
- domain.split('.') | length == (PRIMARY_DOMAIN.split('.') | length + 1) and domain.endswith(PRIMARY_DOMAIN)
- run_once_receive_certificate is not defined
- name: "Load dedicated certificate for domain"
@@ -11,7 +11,7 @@
vars:
wildcard_domain: false
when:
- not (domain.split('.') | length == (primary_domain.split('.') | length + 1) and domain.endswith(primary_domain))
- not (domain.split('.') | length == (PRIMARY_DOMAIN.split('.') | length + 1) and domain.endswith(PRIMARY_DOMAIN))
- name: run the receive_certificate tasks once
set_fact:

View File

@@ -6,20 +6,20 @@
- include_tasks: utils/run_once.yml
when: run_once_srv_web_6_6_tls_core is not defined
- name: "Include flavor '{{ certbot_flavor }}' for '{{ domain }}'"
include_tasks: "{{ role_path }}/tasks/flavors/{{ certbot_flavor }}.yml"
- name: "Include flavor '{{ CERTBOT_FLAVOR }}' for '{{ domain }}'"
include_tasks: "{{ role_path }}/tasks/flavors/{{ CERTBOT_FLAVOR }}.yml"
#- name: "Cleanup dedicated cert for {{ domain }}"
# command: >-
# certbot delete --cert-name {{ domain }} --non-interactive
# when:
# - mode_cleanup | bool
# - MODE_CLEANUP | bool
# # Cleanup mode is enabled
# - certbot_flavor != 'dedicated'
# - CERTBOT_FLAVOR != 'dedicated'
# # Wildcard certificate is enabled
# - domain.split('.') | length == (primary_domain.split('.') | length + 1) and domain.endswith(primary_domain)
# - domain.split('.') | length == (PRIMARY_DOMAIN.split('.') | length + 1) and domain.endswith(PRIMARY_DOMAIN)
# # AND: The domain is a direct first-level subdomain of the primary domain
# - domain != primary_domain
# - domain != PRIMARY_DOMAIN
# # The domain is not the primary domain
# register: certbot_result
# failed_when: certbot_result.rc != 0 and ("No certificate found with name" not in certbot_result.stderr)
@@ -28,8 +28,8 @@
- name: "Find SSL cert folder for '{{ domain }}'"
cert_folder_find:
domain: "{{ domain }}"
cert_base_path: "{{ letsencrypt_live_path }}"
debug: "{{ enable_debug | default(false) }}"
cert_base_path: "{{ LETSENCRYPT_LIVE_PATH }}"
debug: "{{ MODE_DEBUG | default(false) }}"
register: cert_folder_result
delegate_to: "{{ inventory_hostname }}"
changed_when: false

View File

@@ -14,11 +14,11 @@ This Ansible role installs and configures **Nginx** as a core HTTP/stream server
* **Configurable reset and cleanup** modes to purge and recreate directories.
* **Custom `nginx.conf`** template with sensible defaults for performance and security.
* **Stream proxy support**: includes `stream` block for TCP/UDP proxies.
* **Cache directory management**: cleanup and recreation based on `mode_cleanup`.
* **Cache directory management**: cleanup and recreation based on `MODE_CLEANUP`.
## Debugging Tips
* **General logs**: `journalctl -f -u nginx`
* **Filter by host**: `journalctl -u nginx -f | grep "{{ inventory_hostname }}"`
* **Enable detailed format**: set `enable_debug: true` and reload Nginx.
* **Enable detailed format**: set `MODE_DEBUG: true` and reload Nginx.

View File

@@ -20,7 +20,7 @@
- name: "reset (if enabled)"
include_tasks: 02_reset.yml
when: mode_reset | bool
when: MODE_RESET | bool
- name: Ensure nginx configuration directories are present
file:
@@ -47,9 +47,12 @@
mode: '0755'
loop: >
{{ nginx.directories.data.values() | list }}
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
- name: "Include tasks to create cache directories"
include_tasks: 03_cache_directories.yml
when: run_once_nginx_reverse_proxy is not defined
- name: create nginx config file
template:

View File

@@ -1,4 +1,4 @@
- name: "Delete {{nginx.directories.configuration}} directory, when mode_reset"
- name: "Delete {{nginx.directories.configuration}} directory, when MODE_RESET"
file:
path: "{{ nginx.directories.configuration }}"
state: absent

View File

@@ -4,8 +4,7 @@
path: "{{ item.value }}"
state: absent
when:
- mode_cleanup | bool
- run_once_nginx_reverse_proxy is not defined
- MODE_CLEANUP | bool
loop: "{{ nginx.directories.cache | dict2items }}"
loop_control:
label: "{{ item.key }}"
@@ -18,13 +17,12 @@
owner: "{{ nginx.user }}"
group: "{{ nginx.user }}"
mode: '0700'
when: run_once_nginx_reverse_proxy is not defined
loop: "{{ nginx.directories.cache | dict2items }}"
loop_control:
label: "{{ item.key }}"
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
- name: run the nginx_reverse_proxy tasks once
set_fact:
run_once_nginx_reverse_proxy: true
when: run_once_nginx_reverse_proxy is not defined
run_once_nginx_reverse_proxy: true

View File

@@ -24,7 +24,7 @@ http
# --------------------------------------------------------------------------------
{# logging and debugging #}
{% if enable_debug | bool %}
{% if MODE_DEBUG | bool %}
{# individual log format for better debugging #}
log_format debug '$host - $remote_addr [$time_local] '
'"$request" $status $body_bytes_sent '

View File

@@ -45,8 +45,8 @@ All tasks are idempotent—once your certificates are in place and your configur
- A working `srv-web-7-4-core` setup.
- DNS managed via Cloudflare (for CAA record tasks) or equivalent ACME DNS flow.
- Variables:
- `letsencrypt_webroot_path`
- `letsencrypt_live_path`
- `LETSENCRYPT_WEBROOT_PATH`
- `LETSENCRYPT_LIVE_PATH`
- `on_calendar_renew_lets_encrypt_certificates`
---

View File

@@ -6,34 +6,34 @@
- name: install certbot DNS plugin
community.general.pacman:
name: "certbot-dns-{{ certbot_acme_challenge_method }}"
name: "certbot-dns-{{ CERTBOT_ACME_CHALLENGE_METHOD }}"
state: present
when:
- run_once_srv_web_7_7_certbot is not defined
- certbot_acme_challenge_method != 'webroot'
- CERTBOT_ACME_CHALLENGE_METHOD != 'webroot'
- name: Ensure /etc/certbot directory exists
file:
path: "{{ certbot_credentials_dir }}"
path: "{{ CERTBOT_CREDENTIALS_DIR }}"
state: directory
owner: root
group: root
mode: '0755'
when:
- run_once_srv_web_7_7_certbot is not defined
- certbot_acme_challenge_method != 'webroot'
- CERTBOT_ACME_CHALLENGE_METHOD != 'webroot'
- name: Install plugin credentials file
copy:
dest: "{{ certbot_credentials_file }}"
dest: "{{ CERTBOT_CREDENTIALS_FILE }}"
content: |
dns_{{ certbot_acme_challenge_method }}_api_token = {{ certbot_dns_api_token }}
dns_{{ CERTBOT_ACME_CHALLENGE_METHOD }}_api_token = {{ CERTBOT_DNS_API_TOKEN }}
owner: root
group: root
mode: '0600'
when:
- run_once_srv_web_7_7_certbot is not defined
- certbot_acme_challenge_method != 'webroot'
- CERTBOT_ACME_CHALLENGE_METHOD != 'webroot'
- name: run the certbot role once
set_fact:

View File

@@ -10,7 +10,7 @@ Looping over a provided list of domains (`cloudflare_domains`), this role:
- Determines the zone name by extracting the last two labels of each domain.
- Ensures an A-record for each domain points to the specified IP (`cloudflare_target_ip`).
- Honors the `proxied` flag to switch between DNS-only and Cloudflare-proxied modes.
- Provides an optional debug task (`enable_debug`) to output the domain list before changes.
- Provides an optional debug task (`MODE_DEBUG`) to output the domain list before changes.
Ideal for environments where bulk or dynamic DNS updates are needed, this role abstracts away the complexity of Cloudflares zone and record API.
@@ -23,7 +23,7 @@ Cloudflare DNS Records delivers an idempotent, scalable solution for managing A-
- **Automatic Zone Detection:** Parses each domain to derive its zone (`example.com`) without manual intervention.
- **Bulk Record Management:** Creates or updates A-records for all entries in `cloudflare_domains`.
- **Proxy Toggle:** Configure `proxied: true` or `false` per record to switch between DNS-only and proxied modes.
- **Debug Support:** Enable `enable_debug` to print the domain list for validation before execution.
- **Debug Support:** Enable `MODE_DEBUG` to print the domain list for validation before execution.
- **Flexible Authentication:** Supports both API token (`api_token`) and Global API key + email.
- **Low-TTL Option:** Use `ttl: 1` for rapid DNS propagation during dynamic updates.

View File

@@ -2,7 +2,7 @@
- name: Create or update Cloudflare A-record for {{ item }}
community.general.cloudflare_dns:
api_token: "{{ certbot_dns_api_token }}"
api_token: "{{ CERTBOT_DNS_API_TOKEN }}"
zone: "{{ item.split('.')[-2:] | join('.') }}"
state: present
type: A

View File

@@ -10,11 +10,17 @@
set_fact:
inj_enabled:
javascript: "{{ applications | get_app_conf(application_id, 'features.javascript', False) }}"
logout: "{{ (applications | get_app_conf(application_id, 'features.logout', False) or domain == primary_domain) }}"
logout: "{{ (applications | get_app_conf(application_id, 'features.logout', False) or domain == PRIMARY_DOMAIN) }}"
css: "{{ applications | get_app_conf(application_id, 'features.css', False) }}"
matomo: "{{ applications | get_app_conf(application_id, 'features.matomo', False) }}"
port_ui: "{{ applications | get_app_conf(application_id, 'features.port-ui-desktop', False) }}"
- name: "Activate Portfolio iFrame notifier for {{ domain }}"
include_role:
name: srv-web-7-7-inj-port-ui-desktop
public: true # Expose variables so that they can be used in template
when: inj_enabled.port_ui
- name: "Load CDN for {{domain}}"
include_role:
name: web-svc-cdn
@@ -39,7 +45,7 @@
set_fact:
inj_enabled:
javascript: "{{ applications | get_app_conf(application_id, 'features.javascript', False) }}"
logout: "{{ (applications | get_app_conf(application_id, 'features.logout', False) or domain == primary_domain) }}"
logout: "{{ (applications | get_app_conf(application_id, 'features.logout', False) or domain == PRIMARY_DOMAIN) }}"
css: "{{ applications | get_app_conf(application_id, 'features.css', False) }}"
matomo: "{{ applications | get_app_conf(application_id, 'features.matomo', False) }}"
port_ui: "{{ applications | get_app_conf(application_id, 'features.port-ui-desktop', False) }}"
@@ -56,11 +62,6 @@
name: srv-web-7-7-inj-matomo
when: inj_enabled.matomo
- name: "Activate Portfolio iFrame notifier for {{ domain }}"
include_role:
name: srv-web-7-7-inj-port-ui-desktop
when: inj_enabled.port_ui
- name: "Activate Javascript for {{ domain }}"
include_role:
name: srv-web-7-7-inj-javascript

View File

@@ -58,7 +58,7 @@ body_filter_by_lua_block {
-- build a list of body-injection snippets
local body_snippets = {}
{% for body_feature in ['matomo', 'logout' ] %}
{% for body_feature in ['matomo', 'logout', 'port-ui-desktop'] %}
{% if applications | get_app_conf(application_id, 'features.' ~ body_feature, false) | bool %}
body_snippets[#body_snippets + 1] = [=[
{%- include "roles/srv-web-7-7-inj-" ~ body_feature ~ "/templates/body_sub.j2" -%}

View File

@@ -1 +1 @@
<link rel="stylesheet" type="text/css" href="/global.css?version={{global_css_version}}">
<link rel="stylesheet" type="text/css" href="/global.css?version={{ global_css_version }}">

View File

@@ -16,7 +16,7 @@ This Ansible role injects a custom JavaScript snippet into all HTML responses se
Activates only when you enable the `javascript` feature for a given application, keeping your server blocks clean and performant.
- **Debug Mode**
Supports an `enable_debug` flag that appends optional `console.log` statements for easier troubleshooting in staging or development.
Supports an `MODE_DEBUG` flag that appends optional `console.log` statements for easier troubleshooting in staging or development.
## Author

View File

@@ -2,6 +2,6 @@ document.addEventListener('DOMContentLoaded', function() {
initLogoutPatch(
'{{ oidc.client.logout_url }}',
'{{ WEB_PROTOCOL }}',
'{{ primary_domain }}'
'{{ PRIMARY_DOMAIN }}'
);
});

View File

@@ -12,7 +12,7 @@
domain: "{{ domain }}"
base_domain: "{{ base_domain }}"
matomo_verification_url: "{{ matomo_verification_url }}"
when: enable_debug | bool
when: MODE_DEBUG | bool
- name: "Check if site {{ domain }} is allready registered at Matomo"
uri:

View File

@@ -14,6 +14,6 @@ _paq.push(["enableLinkTracking"]);
g.async=true; g.src=u+"matomo.js"; s.parentNode.insertBefore(g,s);
})();
{% if enable_debug | bool %}
{% if MODE_DEBUG | bool %}
console.log("Matomo is loaded.");
{% endif %}

View File

@@ -0,0 +1,16 @@
- name: Deploy iframe-handler.js
template:
src: iframe-handler.js.j2
dest: "{{ inj_port_ui_js_destination }}"
owner: "{{ nginx.user }}"
group: "{{ nginx.user }}"
mode: '0644'
- name: Get stat for iframe-handler.js
stat:
path: "{{ inj_port_ui_js_destination }}"
register: inj_port_ui_js_stat
- name: Set inj_port_ui_js_version
set_fact:
inj_port_ui_js_version: "{{ inj_port_ui_js_stat.stat.mtime }}"

View File

@@ -3,18 +3,20 @@
include_role:
name: srv-web-7-4-core
when: run_once_srv_web_7_4_core is not defined
- include_tasks: 01_deploy.yml
- include_tasks: utils/run_once.yml
when: run_once_srv_web_7_7_inj_port_ui_desktop is not defined
- name: "Load iFrame handler JS template for '{{ application_id }}'"
# --- Build tiny inline initializer (CSP-hashed) ---
- name: "Load iFrame init code for '{{ application_id }}'"
set_fact:
iframe_code: "{{ lookup('template','iframe-handler.js.j2') }}"
iframe_init_code: "{{ lookup('template','iframe-init_one_liner.js.j2') }}"
- name: "Collapse iFrame code into one-liner for '{{ application_id }}'"
- name: "Collapse iFrame init code into one-liner for '{{ application_id }}'"
set_fact:
iframe_code_one_liner: "{{ iframe_code | to_one_liner }}"
iframe_init_code_one_liner: "{{ iframe_init_code | to_one_liner }}"
- name: "Append iFrame CSP hash for '{{ application_id }}'"
- name: "Append iFrame init CSP hash for '{{ application_id }}'"
set_fact:
applications: "{{ applications | append_csp_hash(application_id, iframe_code_one_liner) }}"
applications: "{{ applications | append_csp_hash(application_id, iframe_init_code_one_liner) }}"
changed_when: false

View File

@@ -0,0 +1 @@
<script>{{ iframe_init_code_one_liner }}</script>

View File

@@ -1 +1 @@
<script>{{ iframe_code_one_liner }}</script>
<script src="{{ domains | get_url('web-svc-cdn', WEB_PROTOCOL) }}/{{ inj_port_ui_file_name }}?{{ inj_port_ui_js_version }}"></script>

View File

@@ -1,48 +1,57 @@
(function() {
var primary = "{{ primary_domain }}";
var allowedOrigin = "https://{{ domains | get_domain('web-app-port-ui') }}";
function notifyParent() {
if (window.self !== window.top) {
try {
window.parent.postMessage({
type: "iframeLocationChange",
href: window.location.href
}, allowedOrigin);
} catch (e) {}
(function (global) {
/**
* Initializes the iframe sync & external link forcing logic.
* @param {string} primary_domain
* @param {string} current_domain
* @param {string} allowedOrigin - Parent origin for postMessage
*/
function initIframeHandler(primary_domain, current_domain, allowedOrigin) {
function notifyParent() {
if (window.self !== window.top) {
try {
window.parent.postMessage(
{ type: "iframeLocationChange", href: window.location.href },
allowedOrigin
);
} catch (e) {}
}
}
}
function forceExternalLinks() {
Array.prototype.forEach.call(document.querySelectorAll("a[href]"), function(a) {
try {
var url = new URL(a.href, location);
if (!url.hostname.endsWith(primary)) {
a.target = "_blank";
a.rel = "noopener";
}
} catch (e) {}
function forceExternalLinks() {
Array.prototype.forEach.call(document.querySelectorAll("a[href]"), function (a) {
try {
var url = new URL(a.href, location);
// open new tab if link goes outside our primary OR current domain
if (!(url.hostname.endsWith(primary_domain) || url.hostname.endsWith(current_domain))) {
a.target = "_blank";
a.rel = "noopener";
}
} catch (e) {}
});
}
window.addEventListener("load", function () {
notifyParent();
forceExternalLinks();
});
window.addEventListener("popstate", function () {
notifyParent();
forceExternalLinks();
});
// SPA support
var _pushState = history.pushState;
history.pushState = function () {
_pushState.apply(history, arguments);
notifyParent();
forceExternalLinks();
};
{% if MODE_DEBUG | bool %}
try { console.log("[iframe-sync] initIframeHandler installed."); } catch (e) {}
{% endif %}
}
window.addEventListener("load", function() {
notifyParent();
forceExternalLinks();
});
window.addEventListener("popstate", function() {
notifyParent();
forceExternalLinks();
});
// SPA support
var _pushState = history.pushState;
history.pushState = function() {
_pushState.apply(history, arguments);
notifyParent();
forceExternalLinks();
};
})();
{% if enable_debug | bool %}
console.log("[iframe-sync] Sender for iframe messages is active.");
{% endif %}
// expose for inline bootstrap
global.initIframeHandler = initIframeHandler;
})(window);

View File

@@ -0,0 +1,10 @@
document.addEventListener('DOMContentLoaded', function () {
initIframeHandler(
'{{ PRIMARY_DOMAIN }}',
'{{ domain }}',
'{{ domains | get_url("web-app-port-ui", WEB_PROTOCOL) }}'
);
});
{% if MODE_DEBUG | bool %}
try { console.log("[iframe-sync] Sender for iframe messages is active."); } catch(e) {}
{% endif %}

View File

@@ -0,0 +1,2 @@
inj_port_ui_file_name: "iframe-handler.js"
inj_port_ui_js_destination: "{{ [ nginx.directories.data.cdn, inj_port_ui_file_name ] | path_join }}"

View File

@@ -11,4 +11,4 @@
- name: "Set CAA records for all base domains"
include_tasks: 01_set-caa-records.yml
when: dns_provider == 'cloudflare'
when: DNS_PROVIDER == 'cloudflare'

View File

@@ -1,14 +1,14 @@
---
- name: "Validate certbot_dns_api_token"
- name: "Validate CERTBOT_DNS_API_TOKEN"
fail:
msg: >
The variable "certbot_dns_api_token" must be defined and cannot be empty!
when: (certbot_dns_api_token|default('')|trim) == ''
The variable "CERTBOT_DNS_API_TOKEN" must be defined and cannot be empty!
when: (CERTBOT_DNS_API_TOKEN|default('')|trim) == ''
- name: "Ensure all CAA records are present"
community.general.cloudflare_dns:
api_token: "{{ certbot_dns_api_token }}"
api_token: "{{ CERTBOT_DNS_API_TOKEN }}"
zone: "{{ item.0 }}"
record: "@"
type: CAA
@@ -19,4 +19,6 @@
state: present
loop: "{{ base_sld_domains | product(caa_entries) | list }}"
loop_control:
label: "{{ item.0 }} → {{ item.1.tag }}"
label: "{{ item.0 }} → {{ item.1.tag }}"
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"

View File

@@ -9,7 +9,7 @@ server
#letsencrypt
location ^~ /.well-known/acme-challenge/ {
allow all;
root {{ letsencrypt_webroot_path }};
root {{ LETSENCRYPT_WEBROOT_PATH }};
default_type "text/plain";
try_files $uri =404;
}

View File

@@ -1,3 +1,3 @@
ssl_certificate {{ [ letsencrypt_live_path, ssl_cert_folder] | path_join }}/fullchain.pem;
ssl_certificate_key {{ [ letsencrypt_live_path, ssl_cert_folder] | path_join }}/privkey.pem;
ssl_trusted_certificate {{ [ letsencrypt_live_path, ssl_cert_folder] | path_join }}/chain.pem;
ssl_certificate {{ [ LETSENCRYPT_LIVE_PATH, ssl_cert_folder] | path_join }}/fullchain.pem;
ssl_certificate_key {{ [ LETSENCRYPT_LIVE_PATH, ssl_cert_folder] | path_join }}/privkey.pem;
ssl_trusted_certificate {{ [ LETSENCRYPT_LIVE_PATH, ssl_cert_folder] | path_join }}/chain.pem;

View File

@@ -18,8 +18,8 @@
uidNumber: "{{ item.value.uid | int }}"
gidNumber: "{{ item.value.gid | int }}"
state: present # ↳ creates but never updates
async: 60
poll: 0
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
loop: "{{ users | dict2items }}"
loop_control:
label: "{{ item.key }}"
@@ -37,8 +37,8 @@
objectClass: "{{ ldap.user.objects.structural }}"
mail: "{{ item.value.email }}"
state: exact
async: 60
poll: 0
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
loop: "{{ users | dict2items }}"
loop_control:
label: "{{ item.key }}"

View File

@@ -21,8 +21,8 @@
attributes:
objectClass: "{{ missing_auxiliary }}"
state: present
async: 60
poll: 0
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
loop: "{{ ldap_users_with_classes.results }}"
loop_control:
label: "{{ item.dn }}"

View File

@@ -2,5 +2,8 @@
- name: "For '{{ application_id }}': Load docker-compose"
include_role:
name: docker-compose
vars:
docker_compose_flush_handlers: true
docker_pull_git_repository: false # Deactivated here to deactivate inhirement
- include_tasks: utils/run_once.yml
when: run_once_svc_prx_openresty is not defined

View File

@@ -10,6 +10,6 @@
- {{ nginx.directories.configuration }}:{{ nginx.directories.configuration }}:ro
- {{ nginx.directories.data.www }}:{{ nginx.directories.data.www }}:ro
- {{ nginx.directories.data.well_known }}:{{ nginx.directories.data.well_known }}:ro
- {{ letsencrypt_webroot_path }}:{{ letsencrypt_webroot_path }}:ro
- {{ letsencrypt_base_path }}:{{ letsencrypt_base_path }}:ro
- {{ LETSENCRYPT_WEBROOT_PATH }}:{{ LETSENCRYPT_WEBROOT_PATH }}:ro
- {{ LETSENCRYPT_BASE_PATH }}:{{ LETSENCRYPT_BASE_PATH }}:ro
command: ["openresty", "-g", "daemon off;"]

View File

@@ -8,7 +8,3 @@ database_type: ""
openresty_image: "openresty/openresty"
openresty_version: "alpine"
openresty_container: "{{ applications | get_app_conf(application_id, 'docker.services.openresty.name', True) }}"
# Docker
docker_compose_flush_handlers: true
docker_pull_git_repository: false # Deactivated here to don't inhire this

View File

@@ -12,7 +12,7 @@
- name: "reset (if enabled)"
include_tasks: 03_reset.yml
when: mode_reset | bool
when: MODE_RESET | bool
- name: configure sys-bkp-docker-2-loc-everything.infinito.service
template:

View File

@@ -11,7 +11,7 @@
database_host: "{{ database_host | default('undefined') }}"
database_username: "{{ database_username | default('undefined') }}"
database_password: "{{ database_password | default('undefined') }}"
when: enable_debug | bool
when: MODE_DEBUG | bool
- name: "fail if not all required database variables are defined"
fail:

View File

@@ -14,7 +14,7 @@
vars:
domain: "{{ item }}"
when:
- mode_cleanup | bool
- MODE_CLEANUP | bool
## The revoking just works for the base domain
#- name: "Revoke Certbot certificate for {{ item }}"
@@ -25,7 +25,7 @@
# loop_control:
# label: "{{ item }}"
# when:
# - mode_cleanup | bool
# - MODE_CLEANUP | bool
# - run_once_sys_cln_domains is not defined
# register: certbot_revoke_result
# failed_when: >
@@ -43,7 +43,7 @@
# loop_control:
# label: "{{ item }}"
# when:
# - mode_cleanup | bool
# - MODE_CLEANUP | bool
# - run_once_sys_cln_domains is not defined
# register: certbot_delete_result
# failed_when: >

View File

@@ -4,7 +4,7 @@ This Ansible role handles resetting and cleaning up “Infinito.Nexus” systemd
## Description
When enabled via the `mode_reset` flag, this role will:
When enabled via the `MODE_RESET` flag, this role will:
1. Run its reset tasks exactly once per play (`run_once_sys_rst_daemon` guard).
2. Find all `/etc/systemd/system/*.infinito.service` units.

View File

@@ -1,6 +1,6 @@
- name: "reset (if enabled)"
include_tasks: reset.yml
when: mode_reset | bool and run_once_sys_rst_daemon is not defined
when: MODE_RESET | bool and run_once_sys_rst_daemon is not defined
- name: run {{ role_name }} once
set_fact:

View File

@@ -19,7 +19,7 @@ This Ansible role configures the OpenSSH daemon (`sshd`) by deploying a template
- **Security Defaults**
- Disables password (`PasswordAuthentication no`) and root login (`PermitRootLogin no`)
- Enforces public-key authentication (`PubkeyAuthentication yes`)
- Conditionally sets `LogLevel` to `DEBUG3` when `enable_debug` is true
- Conditionally sets `LogLevel` to `DEBUG3` when `MODE_DEBUG` is true
- **Systemd Integration**
Handles daemon reload and service restart seamlessly on configuration changes.

View File

@@ -25,7 +25,7 @@
# Logging
#SyslogFacility AUTH
LogLevel {% if enable_debug | bool %}DEBUG3{% else %}INFO{% endif %}
LogLevel {% if MODE_DEBUG | bool %}DEBUG3{% else %}INFO{% endif %}
# Authentication:

View File

@@ -9,7 +9,7 @@ This role configures a systemd timer to periodically start a corresponding servi
Optimized for automated task scheduling in a [systemd](https://en.wikipedia.org/wiki/Systemd) environment, this role:
- Generates a timer unit file for a given service (using the `service_name` variable).
- Reloads and restarts the timer using systemd to ensure that changes take effect.
- Supports dynamic configuration of scheduling parameters via variables like `on_calendar` and `randomized_delay_sec`.
- Supports dynamic configuration of scheduling parameters via variables like `on_calendar` and `RANDOMIZED_DELAY_SEC`.
## Purpose

View File

@@ -1,7 +1,7 @@
- name: "reset (if enabled)"
include_tasks: 01_reset.yml
when: mode_reset | bool and run_once_sys_timer is not defined
when: MODE_RESET | bool and run_once_sys_timer is not defined
- name: run {{ role_name }} once
set_fact:
@@ -20,5 +20,5 @@
name: "{{ sys_timer_file }}"
state: restarted
enabled: yes
when: dummy_timer.changed or activate_all_timers | bool
when: dummy_timer.changed or ACTIVATE_ALL_TIMERS | bool

View File

@@ -3,7 +3,7 @@ Description=Timer to start {{service_name}}.infinito.service
[Timer]
OnCalendar={{on_calendar}}
RandomizedDelaySec={{randomized_delay_sec}}
RandomizedDelaySec={{RANDOMIZED_DELAY_SEC}}
Persistent={{ persistent | default('false') }}
[Install]

View File

@@ -8,7 +8,7 @@
name: sys-bkp-docker-2-loc-everything.infinito.service
state: started
when:
- mode_backup | bool
- MODE_BACKUP | bool
- name: create {{update_docker_script}}
template:

View File

@@ -149,7 +149,7 @@ def update_mastodon():
Runs the database migration for Mastodon to ensure all required tables are up to date.
"""
print("Starting Mastodon database migration.")
run_command("docker compose exec -T web bash -c 'RAILS_ENV={{ INFINITO_ENVIRONMENT | lower }} bin/rails db:migrate'")
run_command("docker compose exec -T web bash -c 'RAILS_ENV={{ ENVIRONMENT | lower }} bin/rails db:migrate'")
print("Mastodon database migration complete.")
def upgrade_listmonk():

View File

@@ -2,7 +2,7 @@ users:
administrator:
description: "System Administrator"
username: "administrator"
email: "administrator@{{ primary_domain }}"
email: "administrator@{{ PRIMARY_DOMAIN }}"
password: "{{ ansible_become_password }}"
uid: 1001
gid: 1001

View File

@@ -2,10 +2,10 @@
users:
sld:
description: "Auto Generated Account to reserve the SLD"
username: "{{ primary_domain.split('.')[0] }}"
username: "{{ PRIMARY_DOMAIN.split('.')[0] }}"
tld:
description: "Auto Generated Account to reserve the TLD"
username: "{{ primary_domain.split('.')[1] }}"
username: "{{ PRIMARY_DOMAIN.split('.')[1] }}"
root:
username: root
uid: 0

View File

@@ -1,5 +1,5 @@
company:
name: "Akaunting on {{ primary_domain | upper }}" # @todo load automatic based on service_provider infos, this will fail
name: "Akaunting on {{ PRIMARY_DOMAIN | upper }}" # @todo load automatic based on service_provider infos, this will fail
email: "{{ users.administrator.email }}" # @todo load automatic based on service_provider infos, this will fail
setup_admin_email: "{{ users.administrator.email }}"
features:
@@ -11,7 +11,7 @@ features:
server:
domains:
canonical:
- "accounting.{{ primary_domain }}"
- "accounting.{{ PRIMARY_DOMAIN }}"
docker:
services:
database:

View File

@@ -16,4 +16,4 @@ docker:
server:
domains:
canonical:
- "tickets.{{ primary_domain }}"
- "tickets.{{ PRIMARY_DOMAIN }}"

View File

@@ -1,8 +1,4 @@
---
- name: "For '{{ application_id }}': load docker and db"
include_role:
name: cmp-db-docker
- name: "For '{{ application_id }}': include role to receive certs & do modification routines"
include_role:
name: srv-web-7-6-composer
@@ -12,6 +8,12 @@
loop:
- "{{ domains | get_domain('web-app-mailu') }}"
- "{{ domain }}"
- name: "For '{{ application_id }}': load docker and db"
include_role:
name: cmp-db-docker
vars:
docker_compose_flush_handlers: true
- name: "For '{{ application_id }}': configure {{domains | get_domain(application_id)}}.conf"
template:

View File

@@ -21,4 +21,4 @@ docker:
server:
domains:
canonical:
- baserow.{{ primary_domain }}
- baserow.{{ PRIMARY_DOMAIN }}

View File

@@ -18,7 +18,7 @@ server:
unsafe-inline: true
domains:
canonical:
- "meet.{{ primary_domain }}"
- "meet.{{ PRIMARY_DOMAIN }}"
credentials: {}
docker:

View File

@@ -1,11 +1,26 @@
---
- name: Render HTML-Location-Block in Variable
set_fact:
proxy_extra_configuration: >-
{{ lookup('ansible.builtin.template',
playbook_dir ~ '/roles/srv-proxy-7-4-core/templates/location/html.conf.j2') | trim }}
vars:
location: '^~ /html5client'
oauth2_proxy_enabled: false
proxy_lua_enabled: false
- name: "load docker, proxy for '{{application_id}}'"
include_role:
name: cmp-docker-proxy
vars:
docker_compose_flush_handlers: false
- name: "include 04_seed-database-to-backup.yml"
include_tasks: "{{ playbook_dir }}/roles/sys-bkp-docker-2-loc/tasks/04_seed-database-to-backup.yml"
- name: "Unset 'proxy_extra_configuration'"
set_fact:
proxy_extra_configuration: null
- name: configure websocket_upgrade.conf
copy:
src: "websocket_upgrade.conf"

View File

@@ -1,6 +1,6 @@
ENABLE_COTURN=true
COTURN_TLS_CERT_PATH={{ [ letsencrypt_live_path, ssl_cert_folder] | path_join }}/fullchain.pem
COTURN_TLS_KEY_PATH={{ [ letsencrypt_live_path, ssl_cert_folder] | path_join }}/privkey.pem
COTURN_TLS_CERT_PATH={{ [ LETSENCRYPT_LIVE_PATH, ssl_cert_folder] | path_join }}/fullchain.pem
COTURN_TLS_KEY_PATH={{ [ LETSENCRYPT_LIVE_PATH, ssl_cert_folder] | path_join }}/privkey.pem
ENABLE_GREENLIGHT={{ applications | get_app_conf(application_id, 'enable_greenlight', True) }}
# Enable Webhooks

View File

@@ -1,3 +1,3 @@
users:
administrator:
email: "administrator@{{ primary_domain }}"
email: "administrator@{{ PRIMARY_DOMAIN }}"

View File

@@ -17,4 +17,3 @@ docker_compose_skipp_file_creation: true # Handled in this role
docker_repository_address: "{{ applications | get_app_conf(application_id, 'docker.services.bigbluebutton.repository') }}"
docker_repository_branch: "{{ applications | get_app_conf(application_id, 'docker.services.bigbluebutton.version') }}"
docker_pull_git_repository: true
docker_compose_flush_handlers: false

View File

@@ -11,8 +11,8 @@ features:
server:
domains:
canonical:
web: "bskyweb.{{ primary_domain }}"
api: "bluesky.{{ primary_domain }}"
web: "bskyweb.{{ PRIMARY_DOMAIN }}"
api: "bluesky.{{ PRIMARY_DOMAIN }}"
docker:
services:
database:

View File

@@ -24,7 +24,7 @@
args:
REACT_APP_PDS_URL: "{{ WEB_PROTOCOL }}://{{domains[application_id].api}}" # URL des PDS
REACT_APP_API_URL: "{{ WEB_PROTOCOL }}://{{domains[application_id].api}}" # API-URL des PDS
REACT_APP_SITE_NAME: "{{primary_domain | upper}} - Bluesky"
REACT_APP_SITE_NAME: "{{PRIMARY_DOMAIN | upper}} - Bluesky"
REACT_APP_SITE_DESCRIPTION: "Decentral Social "
ports:
- "127.0.0.1:{{ports.localhost.http['web-app-bluesky_web']}}:8100"

View File

@@ -3,7 +3,7 @@ PDS_ADMIN_EMAIL="{{applications.bluesky.users.administrator.email}}"
PDS_SERVICE_DID="did:web:{{domains[application_id].api}}"
# See https://mattdyson.org/blog/2024/11/self-hosting-bluesky-pds/
PDS_SERVICE_HANDLE_DOMAINS=".{{primary_domain}}"
PDS_SERVICE_HANDLE_DOMAINS=".{{PRIMARY_DOMAIN}}"
PDS_JWT_SECRET="{{ bluesky_jwt_secret }}"
PDS_ADMIN_PASSWORD="{{bluesky_admin_password}}"
PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX="{{ bluesky_rotation_key }}"

View File

@@ -1,3 +1,3 @@
users:
administrator:
email: "administrator@{{ primary_domain }}"
email: "administrator@{{ PRIMARY_DOMAIN }}"

View File

@@ -1,7 +1,7 @@
server:
domains:
canonical:
- "collabora.{{ primary_domain }}"
- "collabora.{{ PRIMARY_DOMAIN }}"
docker:
services:
redis:

View File

@@ -16,10 +16,10 @@ server:
unsafe-inline: true
whitelist:
font-src:
- "http://*.{{primary_domain}}"
- "http://*.{{PRIMARY_DOMAIN}}"
domains:
canonical:
- "forum.{{ primary_domain }}"
- "forum.{{ PRIMARY_DOMAIN }}"
docker:
services:
database:

View File

@@ -1,6 +1,6 @@
- name: "reset (if enabled)"
include_tasks: 02_reset.yml
when: mode_reset | bool
when: MODE_RESET | bool
# Necessary for building: https://chat.openai.com/share/99d258cc-294b-4924-8eef-02fe419bb838
- name: install which

View File

@@ -3,4 +3,4 @@ features:
server:
domains:
canonical:
- elk.{{ primary_domain }}
- elk.{{ PRIMARY_DOMAIN }}

View File

@@ -18,17 +18,17 @@ server:
unsafe-eval: true
whitelist:
connect-src:
- wss://espocrm.{{ primary_domain }}
- wss://espocrm.{{ PRIMARY_DOMAIN }}
- "data:"
frame-src:
- https://s.espocrm.com/
domains:
aliases:
- "crm.{{ primary_domain }}"
- "crm.{{ PRIMARY_DOMAIN }}"
canonical:
- espocrm.{{ primary_domain }}
- espocrm.{{ PRIMARY_DOMAIN }}
email:
from_name: "Customer Relationship Management ({{ primary_domain }})"
from_name: "Customer Relationship Management ({{ PRIMARY_DOMAIN }})"
docker:
services:
database:

View File

@@ -30,7 +30,7 @@
$c = $app->getContainer();
$cfg = $c->get("config");
$writer = $c->get("injectableFactory")->create("\Espo\Core\Utils\Config\ConfigWriter");
$new = "{{ domains | get_url(application_id, WEB_PROTOCOL) }}";
$new = "{{ espocrm_url }}";
if ($cfg->get("siteUrl") !== $new) {
$writer->set("siteUrl", $new);
$writer->save();

Some files were not shown because too many files have changed in this diff Show More