Compare commits

...

4 Commits

24 changed files with 195 additions and 119 deletions

View File

@ -71,6 +71,9 @@ defaults_applications:
keycloak: keycloak:
version: "latest" version: "latest"
administrator_username: "{{administrator_username}}" # Administrator Username for Keycloak administrator_username: "{{administrator_username}}" # Administrator Username for Keycloak
# database_password: # Needs to be defined in inventory file
# administrator_password: # Needs to be defined in inventory file
## LDAP ## LDAP
ldap: ldap:
@ -147,7 +150,7 @@ defaults_applications:
version: "latest" version: "latest"
redirect_url: "https://{{domains.keycloak}}/auth/realms/{{primary_domain}}/protocol/openid-connect/auth" # The redirect URL for the OAuth2 flow. It should match the redirect URL configured in Keycloak. redirect_url: "https://{{domains.keycloak}}/auth/realms/{{primary_domain}}/protocol/openid-connect/auth" # The redirect URL for the OAuth2 flow. It should match the redirect URL configured in Keycloak.
allowed_roles: admin # Restrict it default to admin role. Use the vars/main.yml to open the specific role for other groups allowed_roles: admin # Restrict it default to admin role. Use the vars/main.yml to open the specific role for other groups
cookie_secret: "{{applications.oauth2_proxy.cookie_secret}}" # Default use wildcard for primary domain, subdomain client specific configuration in vars files in the roles is possible openssl rand -hex 16 cookie_secret: "{{ applications.oauth2_proxy.cookie_secret if applications.oauth2_proxy is defined else '' }}" # Default use wildcard for primary domain, subdomain client specific configuration in vars files in the roles is possible openssl rand -hex 16
## Open Project ## Open Project
openproject: openproject:

View File

@ -2,19 +2,12 @@ global_theming:
enabled: true enabled: true
css: css:
colors: colors:
# For buttons and highlights symbolizes the sea (slate blue) # General Colors
primary: "#4F6D7A" base: "#001f3f"
# For navigation, footers, etc. a subtly light brown with a grayish tint (earth)
secondary: "#C8A28F" # Special Action Colors
# For the general (light mode) background an azul, bluegray tone
background: "#DCE6F2" # Sucess Color
# For dark mode: a dark, bluegray background
background_dark: "#2E3B4E"
# For the text true black
text: "#2E3B4E"
# Accent color (e.g., for links or buttons) a golden tone symbolizing the sun
accent: "#FFD700"
# As the positive/success color a light, slightly grayish green (forest)
success: "#B2D3B2" success: "#B2D3B2"
# As the warning color a light brown (earth) # As the warning color a light brown (earth)
warning: "#D2B48C" warning: "#D2B48C"
@ -22,20 +15,4 @@ global_theming:
error: "#DC3545" error: "#DC3545"
# As the info color a very light blue (symbolizing the sky) # As the info color a very light blue (symbolizing the sky)
info: "#F0F8FF" info: "#F0F8FF"
# Links in this case, identical to primary (sea)
link: "#4F6D7A"
# Button text white
button_text: "#FFFFFF"
# Shadows & borders (unchanged)
shadow: "rgba(0, 0, 0, 0.1)"
border: "#DDDDDD"
# New settings for cards and buttons:
# Cards: a slightly lighter tone than the background for subtle differentiation
card_bg_color: "#E6EFF9"
# Buttons: a background that is only a bit darker than the main background and in a blue tone
button_bg_color: "#C6D7E6"
# Bold, larger shadow for containers (cards, dropdowns, etc.)
large_shadow: "4px 4px 15px rgba(0, 0, 0, 0.2)"
# Reduced shadow for buttons
small_shadow: "1px 1px 3px rgba(0, 0, 0, 0.1)"
global_theming_enabled: true global_theming_enabled: true

View File

