Adding option to brand across services where possible

This commit is contained in:
Ian Roddis
2026-03-23 14:54:16 -03:00
parent 6c914d5b82
commit 72f171e88f
20 changed files with 191 additions and 4 deletions
+2 -1
View File
@@ -1,7 +1,8 @@
{ {
"permissions": { "permissions": {
"allow": [ "allow": [
"Bash(ls -la /home/iroddis/dev/sovereign/.*)" "Bash(ls -la /home/iroddis/dev/sovereign/.*)",
"Bash(ls -la /home/iroddis/dev/sovereign/roles/*/templates/)"
] ]
} }
} }
+19
View File
@@ -7,6 +7,25 @@
# Base domain - all services are subdomains of this # Base domain - all services are subdomains of this
base_domain: "example.com" base_domain: "example.com"
# =============================================================================
# BRANDING
# Applied across all services that support custom branding.
# =============================================================================
# Display name shown in service UIs and email subjects
tenant_name: "Example Corp"
# Path to a logo image on the Ansible control machine (PNG or SVG recommended).
# Leave empty to use each service's default logo.
# Example: "files/logo.png"
tenant_logo_local_path: ""
# Primary brand colour (hex). Used for backgrounds, buttons, and highlights.
tenant_primary_color: "#2563eb"
# Accent / secondary colour (hex).
tenant_accent_color: "#1e40af"
# Base directory for all service data # Base directory for all service data
sovereign_base_dir: /opt/sovereign sovereign_base_dir: /opt/sovereign
+15
View File
@@ -7,10 +7,25 @@
loop: loop:
- "{{ authentik_data_dir }}" - "{{ authentik_data_dir }}"
- "{{ authentik_data_dir }}/media" - "{{ authentik_data_dir }}/media"
- "{{ authentik_data_dir }}/media/branding"
- "{{ authentik_data_dir }}/custom-templates" - "{{ authentik_data_dir }}/custom-templates"
- "{{ authentik_data_dir }}/blueprints"
- "{{ authentik_data_dir }}/certs" - "{{ authentik_data_dir }}/certs"
- "{{ authentik_data_dir }}/postgres" - "{{ authentik_data_dir }}/postgres"
- name: Deploy Authentik branding blueprint
ansible.builtin.template:
src: branding-blueprint.yaml.j2
dest: "{{ authentik_data_dir }}/blueprints/sovereign-branding.yaml"
mode: '0644'
- name: Copy tenant logo to Authentik media
ansible.builtin.copy:
src: "{{ tenant_logo_local_path }}"
dest: "{{ authentik_data_dir }}/media/branding/logo.png"
mode: '0644'
when: tenant_logo_local_path | default('') != ''
- name: Deploy Authentik docker-compose - name: Deploy Authentik docker-compose
ansible.builtin.template: ansible.builtin.template:
src: docker-compose.yml.j2 src: docker-compose.yml.j2
@@ -0,0 +1,23 @@
---
# Sovereign branding blueprint — applied automatically by Authentik on startup.
# Customises the default brand with the tenant name, logo, and colour scheme.
version: 1
metadata:
name: Sovereign Tenant Branding
labels:
blueprints.goauthentik.io/description: Tenant branding configuration managed by Ansible
entries:
- model: authentik_brands.brand
state: present
identifiers:
domain: authentik-default
attrs:
branding_title: "{{ tenant_name }}"
{% if tenant_logo_local_path | default('') != '' %}
branding_logo: /media/branding/logo.png
branding_favicon: /media/branding/logo.png
{% endif %}
branding_custom_css: |
:root {
--ak-accent: {{ tenant_primary_color | default('#fd4b2d') }};
}
@@ -61,6 +61,7 @@ services:
volumes: volumes:
- {{ authentik_data_dir }}/media:/media - {{ authentik_data_dir }}/media:/media
- {{ authentik_data_dir }}/custom-templates:/templates - {{ authentik_data_dir }}/custom-templates:/templates
- {{ authentik_data_dir }}/blueprints:/blueprints/custom
- {{ authentik_data_dir }}/certs:/certs - {{ authentik_data_dir }}/certs:/certs
ports: ports:
- "127.0.0.1:9001:9000" - "127.0.0.1:9001:9000"
+8
View File
@@ -7,8 +7,16 @@
loop: loop:
- "{{ forgejo_data_dir }}" - "{{ forgejo_data_dir }}"
- "{{ forgejo_data_dir }}/data" - "{{ forgejo_data_dir }}/data"
- "{{ forgejo_data_dir }}/data/gitea/public/img"
- "{{ forgejo_data_dir }}/config" - "{{ forgejo_data_dir }}/config"
- name: Copy tenant logo to Forgejo custom assets
ansible.builtin.copy:
src: "{{ tenant_logo_local_path }}"
dest: "{{ forgejo_data_dir }}/data/gitea/public/img/logo.png"
mode: '0644'
when: tenant_logo_local_path | default('') != ''
- name: Deploy Forgejo docker-compose - name: Deploy Forgejo docker-compose
ansible.builtin.template: ansible.builtin.template:
src: docker-compose.yml.j2 src: docker-compose.yml.j2
@@ -49,6 +49,7 @@ services:
FORGEJO__openid__ENABLE_OPENID_SIGNUP: "false" FORGEJO__openid__ENABLE_OPENID_SIGNUP: "false"
FORGEJO__oauth2_client__REGISTER_EMAIL_CONFIRM: "false" FORGEJO__oauth2_client__REGISTER_EMAIL_CONFIRM: "false"
FORGEJO__oauth2_client__ENABLE_AUTO_REGISTRATION: "true" FORGEJO__oauth2_client__ENABLE_AUTO_REGISTRATION: "true"
FORGEJO____APP_NAME: "{{ tenant_name }}"
FORGEJO__log__LEVEL: warn FORGEJO__log__LEVEL: warn
ports: ports:
- "{{ forgejo_ssh_port }}:22" - "{{ forgejo_ssh_port }}:22"
+15
View File
@@ -11,6 +11,21 @@
- "{{ jitsi_data_dir }}/jicofo" - "{{ jitsi_data_dir }}/jicofo"
- "{{ jitsi_data_dir }}/jvb" - "{{ jitsi_data_dir }}/jvb"
- name: Deploy Jitsi custom interface config
ansible.builtin.template:
src: custom-interface-config.js.j2
dest: "{{ jitsi_data_dir }}/web/custom-interface-config.js"
mode: '0644'
notify: restart jitsi
- name: Copy tenant logo to Jitsi watermark
ansible.builtin.copy:
src: "{{ tenant_logo_local_path }}"
dest: "{{ jitsi_data_dir }}/web/watermark.png"
mode: '0644'
when: tenant_logo_local_path | default('') != ''
notify: restart jitsi
- name: Deploy Jitsi docker-compose - name: Deploy Jitsi docker-compose
ansible.builtin.template: ansible.builtin.template:
src: docker-compose.yml.j2 src: docker-compose.yml.j2
@@ -0,0 +1,6 @@
// Sovereign tenant branding overrides for Jitsi Meet.
// This file is appended to interface_config.js by the Jitsi web container.
interfaceConfig.APP_NAME = '{{ tenant_name }}';
interfaceConfig.NATIVE_APP_NAME = '{{ tenant_name }}';
interfaceConfig.PROVIDER_NAME = '{{ tenant_name }}';
interfaceConfig.BRAND_WATERMARK_LINK = 'https://{{ base_domain }}';
@@ -5,6 +5,7 @@ services:
restart: unless-stopped restart: unless-stopped
environment: environment:
PUBLIC_URL: "https://{{ jitsi_domain }}" PUBLIC_URL: "https://{{ jitsi_domain }}"
JITSI_WATERMARKLINK: "https://{{ base_domain }}"
XMPP_SERVER: jitsi-prosody XMPP_SERVER: jitsi-prosody
XMPP_DOMAIN: meet.jitsi XMPP_DOMAIN: meet.jitsi
XMPP_AUTH_DOMAIN: auth.meet.jitsi XMPP_AUTH_DOMAIN: auth.meet.jitsi
+2
View File
@@ -1,2 +1,4 @@
--- ---
matrix_data_dir: "{{ sovereign_base_dir }}/matrix" matrix_data_dir: "{{ sovereign_base_dir }}/matrix"
# Element theme: "light" or "dark"
element_theme: "light"
@@ -5,7 +5,7 @@
"server_name": "{{ matrix_domain }}" "server_name": "{{ matrix_domain }}"
} }
}, },
"brand": "Element", "brand": "{{ tenant_name }}",
"integrations_ui_url": "https://scalar.vector.im/", "integrations_ui_url": "https://scalar.vector.im/",
"integrations_rest_url": "https://scalar.vector.im/api", "integrations_rest_url": "https://scalar.vector.im/api",
"bug_report_endpoint_url": "", "bug_report_endpoint_url": "",
@@ -13,7 +13,7 @@
"show_labs_settings": false, "show_labs_settings": false,
"features": {}, "features": {},
"default_federate": true, "default_federate": true,
"default_theme": "light", "default_theme": "{{ element_theme | default('light') }}",
"room_directory": { "room_directory": {
"servers": ["{{ matrix_domain }}"] "servers": ["{{ matrix_domain }}"]
}, },
+30
View File
@@ -19,3 +19,33 @@
community.docker.docker_compose_v2: community.docker.docker_compose_v2:
project_src: "{{ nextcloud_data_dir }}" project_src: "{{ nextcloud_data_dir }}"
state: present state: present
- name: Wait for Nextcloud to be ready
ansible.builtin.command:
cmd: docker exec -u www-data nextcloud php occ status --output=json
register: nc_status
until: (nc_status.stdout | from_json).installed is defined and (nc_status.stdout | from_json).installed
retries: 30
delay: 10
changed_when: false
- name: Copy tenant logo into Nextcloud container volume
ansible.builtin.copy:
src: "{{ tenant_logo_local_path }}"
dest: "{{ nextcloud_data_dir }}/data/sovereign-logo.png"
mode: '0644'
when: tenant_logo_local_path | default('') != ''
- name: Configure Nextcloud theming — name and colour
ansible.builtin.command:
cmd: docker exec -u www-data nextcloud php occ theming:config {{ item.key }} "{{ item.value }}"
loop:
- { key: name, value: "{{ tenant_name }}" }
- { key: color, value: "{{ tenant_primary_color | default('#2563eb') }}" }
changed_when: false
- name: Configure Nextcloud theming — logo
ansible.builtin.command:
cmd: docker exec -u www-data nextcloud php occ theming:config logo /var/www/html/sovereign-logo.png
when: tenant_logo_local_path | default('') != ''
changed_when: false
+2
View File
@@ -1,2 +1,4 @@
--- ---
roundcube_data_dir: "{{ sovereign_base_dir }}/roundcube" roundcube_data_dir: "{{ sovereign_base_dir }}/roundcube"
# Roundcube UI skin. The official image ships "elastic" and "larry".
roundcube_skin: "elastic"
+8
View File
@@ -6,6 +6,14 @@
mode: '0755' mode: '0755'
loop: loop:
- "{{ roundcube_data_dir }}" - "{{ roundcube_data_dir }}"
- "{{ roundcube_data_dir }}/config"
- name: Deploy Roundcube custom branding config
ansible.builtin.template:
src: custom.inc.php.j2
dest: "{{ roundcube_data_dir }}/config/custom.inc.php"
mode: '0644'
notify: restart roundcube
- name: Deploy Roundcube docker-compose - name: Deploy Roundcube docker-compose
ansible.builtin.template: ansible.builtin.template:
@@ -0,0 +1,5 @@
<?php
// Sovereign tenant branding for Roundcube.
// This file is merged with the auto-generated config by the container entrypoint.
$config['product_name'] = '{{ tenant_name }} Webmail';
@@ -35,7 +35,9 @@ services:
ROUNDCUBEMAIL_SMTP_PORT: 587 ROUNDCUBEMAIL_SMTP_PORT: 587
ROUNDCUBEMAIL_DES_KEY: "{{ roundcube_des_key }}" ROUNDCUBEMAIL_DES_KEY: "{{ roundcube_des_key }}"
ROUNDCUBEMAIL_PLUGINS: "archive,zipdownload,managesieve,jqueryui" ROUNDCUBEMAIL_PLUGINS: "archive,zipdownload,managesieve,jqueryui"
ROUNDCUBEMAIL_SKIN: elastic ROUNDCUBEMAIL_SKIN: "{{ roundcube_skin }}"
volumes:
- {{ roundcube_data_dir }}/config/custom.inc.php:/var/roundcube/config/custom.inc.php:ro
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
- "traefik.http.routers.roundcube.rule=Host(`{{ roundcube_domain }}`)" - "traefik.http.routers.roundcube.rule=Host(`{{ roundcube_domain }}`)"
+16
View File
@@ -7,6 +7,22 @@
loop: loop:
- "{{ wazuh_data_dir }}" - "{{ wazuh_data_dir }}"
- "{{ wazuh_data_dir }}/config" - "{{ wazuh_data_dir }}/config"
- "{{ wazuh_data_dir }}/dashboard-config"
- name: Deploy Wazuh dashboard branding config
ansible.builtin.template:
src: opensearch_dashboards.yml.j2
dest: "{{ wazuh_data_dir }}/dashboard-config/opensearch_dashboards.yml"
mode: '0644'
notify: restart wazuh
- name: Copy tenant logo to Wazuh dashboard assets
ansible.builtin.copy:
src: "{{ tenant_logo_local_path }}"
dest: "{{ wazuh_data_dir }}/dashboard-config/branding-logo.png"
mode: '0644'
when: tenant_logo_local_path | default('') != ''
notify: restart wazuh
- name: Set vm.max_map_count for Wazuh indexer (OpenSearch) - name: Set vm.max_map_count for Wazuh indexer (OpenSearch)
ansible.posix.sysctl: ansible.posix.sysctl:
@@ -84,6 +84,10 @@ services:
- {{ wazuh_data_dir }}/wazuh-indexer-certs/wazuh.dashboard.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard.pem - {{ wazuh_data_dir }}/wazuh-indexer-certs/wazuh.dashboard.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard.pem
- {{ wazuh_data_dir }}/wazuh-indexer-certs/wazuh.dashboard-key.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard-key.pem - {{ wazuh_data_dir }}/wazuh-indexer-certs/wazuh.dashboard-key.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard-key.pem
- {{ wazuh_data_dir }}/wazuh-indexer-certs/root-ca.pem:/usr/share/wazuh-dashboard/certs/root-ca.pem - {{ wazuh_data_dir }}/wazuh-indexer-certs/root-ca.pem:/usr/share/wazuh-dashboard/certs/root-ca.pem
- {{ wazuh_data_dir }}/dashboard-config/opensearch_dashboards.yml:/usr/share/wazuh-dashboard/config/opensearch_dashboards.yml:ro
{% if tenant_logo_local_path | default('') != '' %}
- {{ wazuh_data_dir }}/dashboard-config/branding-logo.png:/usr/share/wazuh-dashboard/plugins/wazuh/public/assets/custom/logos/branding-logo.png:ro
{% endif %}
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
- "traefik.http.routers.wazuh.rule=Host(`{{ wazuh_domain }}`)" - "traefik.http.routers.wazuh.rule=Host(`{{ wazuh_domain }}`)"
@@ -0,0 +1,28 @@
# Sovereign tenant branding for Wazuh Dashboard (OpenSearch Dashboards).
# Mounted over the default config — all required fields must be present.
server.host: "0.0.0.0"
server.port: 5601
opensearch.hosts: ["https://wazuh-indexer:9200"]
opensearch.ssl.verificationMode: certificate
opensearch.username: kibanaserver
opensearch.password: "{{ wazuh_admin_password }}"
opensearch.requestHeadersAllowlist: ["securitytenant","Authorization"]
opensearch_security.multitenancy.enabled: false
opensearch_security.readonly_mode.roles: ["kibana_read_only"]
server.ssl.enabled: true
server.ssl.key: /usr/share/wazuh-dashboard/certs/wazuh-dashboard-key.pem
server.ssl.certificate: /usr/share/wazuh-dashboard/certs/wazuh-dashboard.pem
opensearch.ssl.certificateAuthorities: ["/usr/share/wazuh-dashboard/certs/root-ca.pem"]
uiSettings.overrides.defaultRoute: /app/wazuh
opensearchDashboards.branding:
applicationTitle: "{{ tenant_name }} Security"
{% if tenant_logo_local_path | default('') != '' %}
logo:
defaultUrl: "/ui/logos/branding-logo.png"
darkModeUrl: "/ui/logos/branding-logo.png"
mark:
defaultUrl: "/ui/logos/branding-logo.png"
darkModeUrl: "/ui/logos/branding-logo.png"
{% endif %}