mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-09-09 03:37:37 +02:00
Compare commits
16 Commits
4fa1c6cfbd
...
7f42462514
Author | SHA1 | Date | |
---|---|---|---|
7f42462514 | |||
41cd6b7702 | |||
a40d48bb03 | |||
2fba32d384 | |||
f2a765d69a | |||
c729edb525 | |||
597e9d5222 | |||
db0e030900 | |||
004507e233 | |||
e2014b9b59 | |||
567b1365c0 | |||
e99fa77b91 | |||
80dad1a5ed | |||
03290eafe1 | |||
58c64bd7c6 | |||
e497c001d6 |
@@ -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:
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
|
||||||
|
@@ -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 "
|
||||||
|
@@ -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
|
||||||
|
@@ -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) }}
|
||||||
"""
|
"""
|
||||||
|
@@ -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: []
|
||||||
|
|
||||||
|
@@ -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
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
|
@@ -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
|
||||||
|
@@ -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"
|
||||||
|
@@ -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))"
|
||||||
|
|
||||||
|
@@ -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 '' }}"
|
||||||
|
@@ -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"
|
||||||
|
1
roles/cmp-docker-oauth2/defaults/main.yml
Normal file
1
roles/cmp-docker-oauth2/defaults/main.yml
Normal 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
|
@@ -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
|
||||||
|
@@ -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:
|
||||||
|
@@ -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.
|
||||||
|
@@ -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
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
@@ -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 }}"
|
||||||
|
@@ -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 }}"
|
||||||
|
@@ -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:
|
||||||
|
@@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@@ -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
|
||||||
|
@@ -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/"*
|
||||||
|
@@ -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 %}
|
||||||
}
|
}
|
@@ -4,7 +4,7 @@ location ~* \.(jpg|jpeg|png|gif|webp|ico|svg)$ {
|
|||||||
add_header Cache-Control "public, max-age=2592000, immutable";
|
add_header Cache-Control "public, max-age=2592000, immutable";
|
||||||
|
|
||||||
# Cache on reverse proxy side
|
# Cache on reverse proxy side
|
||||||
proxy_pass http://127.0.0.1:{{http_port}};
|
proxy_pass http://127.0.0.1:{{ http_port }};
|
||||||
proxy_cache imgcache;
|
proxy_cache imgcache;
|
||||||
proxy_cache_valid 200 302 60m;
|
proxy_cache_valid 200 302 60m;
|
||||||
proxy_cache_valid 404 1m;
|
proxy_cache_valid 404 1m;
|
||||||
|
@@ -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
|
||||||
|
|
||||||
|
@@ -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
|
@@ -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: >
|
||||||
|
@@ -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:
|
||||||
|
@@ -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
|
||||||
|
@@ -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.
|
||||||
|
@@ -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:
|
||||||
|
@@ -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
|
@@ -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
|
|
@@ -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 '
|
||||||
|
@@ -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`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@@ -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:
|
||||||
|
@@ -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 Cloudflare’s zone and record API.
|
Ideal for environments where bulk or dynamic DNS updates are needed, this role abstracts away the complexity of Cloudflare’s 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.
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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" -%}
|
||||||
|
@@ -1 +1 @@
|
|||||||
<link rel="stylesheet" type="text/css" href="/global.css?version={{global_css_version}}">
|
<link rel="stylesheet" type="text/css" href="/global.css?version={{ global_css_version }}">
|
@@ -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
|
||||||
|
|
||||||
|
@@ -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 }}'
|
||||||
);
|
);
|
||||||
});
|
});
|
@@ -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:
|
||||||
|
@@ -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 %}
|
16
roles/srv-web-7-7-inj-port-ui-desktop/tasks/01_deploy.yml
Normal file
16
roles/srv-web-7-7-inj-port-ui-desktop/tasks/01_deploy.yml
Normal 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 }}"
|
@@ -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
|
||||||
|
@@ -0,0 +1 @@
|
|||||||
|
<script>{{ iframe_init_code_one_liner }}</script>
|
@@ -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>
|
@@ -1,48 +1,57 @@
|
|||||||
(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
|
||||||
function notifyParent() {
|
* @param {string} current_domain
|
||||||
if (window.self !== window.top) {
|
* @param {string} allowedOrigin - Parent origin for postMessage
|
||||||
try {
|
*/
|
||||||
window.parent.postMessage({
|
function initIframeHandler(primary_domain, current_domain, allowedOrigin) {
|
||||||
type: "iframeLocationChange",
|
function notifyParent() {
|
||||||
href: window.location.href
|
if (window.self !== window.top) {
|
||||||
}, allowedOrigin);
|
try {
|
||||||
} catch (e) {}
|
window.parent.postMessage(
|
||||||
|
{ type: "iframeLocationChange", href: window.location.href },
|
||||||
|
allowedOrigin
|
||||||
|
);
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function forceExternalLinks() {
|
function forceExternalLinks() {
|
||||||
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
|
||||||
a.target = "_blank";
|
if (!(url.hostname.endsWith(primary_domain) || url.hostname.endsWith(current_domain))) {
|
||||||
a.rel = "noopener";
|
a.target = "_blank";
|
||||||
}
|
a.rel = "noopener";
|
||||||
} catch (e) {}
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("load", function () {
|
||||||
|
notifyParent();
|
||||||
|
forceExternalLinks();
|
||||||
});
|
});
|
||||||
|
window.addEventListener("popstate", function () {
|
||||||
|
notifyParent();
|
||||||
|
forceExternalLinks();
|
||||||
|
});
|
||||||
|
|
||||||
|
// SPA support
|
||||||
|
var _pushState = history.pushState;
|
||||||
|
history.pushState = function () {
|
||||||
|
_pushState.apply(history, arguments);
|
||||||
|
notifyParent();
|
||||||
|
forceExternalLinks();
|
||||||
|
};
|
||||||
|
|
||||||
|
{% if MODE_DEBUG | bool %}
|
||||||
|
try { console.log("[iframe-sync] initIframeHandler installed."); } catch (e) {}
|
||||||
|
{% endif %}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("load", function() {
|
// expose for inline bootstrap
|
||||||
notifyParent();
|
global.initIframeHandler = initIframeHandler;
|
||||||
forceExternalLinks();
|
})(window);
|
||||||
});
|
|
||||||
window.addEventListener("popstate", function() {
|
|
||||||
notifyParent();
|
|
||||||
forceExternalLinks();
|
|
||||||
});
|
|
||||||
|
|
||||||
// SPA support
|
|
||||||
var _pushState = history.pushState;
|
|
||||||
history.pushState = function() {
|
|
||||||
_pushState.apply(history, arguments);
|
|
||||||
notifyParent();
|
|
||||||
forceExternalLinks();
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
{% if enable_debug | bool %}
|
|
||||||
console.log("[iframe-sync] Sender for iframe messages is active.");
|
|
||||||
{% endif %}
|
|
||||||
|
@@ -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 %}
|
2
roles/srv-web-7-7-inj-port-ui-desktop/vars/main.yml
Normal file
2
roles/srv-web-7-7-inj-port-ui-desktop/vars/main.yml
Normal 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 }}"
|
@@ -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'
|
@@ -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 }}"
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
@@ -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 }}"
|
||||||
|
@@ -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 }}"
|
||||||
|
@@ -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
|
@@ -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;"]
|
@@ -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
|
|
@@ -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:
|
||||||
|
@@ -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:
|
||||||
|
@@ -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: >
|
||||||
|
@@ -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.
|
||||||
|
@@ -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:
|
||||||
|
@@ -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.
|
||||||
|
@@ -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:
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
|
||||||
|
@@ -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]
|
||||||
|
@@ -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:
|
||||||
|
@@ -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():
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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:
|
||||||
|
@@ -16,4 +16,4 @@ docker:
|
|||||||
server:
|
server:
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "tickets.{{ primary_domain }}"
|
- "tickets.{{ PRIMARY_DOMAIN }}"
|
||||||
|
@@ -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
|
||||||
|
@@ -21,4 +21,4 @@ docker:
|
|||||||
server:
|
server:
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- baserow.{{ primary_domain }}
|
- baserow.{{ PRIMARY_DOMAIN }}
|
||||||
|
@@ -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:
|
||||||
|
@@ -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"
|
||||||
|
@@ -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
|
||||||
|
@@ -1,3 +1,3 @@
|
|||||||
users:
|
users:
|
||||||
administrator:
|
administrator:
|
||||||
email: "administrator@{{ primary_domain }}"
|
email: "administrator@{{ PRIMARY_DOMAIN }}"
|
@@ -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
|
|
@@ -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:
|
||||||
|
@@ -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"
|
||||||
|
@@ -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 }}"
|
||||||
|
@@ -1,3 +1,3 @@
|
|||||||
users:
|
users:
|
||||||
administrator:
|
administrator:
|
||||||
email: "administrator@{{ primary_domain }}"
|
email: "administrator@{{ PRIMARY_DOMAIN }}"
|
@@ -1,7 +1,7 @@
|
|||||||
server:
|
server:
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "collabora.{{ primary_domain }}"
|
- "collabora.{{ PRIMARY_DOMAIN }}"
|
||||||
docker:
|
docker:
|
||||||
services:
|
services:
|
||||||
redis:
|
redis:
|
||||||
|
@@ -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:
|
||||||
|
@@ -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
|
||||||
|
@@ -3,4 +3,4 @@ features:
|
|||||||
server:
|
server:
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- elk.{{ primary_domain }}
|
- elk.{{ PRIMARY_DOMAIN }}
|
||||||
|
@@ -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:
|
||||||
|
@@ -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();
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user