@ -15,7 +15,7 @@
{{ {{
(defaults_redirect_domain_mappings | items2dict(key_name='source', value_name='target')) (defaults_redirect_domain_mappings | items2dict(key_name='source', value_name='target'))
| combine( | combine(
(redirect_domain_mappings | items2dict(key_name='source', value_name='target')), (redirect_domain_mappings | default([]) | items2dict(key_name='source', value_name='target')),
recursive=True recursive=True
) )
}} }}

View File

@ -3,4 +3,4 @@
become: true become: true
roles: roles:
- role: system-storage-optimizer - role: system-storage-optimizer
when: "(path_mass_storage is defined or path_rapid_storage is defined) and enable_system_storage_optimizer | bool" when: "(path_mass_storage is defined or path_rapid_storage is defined) and enable_system_storage_optimizer | bool and (docker_enabled is defined and docker_enabled | bool) "

View File

@ -4,7 +4,8 @@
name: docker-central-database name: docker-central-database
- name: "include tasks to receive attendize certbot certificate" - name: "include tasks to receive attendize certbot certificate"
include_tasks: recieve-certbot-certificate.yml include_role:
name: nginx-https-recieve-certificate
vars: vars:
domain: "{{ item }}" domain: "{{ item }}"
loop: loop:

View File

@ -17,32 +17,6 @@ The role integrates Keycloak with PostgreSQL as a database and supports operatio
- Docker and Docker Compose must be installed on the target system. - Docker and Docker Compose must be installed on the target system.
- A working NGINX proxy for forwarding requests to Keycloak (optional). - A working NGINX proxy for forwarding requests to Keycloak (optional).
## Variables ⚙️
### Main Variables
Defined in `vars/main.yml`:
| Variable | Description |
|---------------------------------|------------------------------------------------------------------|
| `application_id` | Name of the Docker Compose project. Default: `keycloak`. |
| `database_type` | Type of the database. Default: `postgres`. |
| `database_password` | Password for the PostgreSQL database user. |
### Additional Variables (Templates)
| Variable | Description |
|---------------------------------|------------------------------------------------------------------|
| `applications.keycloak.version` | Version of the Keycloak image. |
| `domain` | Domain where Keycloak will be accessible. |
| `applications.keycloak.administrator_username` | Admin username for Keycloak. |
| `keycloak_administrator_password` | Admin password for Keycloak. |
| `database_host` | Host of the PostgreSQL database. |
| `database_name` | Name of the PostgreSQL database. |
| `database_username` | Username for the PostgreSQL database. |
| `http_port` | Port where Keycloak will be accessible (default: `8080`). |
| `docker_restart_policy` | Docker restart policy (e.g., `always`, `unless-stopped`). |
## Tasks 🛠️ ## Tasks 🛠️
The role performs the following main tasks: The role performs the following main tasks:

View File

@ -2,7 +2,7 @@ KC_HOSTNAME= https://{{domain}}
KC_HTTP_ENABLED= true KC_HTTP_ENABLED= true
KC_HEALTH_ENABLED= true KC_HEALTH_ENABLED= true
KEYCLOAK_ADMIN= "{{applications.keycloak.administrator_username}}" KEYCLOAK_ADMIN= "{{applications.keycloak.administrator_username}}"
KEYCLOAK_ADMIN_PASSWORD= "{{keycloak_administrator_password}}" KEYCLOAK_ADMIN_PASSWORD= "{{applications.keycloak.administrator_password}}"
KC_DB= postgres KC_DB= postgres
KC_DB_URL= jdbc:postgresql://{{database_host}}/{{database_name}} KC_DB_URL= jdbc:postgresql://{{database_host}}/{{database_name}}
KC_DB_USERNAME= {{database_username}} KC_DB_USERNAME= {{database_username}}

View File

@ -1,4 +1,4 @@
application_id: "keycloak" application_id: "keycloak"
database_type: "postgres" database_type: "postgres"
database_password: "{{keycloak_database_password}}" database_password: "{{applications.keycloak.database_password}}"
ldap_enabled: True ldap_enabled: True

