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(): def main():
args = parse_args() args = parse_args()
primary_domain = '{{ primary_domain }}' primary_domain = '{{ PRIMARY_DOMAIN }}'
become_pwd = '{{ lookup("password", "/dev/null length=42 chars=ascii_letters,digits") }}' become_pwd = '{{ lookup("password", "/dev/null length=42 chars=ascii_letters,digits") }}'
try: try:

View File

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

View File

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

View File

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

View File

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

View File

@@ -19,7 +19,7 @@ class FilterModule(object):
Usage in Jinja: Usage in Jinja:
{{ redirect_list {{ redirect_list
| add_redirect_if_group('lam', | add_redirect_if_group('lam',
'ldap.' ~ primary_domain, 'ldap.' ~ PRIMARY_DOMAIN,
domains | get_domain('web-app-lam'), domains | get_domain('web-app-lam'),
group_names) }} 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 # If true, sensitive credentials will be masked or hidden from all Ansible task logs
# Recommendet to set to true # Recommendet to set to true
@@ -19,54 +19,56 @@ HOST_THOUSAND_SEPARATOR: "."
HOST_DECIMAL_MARK: "," HOST_DECIMAL_MARK: ","
# Deployment mode # 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_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 WEB_PORT: "{{ 443 if WEB_PROTOCOL == 'https' else 80 }}" # Default port web applications will listen to
## Domain # Domain
primary_domain_tld: "localhost" # Top Level Domain of the server PRIMARY_DOMAIN: "localhost" # Primary Domain of the server
primary_domain_sld: "infinito" # Second Level Domain of the server PRIMARY_DOMAIN_tld: "{{ (PRIMARY_DOMAIN == 'localhost') | ternary('localhost', PRIMARY_DOMAIN.split('.')[-1]) }}" # Top Level Domain of the server
primary_domain: "{{primary_domain_sld}}.{{primary_domain_tld}}" # Primary 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 # Server Tact Variables
## Ours in which the server is "awake" (100% working). Rest of the time is reserved for maintanance ## 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. ## Random delay for systemd timers to avoid peak loads.
randomized_delay_sec: "5min" RANDOMIZED_DELAY_SEC: "5min"
# Runtime Variables for Process Control # 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 DNS_PROVIDER: cloudflare # The DNS Provider\Registrar for the domain
# You SHOULD NOT enable this on production servers
enable_debug: false
dns_provider: cloudflare # The DNS Provider\Registrar for the domain
# Which ACME method to use: webroot, cloudflare, or hetzner # Which ACME method to use: webroot, cloudflare, or hetzner
certbot_acme_challenge_method: "cloudflare" CERTBOT_ACME_CHALLENGE_METHOD: "cloudflare"
certbot_credentials_dir: /etc/certbot CERTBOT_CREDENTIALS_DIR: /etc/certbot
certbot_credentials_file: "{{ certbot_credentials_dir }}/{{ certbot_acme_challenge_method }}.ini" 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_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_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_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 # 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 # 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 # 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 Role Specific Parameters
DOCKER_RESTART_POLICY: "unless-stopped" DOCKER_RESTART_POLICY: "unless-stopped"
DOCKER_VARS_FILE: "{{ playbook_dir }}/roles/docker-compose/vars/docker-compose.yml" 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 # default value if not set via CLI (-e) or in playbook vars
allowed_applications: [] allowed_applications: []

View File

@@ -1,8 +1,9 @@
# Mode # Mode
# The following modes can be combined with each other # 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_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_TEST: false # Executes test routines instead of productive routines
mode_update: true # Executes updates MODE_UPDATE: true # Executes updates
mode_backup: true # Activates the backup before the update procedure MODE_BACKUP: true # Activates the backup before the update procedure
mode_cleanup: true # Cleanup unused files and configurations 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 # Email Configuration
default_system_email: default_system_email:
domain: "{{primary_domain}}" domain: "{{PRIMARY_DOMAIN}}"
host: "mail.{{primary_domain}}" host: "mail.{{PRIMARY_DOMAIN}}"
port: 465 port: 465
tls: true # true for TLS and false for SSL tls: true # true for TLS and false for SSL
start_tls: false start_tls: false

View File

@@ -24,3 +24,4 @@ nginx:
general: "/tmp/cache_nginx_general/" # Directory which nginx uses to cache general data 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 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_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_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_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_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_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_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_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 on_calendar_health_msmtp: "*-*-* 00:00:00" # Check once per day SMTP Server
## Schedule for Cleanup Tasks ## 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" on_calendar_backup_remote_to_local: "*-*-* 21:30:00"
## Schedule for Maintenance Tasks ## 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_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_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 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 # @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 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: "{{
(oidc.url (oidc.url
if (oidc is defined and oidc.url is defined) 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_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: defaults_oidc:
url: "{{ _oidc_url }}" 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 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"

View File

@@ -5,12 +5,12 @@
# Helper Variables: # Helper Variables:
# Keep in mind to mapp this variables if there is ever the possibility for the user to define them in the inventory # 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_docker_network_enabled: "{{ applications | get_app_conf('svc-db-openldap', 'network.docker') }}"
_ldap_protocol: "{{ 'ldap' if _ldap_docker_network_enabled else 'ldaps' }}" _ldap_protocol: "{{ 'ldap' if _ldap_docker_network_enabled else 'ldaps' }}"
_ldap_server_port: "{{ ports.localhost[_ldap_protocol]['svc-db-openldap'] }}" _ldap_server_port: "{{ ports.localhost[_ldap_protocol]['svc-db-openldap'] }}"
_ldap_name: "{{ applications | get_app_conf('svc-db-openldap', 'docker.services.openldap.name') }}" _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_user_id: "uid"
_ldap_filters_users_all: "(|(objectclass=inetOrgPerson))" _ldap_filters_users_all: "(|(objectclass=inetOrgPerson))"

View File

@@ -19,7 +19,7 @@ defaults_service_provider:
web-app-bluesky: >- web-app-bluesky: >-
{{ ('@' ~ users.contact.username ~ '.' ~ domains['web-app-bluesky'].api) {{ ('@' ~ users.contact.username ~ '.' ~ domains['web-app-bluesky'].api)
if 'web-app-bluesky' in group_names else '' }} 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 '' }}" 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 '' }}" 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 '' }}" 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). 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 ## 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. 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!** **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: Set the token in your Ansible inventory or secrets file:
```yaml ```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 # 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" - name: "For '{{ application_id }}': include role srv-proxy-6-6-domain"
include_role: include_role:
name: srv-proxy-6-6-domain name: srv-proxy-6-6-domain

View File

@@ -15,7 +15,7 @@
- name: Warn if repo is not reachable - name: Warn if repo is not reachable
debug: debug:
msg: "Warning: Repository is not reachable." 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 - name: Ensure systemd user directory exists
file: file:

View File

@@ -8,7 +8,7 @@ Refer to the [Docker Compose documentation](https://docs.docker.com/compose/), t
## Overview ## 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 ## Purpose
@@ -17,7 +17,7 @@ To offer a centralized, extensible system for managing containerized application
## Features ## Features
- **Dynamic Directory Structure:** Creates per-application instance folders for Compose setups. - **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. - **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. - **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. - **Integration Support:** Compatible with `srv-proxy-7-4-core` and other Infinito.Nexus service roles.

View File

@@ -11,13 +11,22 @@
- docker compose restart - docker compose restart
- docker compose just up - docker compose just up
- name: Build docker - name: Build docker compose
command: shell: |
cmd: docker compose build 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 }}" chdir: "{{ docker_compose.directories.instance }}"
executable: /bin/bash
environment: environment:
COMPOSE_HTTP_TIMEOUT: 600 COMPOSE_HTTP_TIMEOUT: 600
DOCKER_CLIENT_TIMEOUT: 600 DOCKER_CLIENT_TIMEOUT: 600
# Faster build
DOCKER_BUILDKIT: "1"
COMPOSE_DOCKER_CLI_BUILD: "1"
listen: listen:
- docker compose build - docker compose build

View File

@@ -10,7 +10,7 @@
- name: "reset (if enabled)" - name: "reset (if enabled)"
include_tasks: 01_reset.yml 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 # 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 # @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 }}" url: "{{ cf_api_url }}?name={{ domain | to_primary_domain }}"
method: GET method: GET
headers: headers:
Authorization: "Bearer {{ certbot_dns_api_token }}" Authorization: "Bearer {{ CERTBOT_DNS_API_TOKEN }}"
Content-Type: "application/json" Content-Type: "application/json"
return_content: yes return_content: yes
register: cf_zone_lookup_dev register: cf_zone_lookup_dev
@@ -43,8 +43,8 @@
- name: activate cloudflare cache development mode - name: activate cloudflare cache development mode
include_tasks: "cloudflare/02_enable_cf_dev_mode.yml" include_tasks: "cloudflare/02_enable_cf_dev_mode.yml"
when: (INFINITO_ENVIRONMENT | lower) == 'development' when: (ENVIRONMENT | lower) == 'development'
- name: purge cloudflare domain cache - name: purge cloudflare domain cache
include_tasks: "cloudflare/01_cleanup.yml" 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" url: "https://api.cloudflare.com/client/v4/zones/{{ cf_zone_id }}/purge_cache"
method: POST method: POST
headers: headers:
Authorization: "Bearer {{ certbot_dns_api_token }}" Authorization: "Bearer {{ CERTBOT_DNS_API_TOKEN }}"
Content-Type: "application/json" Content-Type: "application/json"
body: body:
purge_everything: true purge_everything: true
body_format: json body_format: json
return_content: yes 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 # roles/srv-proxy-6-6-domain/tasks/02_enable_cf_dev_mode.yml
--- ---
# Enables Cloudflare Development Mode (bypasses cache for ~3 hours). # 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. # Assumes `domain` and (optionally) `cf_zone_id` are available.
# Safe to run repeatedly; only changes when the mode is not already "on". # 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" url: "https://api.cloudflare.com/client/v4/zones/{{ cf_zone_id }}/settings/development_mode"
method: GET method: GET
headers: headers:
Authorization: "Bearer {{ certbot_dns_api_token }}" Authorization: "Bearer {{ CERTBOT_DNS_API_TOKEN }}"
Content-Type: "application/json" Content-Type: "application/json"
return_content: yes return_content: yes
register: cf_dev_mode_current register: cf_dev_mode_current
when: ASYNC_ENABLED | bool
- name: "Enable Cloudflare Development Mode" - name: "Enable Cloudflare Development Mode"
ansible.builtin.uri: ansible.builtin.uri:
url: "https://api.cloudflare.com/client/v4/zones/{{ cf_zone_id }}/settings/development_mode" url: "https://api.cloudflare.com/client/v4/zones/{{ cf_zone_id }}/settings/development_mode"
method: PATCH method: PATCH
headers: headers:
Authorization: "Bearer {{ certbot_dns_api_token }}" Authorization: "Bearer {{ CERTBOT_DNS_API_TOKEN }}"
Content-Type: "application/json" Content-Type: "application/json"
body: body:
value: "on" value: "on"
@@ -28,5 +29,8 @@
return_content: yes return_content: yes
register: cf_dev_mode_enable register: cf_dev_mode_enable
changed_when: > changed_when: >
ASYNC_ENABLED | bool and
cf_dev_mode_current.json.result.value is defined and cf_dev_mode_current.json.result.value is defined and
cf_dev_mode_current.json.result.value != 'on' 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 when: run_once_srv_proxy_6_6_domain is not defined
- include_tasks: "01_cloudflare.yml" - include_tasks: "01_cloudflare.yml"
when: dns_provider == "cloudflare" when: DNS_PROVIDER == "cloudflare"
- include_tasks: "{{ playbook_dir }}/tasks/utils/load_handlers.yml" - include_tasks: "{{ playbook_dir }}/tasks/utils/load_handlers.yml"
vars: vars:

View File

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

View File

@@ -6,7 +6,8 @@ location {{location}}
{% include 'roles/web-app-oauth2-proxy/templates/following_directives.conf.j2'%} {% include 'roles/web-app-oauth2-proxy/templates/following_directives.conf.j2'%}
{% endif %} {% 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 # headers
proxy_set_header Host $host; 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-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port {{ WEB_PORT }}; 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' %} {% include 'roles/srv-proxy-7-4-core/templates/headers/content_security_policy.conf.j2' %}
# WebSocket specific header # WebSocket specific header
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade"; proxy_set_header Connection $connection_upgrade;
# Activate buffering
# Needs to be enabled, so that lua can do str replaces
proxy_buffering on;
proxy_request_buffering on;
# timeouts # timeouts
proxy_connect_timeout 1s; proxy_connect_timeout 5s;
proxy_send_timeout 900s; proxy_send_timeout 900s;
proxy_read_timeout 900s; proxy_read_timeout 900s;
send_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'%} {% include 'roles/srv-web-7-7-inj-compose/templates/location.lua.j2'%}
{% endif %}
} }

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. - **ACME Challenge Selection:** Supports DNS plugins or webroot method automatically.
- **Wildcard Certificate Management:** Issues wildcard certificates when configured, saving effort for subdomain-heavy deployments. - **Wildcard Certificate Management:** Issues wildcard certificates when configured, saving effort for subdomain-heavy deployments.
- **Safe Cleanup:** Ensures that no unused certificates are left behind. - **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 ## 🔗 Learn More

View File

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

View File

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

View File

@@ -3,7 +3,7 @@
vars: vars:
wildcard_domain: true wildcard_domain: true
when: 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 - run_once_receive_certificate is not defined
- name: "Load dedicated certificate for domain" - name: "Load dedicated certificate for domain"
@@ -11,7 +11,7 @@
vars: vars:
wildcard_domain: false wildcard_domain: false
when: 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 - name: run the receive_certificate tasks once
set_fact: set_fact:

View File

@@ -6,20 +6,20 @@
- include_tasks: utils/run_once.yml - include_tasks: utils/run_once.yml
when: run_once_srv_web_6_6_tls_core is not defined when: run_once_srv_web_6_6_tls_core is not defined
- name: "Include flavor '{{ certbot_flavor }}' for '{{ domain }}'" - name: "Include flavor '{{ CERTBOT_FLAVOR }}' for '{{ domain }}'"
include_tasks: "{{ role_path }}/tasks/flavors/{{ certbot_flavor }}.yml" include_tasks: "{{ role_path }}/tasks/flavors/{{ CERTBOT_FLAVOR }}.yml"
#- name: "Cleanup dedicated cert for {{ domain }}" #- name: "Cleanup dedicated cert for {{ domain }}"
# command: >- # command: >-
# certbot delete --cert-name {{ domain }} --non-interactive # certbot delete --cert-name {{ domain }} --non-interactive
# when: # when:
# - mode_cleanup | bool # - MODE_CLEANUP | bool
# # Cleanup mode is enabled # # Cleanup mode is enabled
# - certbot_flavor != 'dedicated' # - CERTBOT_FLAVOR != 'dedicated'
# # Wildcard certificate is enabled # # 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 # # 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 # # The domain is not the primary domain
# register: certbot_result # register: certbot_result
# failed_when: certbot_result.rc != 0 and ("No certificate found with name" not in certbot_result.stderr) # 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 }}'" - name: "Find SSL cert folder for '{{ domain }}'"
cert_folder_find: cert_folder_find:
domain: "{{ domain }}" domain: "{{ domain }}"
cert_base_path: "{{ letsencrypt_live_path }}" cert_base_path: "{{ LETSENCRYPT_LIVE_PATH }}"
debug: "{{ enable_debug | default(false) }}" debug: "{{ MODE_DEBUG | default(false) }}"
register: cert_folder_result register: cert_folder_result
delegate_to: "{{ inventory_hostname }}" delegate_to: "{{ inventory_hostname }}"
changed_when: false 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. * **Configurable reset and cleanup** modes to purge and recreate directories.
* **Custom `nginx.conf`** template with sensible defaults for performance and security. * **Custom `nginx.conf`** template with sensible defaults for performance and security.
* **Stream proxy support**: includes `stream` block for TCP/UDP proxies. * **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 ## Debugging Tips
* **General logs**: `journalctl -f -u nginx` * **General logs**: `journalctl -f -u nginx`
* **Filter by host**: `journalctl -u nginx -f | grep "{{ inventory_hostname }}"` * **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)" - name: "reset (if enabled)"
include_tasks: 02_reset.yml include_tasks: 02_reset.yml
when: mode_reset | bool when: MODE_RESET | bool
- name: Ensure nginx configuration directories are present - name: Ensure nginx configuration directories are present
file: file:
@@ -47,9 +47,12 @@
mode: '0755' mode: '0755'
loop: > loop: >
{{ nginx.directories.data.values() | list }} {{ 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" - name: "Include tasks to create cache directories"
include_tasks: 03_cache_directories.yml include_tasks: 03_cache_directories.yml
when: run_once_nginx_reverse_proxy is not defined
- name: create nginx config file - name: create nginx config file
template: 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: file:
path: "{{ nginx.directories.configuration }}" path: "{{ nginx.directories.configuration }}"
state: absent state: absent

View File

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

View File

@@ -24,7 +24,7 @@ http
# -------------------------------------------------------------------------------- # --------------------------------------------------------------------------------
{# logging and debugging #} {# logging and debugging #}
{% if enable_debug | bool %} {% if MODE_DEBUG | bool %}
{# individual log format for better debugging #} {# individual log format for better debugging #}
log_format debug '$host - $remote_addr [$time_local] ' log_format debug '$host - $remote_addr [$time_local] '
'"$request" $status $body_bytes_sent ' '"$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. - A working `srv-web-7-4-core` setup.
- DNS managed via Cloudflare (for CAA record tasks) or equivalent ACME DNS flow. - DNS managed via Cloudflare (for CAA record tasks) or equivalent ACME DNS flow.
- Variables: - Variables:
- `letsencrypt_webroot_path` - `LETSENCRYPT_WEBROOT_PATH`
- `letsencrypt_live_path` - `LETSENCRYPT_LIVE_PATH`
- `on_calendar_renew_lets_encrypt_certificates` - `on_calendar_renew_lets_encrypt_certificates`
--- ---

View File

@@ -6,34 +6,34 @@
- name: install certbot DNS plugin - name: install certbot DNS plugin
community.general.pacman: community.general.pacman:
name: "certbot-dns-{{ certbot_acme_challenge_method }}" name: "certbot-dns-{{ CERTBOT_ACME_CHALLENGE_METHOD }}"
state: present state: present
when: when:
- run_once_srv_web_7_7_certbot is not defined - 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 - name: Ensure /etc/certbot directory exists
file: file:
path: "{{ certbot_credentials_dir }}" path: "{{ CERTBOT_CREDENTIALS_DIR }}"
state: directory state: directory
owner: root owner: root
group: root group: root
mode: '0755' mode: '0755'
when: when:
- run_once_srv_web_7_7_certbot is not defined - 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 - name: Install plugin credentials file
copy: copy:
dest: "{{ certbot_credentials_file }}" dest: "{{ CERTBOT_CREDENTIALS_FILE }}"
content: | 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 owner: root
group: root group: root
mode: '0600' mode: '0600'
when: when:
- run_once_srv_web_7_7_certbot is not defined - 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 - name: run the certbot role once
set_fact: 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. - 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`). - 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. - 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. 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. - **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`. - **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. - **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. - **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. - **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 }} - name: Create or update Cloudflare A-record for {{ item }}
community.general.cloudflare_dns: community.general.cloudflare_dns:
api_token: "{{ certbot_dns_api_token }}" api_token: "{{ CERTBOT_DNS_API_TOKEN }}"
zone: "{{ item.split('.')[-2:] | join('.') }}" zone: "{{ item.split('.')[-2:] | join('.') }}"
state: present state: present
type: A type: A

View File

@@ -10,11 +10,17 @@
set_fact: set_fact:
inj_enabled: inj_enabled:
javascript: "{{ applications | get_app_conf(application_id, 'features.javascript', False) }}" 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) }}" css: "{{ applications | get_app_conf(application_id, 'features.css', False) }}"
matomo: "{{ applications | get_app_conf(application_id, 'features.matomo', False) }}" matomo: "{{ applications | get_app_conf(application_id, 'features.matomo', False) }}"
port_ui: "{{ applications | get_app_conf(application_id, 'features.port-ui-desktop', 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}}" - name: "Load CDN for {{domain}}"
include_role: include_role:
name: web-svc-cdn name: web-svc-cdn
@@ -39,7 +45,7 @@
set_fact: set_fact:
inj_enabled: inj_enabled:
javascript: "{{ applications | get_app_conf(application_id, 'features.javascript', False) }}" 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) }}" css: "{{ applications | get_app_conf(application_id, 'features.css', False) }}"
matomo: "{{ applications | get_app_conf(application_id, 'features.matomo', False) }}" matomo: "{{ applications | get_app_conf(application_id, 'features.matomo', False) }}"
port_ui: "{{ applications | get_app_conf(application_id, 'features.port-ui-desktop', 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 name: srv-web-7-7-inj-matomo
when: inj_enabled.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 }}" - name: "Activate Javascript for {{ domain }}"
include_role: include_role:
name: srv-web-7-7-inj-javascript 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 -- build a list of body-injection snippets
local body_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 %} {% if applications | get_app_conf(application_id, 'features.' ~ body_feature, false) | bool %}
body_snippets[#body_snippets + 1] = [=[ body_snippets[#body_snippets + 1] = [=[
{%- include "roles/srv-web-7-7-inj-" ~ body_feature ~ "/templates/body_sub.j2" -%} {%- include "roles/srv-web-7-7-inj-" ~ body_feature ~ "/templates/body_sub.j2" -%}

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. Activates only when you enable the `javascript` feature for a given application, keeping your server blocks clean and performant.
- **Debug Mode** - **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 ## Author

View File

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

View File

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

View File

@@ -14,6 +14,6 @@ _paq.push(["enableLinkTracking"]);
g.async=true; g.src=u+"matomo.js"; s.parentNode.insertBefore(g,s); 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."); console.log("Matomo is loaded.");
{% endif %} {% 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: include_role:
name: srv-web-7-4-core name: srv-web-7-4-core
when: run_once_srv_web_7_4_core is not defined when: run_once_srv_web_7_4_core is not defined
- include_tasks: 01_deploy.yml
- include_tasks: utils/run_once.yml - include_tasks: utils/run_once.yml
when: run_once_srv_web_7_7_inj_port_ui_desktop is not defined 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: 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: 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: 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 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,14 +1,18 @@
(function() { (function (global) {
var primary = "{{ primary_domain }}"; /**
var allowedOrigin = "https://{{ domains | get_domain('web-app-port-ui') }}"; * 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() { function notifyParent() {
if (window.self !== window.top) { if (window.self !== window.top) {
try { try {
window.parent.postMessage({ window.parent.postMessage(
type: "iframeLocationChange", { type: "iframeLocationChange", href: window.location.href },
href: window.location.href allowedOrigin
}, allowedOrigin); );
} catch (e) {} } catch (e) {}
} }
} }
@@ -17,7 +21,8 @@
Array.prototype.forEach.call(document.querySelectorAll("a[href]"), function (a) { Array.prototype.forEach.call(document.querySelectorAll("a[href]"), function (a) {
try { try {
var url = new URL(a.href, location); var url = new URL(a.href, location);
if (!url.hostname.endsWith(primary)) { // 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.target = "_blank";
a.rel = "noopener"; a.rel = "noopener";
} }
@@ -41,8 +46,12 @@
notifyParent(); notifyParent();
forceExternalLinks(); forceExternalLinks();
}; };
})();
{% if enable_debug | bool %} {% if MODE_DEBUG | bool %}
console.log("[iframe-sync] Sender for iframe messages is active."); try { console.log("[iframe-sync] initIframeHandler installed."); } catch (e) {}
{% endif %} {% 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" - name: "Set CAA records for all base domains"
include_tasks: 01_set-caa-records.yml 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: fail:
msg: > msg: >
The variable "certbot_dns_api_token" must be defined and cannot be empty! The variable "CERTBOT_DNS_API_TOKEN" must be defined and cannot be empty!
when: (certbot_dns_api_token|default('')|trim) == '' when: (CERTBOT_DNS_API_TOKEN|default('')|trim) == ''
- name: "Ensure all CAA records are present" - name: "Ensure all CAA records are present"
community.general.cloudflare_dns: community.general.cloudflare_dns:
api_token: "{{ certbot_dns_api_token }}" api_token: "{{ CERTBOT_DNS_API_TOKEN }}"
zone: "{{ item.0 }}" zone: "{{ item.0 }}"
record: "@" record: "@"
type: CAA type: CAA
@@ -20,3 +20,5 @@
loop: "{{ base_sld_domains | product(caa_entries) | list }}" loop: "{{ base_sld_domains | product(caa_entries) | list }}"
loop_control: 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 #letsencrypt
location ^~ /.well-known/acme-challenge/ { location ^~ /.well-known/acme-challenge/ {
allow all; allow all;
root {{ letsencrypt_webroot_path }}; root {{ LETSENCRYPT_WEBROOT_PATH }};
default_type "text/plain"; default_type "text/plain";
try_files $uri =404; try_files $uri =404;
} }

View File

@@ -1,3 +1,3 @@
ssl_certificate {{ [ letsencrypt_live_path, ssl_cert_folder] | path_join }}/fullchain.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_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_trusted_certificate {{ [ LETSENCRYPT_LIVE_PATH, ssl_cert_folder] | path_join }}/chain.pem;

View File

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

View File

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

View File

@@ -2,5 +2,8 @@
- name: "For '{{ application_id }}': Load docker-compose" - name: "For '{{ application_id }}': Load docker-compose"
include_role: include_role:
name: docker-compose 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 - include_tasks: utils/run_once.yml
when: run_once_svc_prx_openresty is not defined when: run_once_svc_prx_openresty is not defined

View File

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

View File

@@ -8,7 +8,3 @@ database_type: ""
openresty_image: "openresty/openresty" openresty_image: "openresty/openresty"
openresty_version: "alpine" openresty_version: "alpine"
openresty_container: "{{ applications | get_app_conf(application_id, 'docker.services.openresty.name', True) }}" 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)" - name: "reset (if enabled)"
include_tasks: 03_reset.yml include_tasks: 03_reset.yml
when: mode_reset | bool when: MODE_RESET | bool
- name: configure sys-bkp-docker-2-loc-everything.infinito.service - name: configure sys-bkp-docker-2-loc-everything.infinito.service
template: template:

View File

@@ -11,7 +11,7 @@
database_host: "{{ database_host | default('undefined') }}" database_host: "{{ database_host | default('undefined') }}"
database_username: "{{ database_username | default('undefined') }}" database_username: "{{ database_username | default('undefined') }}"
database_password: "{{ database_password | 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" - name: "fail if not all required database variables are defined"
fail: fail:

View File

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

View File

@@ -4,7 +4,7 @@ This Ansible role handles resetting and cleaning up “Infinito.Nexus” systemd
## Description ## 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). 1. Run its reset tasks exactly once per play (`run_once_sys_rst_daemon` guard).
2. Find all `/etc/systemd/system/*.infinito.service` units. 2. Find all `/etc/systemd/system/*.infinito.service` units.

View File

@@ -1,6 +1,6 @@
- name: "reset (if enabled)" - name: "reset (if enabled)"
include_tasks: reset.yml 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 - name: run {{ role_name }} once
set_fact: set_fact:

View File

@@ -19,7 +19,7 @@ This Ansible role configures the OpenSSH daemon (`sshd`) by deploying a template
- **Security Defaults** - **Security Defaults**
- Disables password (`PasswordAuthentication no`) and root login (`PermitRootLogin no`) - Disables password (`PasswordAuthentication no`) and root login (`PermitRootLogin no`)
- Enforces public-key authentication (`PubkeyAuthentication yes`) - 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** - **Systemd Integration**
Handles daemon reload and service restart seamlessly on configuration changes. Handles daemon reload and service restart seamlessly on configuration changes.

View File

@@ -25,7 +25,7 @@
# Logging # Logging
#SyslogFacility AUTH #SyslogFacility AUTH
LogLevel {% if enable_debug | bool %}DEBUG3{% else %}INFO{% endif %} LogLevel {% if MODE_DEBUG | bool %}DEBUG3{% else %}INFO{% endif %}
# Authentication: # 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: 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). - 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. - 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 ## Purpose

View File

@@ -1,7 +1,7 @@
- name: "reset (if enabled)" - name: "reset (if enabled)"
include_tasks: 01_reset.yml 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 - name: run {{ role_name }} once
set_fact: set_fact:
@@ -20,5 +20,5 @@
name: "{{ sys_timer_file }}" name: "{{ sys_timer_file }}"
state: restarted state: restarted
enabled: yes 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] [Timer]
OnCalendar={{on_calendar}} OnCalendar={{on_calendar}}
RandomizedDelaySec={{randomized_delay_sec}} RandomizedDelaySec={{RANDOMIZED_DELAY_SEC}}
Persistent={{ persistent | default('false') }} Persistent={{ persistent | default('false') }}
[Install] [Install]

View File

@@ -8,7 +8,7 @@
name: sys-bkp-docker-2-loc-everything.infinito.service name: sys-bkp-docker-2-loc-everything.infinito.service
state: started state: started
when: when:
- mode_backup | bool - MODE_BACKUP | bool
- name: create {{update_docker_script}} - name: create {{update_docker_script}}
template: 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. Runs the database migration for Mastodon to ensure all required tables are up to date.
""" """
print("Starting Mastodon database migration.") 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.") print("Mastodon database migration complete.")
def upgrade_listmonk(): def upgrade_listmonk():

View File

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

View File

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

View File

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

View File

@@ -16,4 +16,4 @@ docker:
server: server:
domains: domains:
canonical: 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" - name: "For '{{ application_id }}': include role to receive certs & do modification routines"
include_role: include_role:
name: srv-web-7-6-composer name: srv-web-7-6-composer
@@ -13,6 +9,12 @@
- "{{ domains | get_domain('web-app-mailu') }}" - "{{ domains | get_domain('web-app-mailu') }}"
- "{{ domain }}" - "{{ 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" - name: "For '{{ application_id }}': configure {{domains | get_domain(application_id)}}.conf"
template: template:
src: roles/srv-proxy-7-4-core/templates/vhost/basic.conf.j2 src: roles/srv-proxy-7-4-core/templates/vhost/basic.conf.j2

View File

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

View File

@@ -18,7 +18,7 @@ server:
unsafe-inline: true unsafe-inline: true
domains: domains:
canonical: canonical:
- "meet.{{ primary_domain }}" - "meet.{{ PRIMARY_DOMAIN }}"
credentials: {} credentials: {}
docker: 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}}'" - name: "load docker, proxy for '{{application_id}}'"
include_role: include_role:
name: cmp-docker-proxy name: cmp-docker-proxy
vars:
docker_compose_flush_handlers: false
- name: "include 04_seed-database-to-backup.yml" - 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" 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 - name: configure websocket_upgrade.conf
copy: copy:
src: "websocket_upgrade.conf" src: "websocket_upgrade.conf"

View File

@@ -1,6 +1,6 @@
ENABLE_COTURN=true ENABLE_COTURN=true
COTURN_TLS_CERT_PATH={{ [ letsencrypt_live_path, ssl_cert_folder] | path_join }}/fullchain.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 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_GREENLIGHT={{ applications | get_app_conf(application_id, 'enable_greenlight', True) }}
# Enable Webhooks # Enable Webhooks

View File

@@ -1,3 +1,3 @@
users: users:
administrator: 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_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_repository_branch: "{{ applications | get_app_conf(application_id, 'docker.services.bigbluebutton.version') }}"
docker_pull_git_repository: true docker_pull_git_repository: true
docker_compose_flush_handlers: false

View File

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

View File

@@ -24,7 +24,7 @@
args: args:
REACT_APP_PDS_URL: "{{ WEB_PROTOCOL }}://{{domains[application_id].api}}" # URL des PDS 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_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 " REACT_APP_SITE_DESCRIPTION: "Decentral Social "
ports: ports:
- "127.0.0.1:{{ports.localhost.http['web-app-bluesky_web']}}:8100" - "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}}" PDS_SERVICE_DID="did:web:{{domains[application_id].api}}"
# See https://mattdyson.org/blog/2024/11/self-hosting-bluesky-pds/ # 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_JWT_SECRET="{{ bluesky_jwt_secret }}"
PDS_ADMIN_PASSWORD="{{bluesky_admin_password}}" PDS_ADMIN_PASSWORD="{{bluesky_admin_password}}"
PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX="{{ bluesky_rotation_key }}" PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX="{{ bluesky_rotation_key }}"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -30,7 +30,7 @@
$c = $app->getContainer(); $c = $app->getContainer();
$cfg = $c->get("config"); $cfg = $c->get("config");
$writer = $c->get("injectableFactory")->create("\Espo\Core\Utils\Config\ConfigWriter"); $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) { if ($cfg->get("siteUrl") !== $new) {
$writer->set("siteUrl", $new); $writer->set("siteUrl", $new);
$writer->save(); $writer->save();

View File

@@ -2,6 +2,8 @@
- name: "load docker, db and proxy for {{application_id}}" - name: "load docker, db and proxy for {{application_id}}"
include_role: include_role:
name: cmp-db-docker-proxy name: cmp-db-docker-proxy
vars:
docker_compose_flush_handlers: true
- name: Check if config.php exists in EspoCRM - name: Check if config.php exists in EspoCRM
command: docker exec --user root {{ espocrm_name }} test -f {{ espocrm_config_file }} command: docker exec --user root {{ espocrm_name }} test -f {{ espocrm_config_file }}
@@ -15,7 +17,6 @@
- name: Flush handlers to make DB available before password reset - name: Flush handlers to make DB available before password reset
meta: flush_handlers meta: flush_handlers
when: docker_compose_flush_handlers | bool
- name: Set OIDC scopes in EspoCRM config (inside web container) - name: Set OIDC scopes in EspoCRM config (inside web container)
ansible.builtin.shell: | ansible.builtin.shell: |

View File

@@ -23,7 +23,7 @@ ESPOCRM_ADMIN_USERNAME={{ applications | get_app_conf(application_id, 'users.adm
ESPOCRM_ADMIN_PASSWORD={{ applications | get_app_conf(application_id, 'credentials.administrator_password', True) }} ESPOCRM_ADMIN_PASSWORD={{ applications | get_app_conf(application_id, 'credentials.administrator_password', True) }}
# Public base URL of the EspoCRM instance # Public base URL of the EspoCRM instance
ESPOCRM_SITE_URL={{ domains | get_url(application_id, WEB_PROTOCOL) }} ESPOCRM_SITE_URL={{ espocrm_url }}
# ------------------------------------------------ # ------------------------------------------------
# General UI & locale settings # General UI & locale settings
@@ -41,7 +41,7 @@ ESPOCRM_CONFIG_DEFAULT_CURRENCY={{ HOST_CURRENCY }}
# ------------------------------------------------ # ------------------------------------------------
# Logger # Logger
# ------------------------------------------------ # ------------------------------------------------
ESPOCRM_CONFIG_LOGGER_LEVEL={{ 'DEBUG' if enable_debug | bool else 'INFO' }} ESPOCRM_CONFIG_LOGGER_LEVEL={{ 'DEBUG' if MODE_DEBUG | bool else 'INFO' }}
ESPOCRM_CONFIG_LOGGER_PATH=php://stdout ESPOCRM_CONFIG_LOGGER_PATH=php://stdout
ESPOCRM_CONFIG_LOGGER_ROTATION=false ESPOCRM_CONFIG_LOGGER_ROTATION=false
@@ -94,7 +94,7 @@ 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=https://{{ domains | get_domain(application_id) }}/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

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