View File

@ -1,6 +1,7 @@
- name: "include task certbot-and-globals.yml" - name: "include role receive certbot certificate"
include_tasks: certbot-and-globals.yml include_role:
name: nginx-https-recieve-certificate
- name: configure {{domain}}.conf - name: configure {{domain}}.conf
template: template:
src: "mastodon.conf.j2" src: "mastodon.conf.j2"

View File

@ -3,8 +3,9 @@
include_role: include_role:
name: docker-central-database name: docker-central-database
- name: "include task certbot-and-globals.yml" - name: "include role receive certbot certificate"
include_tasks: certbot-and-globals.yml include_role:
name: nginx-https-recieve-certificate
vars: vars:
domain: "{{domains.matrix_synapse}}" domain: "{{domains.matrix_synapse}}"
http_port: "{{ports.localhost.http.matrix_synapse}}" http_port: "{{ports.localhost.http.matrix_synapse}}"

View File

@ -3,8 +3,9 @@
include_role: include_role:
name: docker-central-database name: docker-central-database
- name: "include task certbot-and-globals.yml" - name: "include role receive certbot certificate"
include_tasks: certbot-and-globals.yml include_role:
name: nginx-https-recieve-certificate
- name: create nextcloud nginx proxy configuration file - name: create nextcloud nginx proxy configuration file
template: template:

View File

@ -1,5 +1,6 @@
- name: "include task certbot-and-globals.yml" - name: "include role receive certbot certificate"
include_tasks: certbot-and-globals.yml include_role:
name: nginx-https-recieve-certificate
- name: configure {{domain}}.conf - name: configure {{domain}}.conf
template: template:

View File

@ -16,6 +16,11 @@
group: administrator group: administrator
when: run_once_docker is not defined when: run_once_docker is not defined
- name: Set docker_enabled to true, to activate system-storage-optimizer
set_fact:
docker_enabled: true
when: run_once_docker is not defined
- name: flush docker service - name: flush docker service
meta: flush_handlers meta: flush_handlers
when: run_once_docker is not defined when: run_once_docker is not defined

View File

@ -1,6 +1,7 @@
--- ---
- name: "include task receive certbot certificate" - name: "include task receive certbot certificate"
include_tasks: recieve-certbot-certificate.yml include_role:
name: nginx-https-recieve-certificate
vars: vars:
domain: "{{item.source}}" domain: "{{item.source}}"
loop: "{{domain_mappings}}" loop: "{{domain_mappings}}"

View File

@ -1,19 +1,66 @@
def adjust_color(hex_color, amount): import colorsys
# Remove the leading '#' if present
def adjust_color(hex_color, lightness_change=0, hue_shift=0, saturation_change=0):
"""
Adjust a HEX color in HSL space.
@See https://chatgpt.com/c/67b08ad4-eb58-800f-80cc-f1b22d8c64f3
- lightness_change: Percentage points to add or subtract from lightness (0-100 => 0-1 in HSL).
- hue_shift: Degrees to shift hue (e.g., +180 for complementary).
- saturation_change: Percentage points to add or subtract from saturation (0-100 => 0-1 in HSL).
Uses a 'cyclical' approach for lightness and saturation:
If the new value goes above 1, it wraps around (subtract 1).
If it goes below 0, it wraps around (add 1).
This creates strong contrast when crossing boundaries.
"""
# Strip leading '#' if present
hex_color = hex_color.lstrip('#') hex_color = hex_color.lstrip('#')
# Extract the RGB components # Parse the original RGB values
r = int(hex_color[0:2], 16) r = int(hex_color[0:2], 16)
g = int(hex_color[2:4], 16) g = int(hex_color[2:4], 16)
b = int(hex_color[4:6], 16) b = int(hex_color[4:6], 16)
# Adjust the values, ensuring they remain within the range 0-255 # Convert from [0..255] range to [0..1] for colorsys
r = max(0, min(255, r + amount)) r /= 255.0
g = max(0, min(255, g + amount)) g /= 255.0
b = max(0, min(255, b + amount)) b /= 255.0
# Convert the values back into a hexadecimal string # Convert RGB -> HLS (note: in Python, it's Hue, Lightness, Saturation)
return '#{0:02x}{1:02x}{2:02x}'.format(r, g, b) # Hue, Lightness, Saturation are each in [0..1]
h, l, s = colorsys.rgb_to_hls(r, g, b)
# Shift hue by (hue_shift / 360)
# e.g., hue_shift=180 => shift by 0.5 in HLS space
h = (h + (hue_shift / 360.0)) % 1.0
# Shift saturation (cyc wrap)
# saturation_change is e.g. +20 => +0.20 in HLS
s_new = s + (saturation_change / 100.0)
if s_new > 1:
s_new -= 1
elif s_new < 0:
s_new += 1
# Shift lightness (cyc wrap)
# lightness_change is e.g. +30 => +0.30 in HLS
l_new = l + (lightness_change / 100.0)
if l_new > 1:
l_new -= 1
elif l_new < 0:
l_new += 1
# Convert back to RGB
new_r, new_g, new_b = colorsys.hls_to_rgb(h, l_new, s_new)
# Scale back to [0..255]
new_r = int(new_r * 255)
new_g = int(new_g * 255)
new_b = int(new_b * 255)
return '#{:02x}{:02x}{:02x}'.format(new_r, new_g, new_b)
class FilterModule(object): class FilterModule(object):
'''Custom filters for Ansible''' '''Custom filters for Ansible'''

View File

@ -7,25 +7,42 @@ HINT: Better overwritte CSS variables instead of individual elements.
*/ */
:root { :root {
--primary-color: {{ global_theming.css.colors.primary }}; /** Derived Colors from the Base Color **/
--secondary-color: {{ global_theming.css.colors.secondary }};
--brightest-color: {{ global_theming.css.colors.button_text }}; /* Primary Color: the base color itself */
--bright-color: {{ global_theming.css.colors.background }}; --primary-color: {{ global_theming.css.colors.base }};
--dark-color: {{ global_theming.css.colors.background_dark }};
/* Secondary Color: slightly lightened */
--secondary-color: {{ global_theming.css.colors.base | adjust_color(15) }};
/* Complementary Color: moderately lightened to fall within a mid-brightness range */
--complementary-color: {{ global_theming.css.colors.base | adjust_color(30) }};
/* Bright Background: significantly lightened */
--bright-color: {{ global_theming.css.colors.base | adjust_color(45) }};
/* Brightest Tone (e.g., for button text or accents): nearly white */
--brightest-color: {{ global_theming.css.colors.base | adjust_color(60) }};
/* Dark Background: a darker variant of the base color */
--dark-color: {{ global_theming.css.colors.base | adjust_color(-30) }};
/* Border Color: slightly offset with a light adjustment */
--border-color: {{ global_theming.css.colors.base | adjust_color(10) }};
/* Button Background: a gentle lightening for soft contrast */
--button-bg-color: {{ global_theming.css.colors.base | adjust_color(20) }};
/** Special Action Colors **/
--success-color: {{ global_theming.css.colors.success }}; --success-color: {{ global_theming.css.colors.success }};
--warning-color: {{ global_theming.css.colors.warning }}; --warning-color: {{ global_theming.css.colors.warning }};
--error-color: {{ global_theming.css.colors.error }}; --error-color: {{ global_theming.css.colors.error }};
--info-color: {{ global_theming.css.colors.info }}; --info-color: {{ global_theming.css.colors.info }};
--shadow-color: {{ global_theming.css.colors.shadow }};
--border-color: {{ global_theming.css.colors.border }};
/* New variables for cards and buttons */
--card-bg-color: {{ global_theming.css.colors.card_bg_color }};
--large-shadow: {{ global_theming.css.colors.large_shadow }};
--button-bg-color: {{ global_theming.css.colors.button_bg_color }};
--small-shadow: {{ global_theming.css.colors.small_shadow }};
} }
/* Bootstrap Overrides (Color/Shadow Variables Only) */ /* Bootstrap Overrides (Color/Shadow Variables Only) */
:root { :root {
--bs-primary: var(--primary-color); --bs-primary: var(--primary-color);
@ -42,11 +59,11 @@ HINT: Better overwritte CSS variables instead of individual elements.
/** Keycloak Overrides **/ /** Keycloak Overrides **/
:root{ :root{
--pf-v5-global--Color--100: {{global_theming.css.colors.background_dark | adjust_color(10) }}; --pf-v5-global--Color--100: {{ global_theming.css.colors.base | adjust_color(-30) }};
--pf-v5-global--Color--200: {{global_theming.css.colors.background_dark | adjust_color(30) }}; --pf-v5-global--Color--200: {{ global_theming.css.colors.base | adjust_color(-10) }};
--pf-v5-global--Color--light-100: {{global_theming.css.colors.background_dark | adjust_color(0) }}; --pf-v5-global--Color--light-100: {{ global_theming.css.colors.base | adjust_color(-30) }};
--pf-v5-global--Color--light-200: {{global_theming.css.colors.background_dark | adjust_color(20) }}; --pf-v5-global--Color--light-200: {{ global_theming.css.colors.base | adjust_color(-10) }};
--pf-v5-global--Color--light-300: {{global_theming.css.colors.background_dark | adjust_color(40) }}; --pf-v5-global--Color--light-300: {{ global_theming.css.colors.base | adjust_color(10) }};
} }
/** Mastodon Overrides **/ /** Mastodon Overrides **/
@ -67,11 +84,18 @@ HINT: Better overwritte CSS variables instead of individual elements.
--color-primary-element-light: var(--secondary-color); --color-primary-element-light: var(--secondary-color);
} }
/* Peertube Overrides */ /** Peertube **/
:root { :root {
--mainColor: var(--primary-color); --mainColor: var(--primary-color);
} }
/** Pixelfed **/
:root {
--card-bg: var(--complementary-color);
--light-gray: var(--complementary-color);
}
/* Global Defaults (Colors Only) */ /* Global Defaults (Colors Only) */
body { body {
background-color: var(--bright-color) !important; background-color: var(--bright-color) !important;
@ -125,7 +149,6 @@ input, textarea, select {
} }
input:focus, textarea:focus, select:focus { input:focus, textarea:focus, select:focus {
border-color: var(--primary-color) !important; border-color: var(--primary-color) !important;
/** box-shadow: 0 0 5px var(--shadow-color);**/
} }
/* Navigation (Background and Text Colors) */ /* Navigation (Background and Text Colors) */
@ -159,8 +182,7 @@ thead {
/* Cards / Containers (Background, Border, and Shadow) /* Cards / Containers (Background, Border, and Shadow)
Cards now use a slightly lighter background and a bold, clear shadow */ Cards now use a slightly lighter background and a bold, clear shadow */
.card { .card {
background-color: var(--card-bg-color) !important; background-color: var(--complementary-color) !important;
/** box-shadow: var(--large-shadow) !important;**/
border-color: var(--border-color) !important; border-color: var(--border-color) !important;
} }
@ -195,7 +217,7 @@ button.icon-button {
/** Keycloak **/ /** Keycloak **/
body#keycloak-bg main{ body#keycloak-bg main{
background-color: var(--card-bg-color) !important; background-color: var(--complementary-color) !important;
} }
div#app header, div#app header *{ div#app header, div#app header *{
@ -204,7 +226,7 @@ div#app header, div#app header *{
} }
div#app div#page-sidebar, div#app main#kc-main-content-page-container{ div#app div#page-sidebar, div#app main#kc-main-content-page-container{
background-color: var(--card-bg-color) !important; background-color: var(--complementary-color) !important;
} }
div#app main#kc-main-content-page-container section, div#app main#kc-main-content-page-container section,

View File

@ -0,0 +1,39 @@
# Let's Encrypt Certificate Role
This Ansible role uses Certbot to obtain Let's Encrypt SSL/TLS certificates. It supports both dedicated and wildcard certificate requests based on domain conditions. It can also clean up (delete) dedicated certificates when cleanup mode is enabled.
## Features
- **Dedicated Certificate Request:**
Requests a certificate for a given domain using Certbot's `certonly` command with the webroot plugin.
- **Wildcard Certificate Request:**
When enabled, obtains a wildcard certificate for the primary domain (including both the primary domain and all its direct subdomains).
- **Certificate Cleanup:**
Provides an option to delete dedicated certificates if cleanup mode is active.
- **Run Once for Wildcard:**
Ensures that the wildcard certificate task runs only once to prevent duplicate requests.
## Tasks Overview
- **Receive Dedicated Certificate:**
Executes Certbot to request a dedicated certificate for `{{ domain }}` when a wildcard certificate is not applicable.
- **Receive Wildcard Certificate:**
Executes Certbot to request a wildcard certificate for `*{{ primary_domain }}` under the appropriate conditions.
- **Cleanup Dedicated Certificate:**
Runs Certbot's delete command to remove the dedicated certificate if cleanup mode is active.
- **Run Once Flag:**
Sets a fact to ensure that the wildcard certificate task is executed only once per playbook run.
## Author
This role is authored by [Kevin Veen-Birkenbach](https://www.veen.world).
---
Feel free to contribute or open issues if you have suggestions or encounter any problems with the role. Enjoy secure connections with Let's Encrypt and Ansible!

View File

@ -0,0 +1,2 @@
dependencies:
- nginx-https

View File

@ -1,2 +1,3 @@
dependencies: dependencies:
- health-nginx - health-nginx
- nginx-global

View File

@ -33,11 +33,15 @@
notify: restart nginx notify: restart nginx
when: run_once_nginx is not defined when: run_once_nginx is not defined
- name: "include task certbot-and-globals.yml" # Activated due to the reason that certificate management should be part of role nginx-https
include_tasks: certbot-and-globals.yml # I don't know why this is activated here.
vars: # Propably solved on 2025-02-15 . Please remove latest on 2025-12-31 if no errors appear or earlier
domain: "{{primary_domain}}" #
when: run_once_nginx is not defined #- name: "include task certbot-and-globals.yml"
# include_tasks: certbot-and-globals.yml
# vars:
# domain: "{{primary_domain}}"
# when: run_once_nginx is not defined
- name: flush nginx service - name: flush nginx service
meta: flush_handlers meta: flush_handlers

View File

@ -1,6 +0,0 @@
- name: "include task receive certbot certificate"
include_tasks: recieve-certbot-certificate.yml
- name: Load global Nginx Configuration
include_role:
name: nginx-global

View File

@ -1,5 +1,6 @@
- name: "include task certbot-and-globals.yml" - name: "include role receive certbot certificate"
include_tasks: certbot-and-globals.yml include_role:
name: nginx-https-recieve-certificate
- name: "include task create-domain-conf.yml" - name: "include task create-domain-conf.yml"
include_tasks: create-domain-conf.yml include_tasks: create-domain-conf.yml

View File

@ -1,5 +1,6 @@
- name: "include task certbot-and-globals.yml" - name: "include role receive certbot certificate"
include_tasks: certbot-and-globals.yml include_role:
name: nginx-https-recieve-certificate
- name: "include task create-domain-conf.yml" - name: "include task create-domain-conf.yml"
include_tasks: create-domain-conf.yml include_tasks: create-domain-conf.yml