mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-06-25 19:55:31 +02:00
Compare commits
No commits in common. "0ac9dac65845fd2c3c8d4d2729d32f0f1e1af367" and "4ebe7ee9184370d7fc260a7969ebb824b82b7ec9" have entirely different histories.
0ac9dac658
...
4ebe7ee918
2
filter_plugins/TODO.md
Normal file
2
filter_plugins/TODO.md
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Todos
|
||||||
|
- Refactor all 4 functions to one
|
@ -1,37 +0,0 @@
|
|||||||
# roles/<your-role>/filter_plugins/redirect_filters.py
|
|
||||||
from ansible.errors import AnsibleFilterError
|
|
||||||
|
|
||||||
class FilterModule(object):
|
|
||||||
"""
|
|
||||||
Custom filters for redirect domain mappings
|
|
||||||
"""
|
|
||||||
|
|
||||||
def filters(self):
|
|
||||||
return {
|
|
||||||
"add_redirect_if_group": self.add_redirect_if_group,
|
|
||||||
}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def add_redirect_if_group(redirect_list, group, source, target, group_names):
|
|
||||||
"""
|
|
||||||
Append {"source": source, "target": target} to *redirect_list*
|
|
||||||
**only** if *group* is contained in *group_names*.
|
|
||||||
|
|
||||||
Usage in Jinja:
|
|
||||||
{{ redirect_list
|
|
||||||
| add_redirect_if_group('lam',
|
|
||||||
'ldap.' ~ primary_domain,
|
|
||||||
domains.lam,
|
|
||||||
group_names) }}
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
# Make a copy so we don’t mutate the original list in place
|
|
||||||
redirects = list(redirect_list)
|
|
||||||
|
|
||||||
if group in group_names:
|
|
||||||
redirects.append({"source": source, "target": target})
|
|
||||||
|
|
||||||
return redirects
|
|
||||||
|
|
||||||
except Exception as exc:
|
|
||||||
raise AnsibleFilterError(f"add_redirect_if_group failed: {exc}")
|
|
@ -53,31 +53,31 @@ defaults_domains:
|
|||||||
- "blog.{{primary_domain}}"
|
- "blog.{{primary_domain}}"
|
||||||
|
|
||||||
## Domain Redirects
|
## Domain Redirects
|
||||||
defaults_redirect_domain_mappings: >-
|
defaults_redirect_domain_mappings:
|
||||||
{{ []
|
- { source: "akaunting.{{primary_domain}}", target: "{{domains.akaunting}}" }
|
||||||
| add_redirect_if_group('akaunting', "akaunting." ~ primary_domain, domains.akaunting, group_names)
|
- { source: "bbb.{{primary_domain}}", target: "{{domains.bigbluebutton}}" }
|
||||||
| add_redirect_if_group('bigbluebutton', "bbb." ~ primary_domain, domains.bigbluebutton, group_names)
|
- { source: "discourse.{{primary_domain}}", target: "{{domains.discourse}}" }
|
||||||
| add_redirect_if_group('discourse', "discourse." ~ primary_domain, domains.discourse, group_names)
|
- { source: "crm.{{primary_domain}}", target: "{{domains.espocrm}}" }
|
||||||
| add_redirect_if_group('espocrm', "crm." ~ primary_domain, domains.espocrm, group_names)
|
- { source: "funkwhale.{{primary_domain}}", target: "{{domains.funkwhale}}" }
|
||||||
| add_redirect_if_group('funkwhale', "funkwhale." ~ primary_domain, domains.funkwhale, group_names)
|
- { source: "gitea.{{primary_domain}}", target: "{{domains.gitea}}" }
|
||||||
| add_redirect_if_group('gitea', "gitea." ~ primary_domain, domains.gitea, group_names)
|
- { source: "keycloak.{{primary_domain}}", target: "{{domains.keycloak}}" }
|
||||||
| add_redirect_if_group('keycloak', "keycloak." ~ primary_domain, domains.keycloak, group_names)
|
- {
|
||||||
| add_redirect_if_group('lam', domains.ldap, domains.lam, group_names)
|
source: "{{ domains.ldap }}",
|
||||||
| add_redirect_if_group('phpmyldapadmin', domains.ldap, domains.phpmyldap, group_names)
|
target: "{% if 'lam' in group_names %}{{ domains.lam }}{% elif 'phpmyldapadmin' in group_names %}{{ domains.phpmyldap }}{% else %}{{ primary_domain }}{% endif %}"
|
||||||
| add_redirect_if_group('listmonk', "listmonk." ~ primary_domain, domains.listmonk, group_names)
|
}
|
||||||
| add_redirect_if_group('mailu', "mailu." ~ primary_domain, domains.mailu, group_names)
|
- { source: "listmonk.{{primary_domain}}", target: "{{domains.listmonk}}" }
|
||||||
| add_redirect_if_group('moodle', "moodle." ~ primary_domain, domains.moodle, group_names)
|
- { source: "mailu.{{primary_domain}}", target: "{{domains.mailu}}" }
|
||||||
| add_redirect_if_group('nextcloud', "nextcloud." ~ primary_domain, domains.nextcloud, group_names)
|
- { source: "moodle.{{primary_domain}}", target: "{{domains.moodle}}" }
|
||||||
| add_redirect_if_group('openproject', "openproject." ~ primary_domain, domains.openproject, group_names)
|
- { source: "nextcloud.{{primary_domain}}", target: "{{domains.nextcloud}}" }
|
||||||
| add_redirect_if_group('peertube', "peertube." ~ primary_domain, domains.peertube, group_names)
|
- { source: "openproject.{{primary_domain}}", target: "{{domains.openproject}}" }
|
||||||
| add_redirect_if_group('pixelfed', "pictures." ~ primary_domain, domains.pixelfed, group_names)
|
- { source: "peertube.{{primary_domain}}", target: "{{domains.peertube}}" }
|
||||||
| add_redirect_if_group('pixelfed', "pixelfed." ~ primary_domain, domains.pixelfed, group_names)
|
- { source: "pictures.{{primary_domain}}", target: "{{domains.pixelfed}}" }
|
||||||
| add_redirect_if_group('yourls', "short." ~ primary_domain, domains.yourls, group_names)
|
- { source: "pixelfed.{{primary_domain}}", target: "{{domains.pixelfed}}" }
|
||||||
| add_redirect_if_group('snipe-it', "snipe-it." ~ primary_domain, domains.snipe_it, group_names)
|
- { source: "short.{{primary_domain}}", target: "{{domains.yourls}}" }
|
||||||
| add_redirect_if_group('taiga', "taiga." ~ primary_domain, domains.taiga, group_names)
|
- { source: "snipe-it.{{primary_domain}}", target: "{{domains.snipe_it}}" }
|
||||||
| add_redirect_if_group('peertube', "videos." ~ primary_domain, domains.peertube, group_names)
|
- { source: "taiga.{{primary_domain}}", target: "{{domains.taiga}}" }
|
||||||
| add_redirect_if_group('wordpress', "wordpress." ~ primary_domain, domains.wordpress[0], group_names)
|
- { source: "videos.{{primary_domain}}", target: "{{domains.peertube}}" }
|
||||||
}}
|
- { source: "wordpress.{{primary_domain}}", target: "{{domains.wordpress[0]}}" }
|
||||||
|
|
||||||
# Domains which are deprecated and should be cleaned up
|
# Domains which are deprecated and should be cleaned up
|
||||||
deprecated_domains: []
|
deprecated_domains: []
|
@ -1,14 +1,14 @@
|
|||||||
---
|
---
|
||||||
- name: "stop and remove discourse container if it exist"
|
- name: "stop and remove discourse container if it exist"
|
||||||
docker_container:
|
docker_container:
|
||||||
name: "{{applications[application_id].container}}"
|
name: "{{applications.discourse.container}}"
|
||||||
state: absent
|
state: absent
|
||||||
register: container_action
|
register: container_action
|
||||||
failed_when: container_action.failed and 'No such container' not in container_action.msg
|
failed_when: container_action.failed and 'No such container' not in container_action.msg
|
||||||
listen: recreate discourse
|
listen: recreate discourse
|
||||||
|
|
||||||
- name: "add central database temporary to {{application_id}}_default"
|
- name: "add central database temporary to {{application_id}}_default"
|
||||||
command: docker network connect {{applications[application_id].network}} central-{{ database_type }}
|
command: docker network connect {{applications.discourse.network}} central-{{ database_type }}
|
||||||
failed_when: >
|
failed_when: >
|
||||||
result.rc != 0 and
|
result.rc != 0 and
|
||||||
'already exists in network' not in result.stderr
|
'already exists in network' not in result.stderr
|
||||||
@ -18,6 +18,6 @@
|
|||||||
|
|
||||||
- name: rebuild discourse
|
- name: rebuild discourse
|
||||||
command:
|
command:
|
||||||
cmd: "./launcher rebuild {{applications[application_id].container}}"
|
cmd: "./launcher rebuild {{applications.discourse.container}}"
|
||||||
chdir: "{{docker_repository_directory }}"
|
chdir: "{{docker_repository_directory }}"
|
||||||
listen: recreate discourse
|
listen: recreate discourse
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
- name: "cleanup central database from {{application_id}}_default network"
|
- name: "cleanup central database from {{application_id}}_default network"
|
||||||
command:
|
command:
|
||||||
cmd: "docker network disconnect {{applications[application_id].network}} central-{{ database_type }}"
|
cmd: "docker network disconnect {{applications.discourse.network}} central-{{ database_type }}"
|
||||||
ignore_errors: true
|
ignore_errors: true
|
||||||
when:
|
when:
|
||||||
- mode_reset | bool
|
- mode_reset | bool
|
||||||
@ -34,7 +34,7 @@
|
|||||||
dest: "{{docker_compose.directories.instance}}docker-compose.yml"
|
dest: "{{docker_compose.directories.instance}}docker-compose.yml"
|
||||||
notify:
|
notify:
|
||||||
- docker compose project setup
|
- docker compose project setup
|
||||||
when: run_once_docker_discourse is not defined
|
- run_once_docker_discourse is not defined
|
||||||
|
|
||||||
- name: flush, to recreate discourse docker compose
|
- name: flush, to recreate discourse docker compose
|
||||||
meta: flush_handlers
|
meta: flush_handlers
|
||||||
@ -78,9 +78,9 @@
|
|||||||
meta: flush_handlers
|
meta: flush_handlers
|
||||||
when: run_once_docker_discourse is not defined
|
when: run_once_docker_discourse is not defined
|
||||||
|
|
||||||
- name: "add {{applications[application_id].container}} to network central_postgres"
|
- name: "add {{applications.discourse.container}} to network central_postgres"
|
||||||
command:
|
command:
|
||||||
cmd: "docker network connect central_postgres {{applications[application_id].container}}"
|
cmd: "docker network connect central_postgres {{applications.discourse.container}}"
|
||||||
ignore_errors: true
|
ignore_errors: true
|
||||||
when:
|
when:
|
||||||
- applications | is_feature_enabled('central_database',application_id)
|
- applications | is_feature_enabled('central_database',application_id)
|
||||||
@ -88,7 +88,7 @@
|
|||||||
|
|
||||||
- name: "remove central database from {{application_id}}_default"
|
- name: "remove central database from {{application_id}}_default"
|
||||||
command:
|
command:
|
||||||
cmd: "docker network disconnect {{applications[application_id].network}} central-{{ database_type }}"
|
cmd: "docker network disconnect {{applications.discourse.network}} central-{{ database_type }}"
|
||||||
ignore_errors: true
|
ignore_errors: true
|
||||||
when:
|
when:
|
||||||
- applications | is_feature_enabled('central_database',application_id)
|
- applications | is_feature_enabled('central_database',application_id)
|
||||||
|
@ -143,4 +143,4 @@ run:
|
|||||||
|
|
||||||
docker_args:
|
docker_args:
|
||||||
- --network={{application_id}}_default
|
- --network={{application_id}}_default
|
||||||
- --name={{applications[application_id].container}}
|
- --name={{applications.discourse.container}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
application_id: "discourse"
|
application_id: "discourse"
|
||||||
database_password: "{{ applications[application_id].credentials.database_password }}"
|
database_password: "{{ applications.discourse.credentials.database_password }}"
|
||||||
database_type: "postgres"
|
database_type: "postgres"
|
||||||
docker_repository_directory : "{{docker_compose.directories.services}}{{applications[application_id].repository}}/"
|
docker_repository_directory : "{{docker_compose.directories.services}}{{applications.discourse.repository}}/"
|
||||||
discourse_application_yml_destination: "{{docker_repository_directory }}containers/{{applications[application_id].container}}.yml"
|
discourse_application_yml_destination: "{{docker_repository_directory }}containers/{{applications.discourse.container}}.yml"
|
@ -1,3 +0,0 @@
|
|||||||
# Todos
|
|
||||||
- Optimize buffering
|
|
||||||
- Optimize caching
|
|
@ -1,8 +1,8 @@
|
|||||||
{# Create a namespace to hold the accumulated CSP parts #}
|
{# Initialize an array to collect each CSP directive line #}
|
||||||
{% set ns = namespace(csp_parts=[]) %}
|
{%- set csp_parts = [] %}
|
||||||
|
|
||||||
{# List of directives to build dynamically (except img-src) #}
|
{# List of all directives to process dynamically (except img-src) #}
|
||||||
{% set directives = [
|
{%- set directives = [
|
||||||
'default-src',
|
'default-src',
|
||||||
'connect-src',
|
'connect-src',
|
||||||
'frame-ancestors',
|
'frame-ancestors',
|
||||||
@ -10,36 +10,36 @@
|
|||||||
'script-src',
|
'script-src',
|
||||||
'style-src',
|
'style-src',
|
||||||
'font-src'
|
'font-src'
|
||||||
] %}
|
] %}
|
||||||
|
|
||||||
{# Build each directive line #}
|
{# Loop over each directive and build its value from 'self', any unsafe flags, whitelist URLs, and optional Matomo #}
|
||||||
{% for directive in directives %}
|
{%- for directive in directives %}
|
||||||
{# Always start with 'self' #}
|
{# Start with the 'self' source #}
|
||||||
{% set tokens = ["'self'"] %}
|
{%- set tokens = ["'self'"] %}
|
||||||
|
|
||||||
{# Add any unsafe flags for this directive #}
|
{# Add any unsafe flags (unsafe-eval, unsafe-inline) from csp.flags.<directive> #}
|
||||||
{% for flag in applications | get_csp_flags(application_id, directive) %}
|
{%- for flag in applications | get_csp_flags(application_id, directive) %}
|
||||||
{% set tokens = tokens + [flag] %}
|
{%- set tokens = tokens + [flag] %}
|
||||||
{% endfor %}
|
{%- endfor %}
|
||||||
|
|
||||||
{# If Matomo is enabled, allow its script and connect endpoints #}
|
{# If Matomo feature is enabled, whitelist its script and connect sources #}
|
||||||
{% if applications | is_feature_enabled('matomo', application_id)
|
{%- if applications | is_feature_enabled('matomo', application_id) and directive in ['script-src','connect-src'] %}
|
||||||
and directive in ['script-src', 'connect-src'] %}
|
{%- set tokens = tokens + ['{{ web_protocol }}://{{ domains.matomo }}'] %}
|
||||||
{% set tokens = tokens + [web_protocol ~ '://' ~ domains.matomo] %}
|
{%- endif %}
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{# Append any extra whitelist URLs for this directive #}
|
{# Add any extra hosts/URLs from csp.whitelist.<directive> #}
|
||||||
{% for url in applications | get_csp_whitelist(application_id, directive) %}
|
{%- for url in applications | get_csp_whitelist(application_id, directive) %}
|
||||||
{% set tokens = tokens + [url] %}
|
{%- set tokens = tokens + [url] %}
|
||||||
{% endfor %}
|
{%- endfor %}
|
||||||
|
|
||||||
{# Store the completed directive line in the namespace #}
|
{# Combine into a single directive line and append to csp_parts #}
|
||||||
{% set ns.csp_parts = ns.csp_parts + [directive ~ ' ' ~ (tokens | join(' ')) ~ ';'] %}
|
{%- set csp_parts = csp_parts + [directive ~ ' ' ~ (tokens | join(' ')) ~ ';'] %}
|
||||||
{% endfor %}
|
{%- endfor %}
|
||||||
|
|
||||||
{# Add the (static) img-src directive #}
|
{# Preserve original img-src directive logic (do not loop) #}
|
||||||
{% set ns.csp_parts = ns.csp_parts + ['img-src * data: blob:;'] %}
|
{%- set img_src = 'img-src * data: blob:' %}
|
||||||
|
{%- set csp_parts = csp_parts + [img_src ~ ';'] %}
|
||||||
|
|
||||||
{# Emit the final header and hide any upstream header #}
|
{# Emit the assembled Content-Security-Policy header and hide any upstream CSP header #}
|
||||||
add_header Content-Security-Policy "{{ ns.csp_parts | join(' ') }}" always;
|
add_header Content-Security-Policy "{{ csp_parts | join(' ') }}" always;
|
||||||
proxy_hide_header Content-Security-Policy;
|
proxy_hide_header Content-Security-Policy;
|
||||||
|
@ -30,4 +30,7 @@ location {{location | default("/")}}
|
|||||||
proxy_send_timeout 900s;
|
proxy_send_timeout 900s;
|
||||||
proxy_read_timeout 900s;
|
proxy_read_timeout 900s;
|
||||||
send_timeout 900s;
|
send_timeout 900s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Load caching
|
||||||
|
{% include 'roles/nginx-docker-reverse-proxy/templates/location/proxy_cache.conf.j2' %}
|
@ -11,9 +11,9 @@
|
|||||||
- name: "set oauth2_proxy_application_id (Needed due to lazzy loading issue)"
|
- name: "set oauth2_proxy_application_id (Needed due to lazzy loading issue)"
|
||||||
set_fact:
|
set_fact:
|
||||||
oauth2_proxy_application_id: "{{ application_id }}"
|
oauth2_proxy_application_id: "{{ application_id }}"
|
||||||
when: applications | is_feature_enabled('oauth2',application_id)
|
when: "{{applications[application_id].get('features', {}).get('oauth2', False)}}"
|
||||||
|
|
||||||
- name: "include the docker-oauth2-proxy role {{domain}}"
|
- name: "include the docker-oauth2-proxy role {{domain}}"
|
||||||
include_role:
|
include_role:
|
||||||
name: docker-oauth2-proxy
|
name: docker-oauth2-proxy
|
||||||
when: applications | is_feature_enabled('oauth2',application_id)
|
when: "{{applications[application_id].get('features', {}).get('oauth2', False)}}"
|
@ -1,53 +0,0 @@
|
|||||||
import os
|
|
||||||
import sys
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../.."))
|
|
||||||
sys.path.insert(0, PROJECT_ROOT)
|
|
||||||
|
|
||||||
from filter_plugins.redirect_filters import FilterModule
|
|
||||||
|
|
||||||
|
|
||||||
class TestAddRedirectIfGroup(unittest.TestCase):
|
|
||||||
"""Unit-tests for the add_redirect_if_group filter."""
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
# Obtain the callable once for reuse
|
|
||||||
self.add_redirect = FilterModule().filters()["add_redirect_if_group"]
|
|
||||||
|
|
||||||
def test_appends_redirect_when_group_present(self):
|
|
||||||
original = [{"source": "a", "target": "b"}]
|
|
||||||
result = self.add_redirect(
|
|
||||||
original,
|
|
||||||
group="lam",
|
|
||||||
source="ldap.example.com",
|
|
||||||
target="lam.example.com",
|
|
||||||
group_names=["lam", "other"],
|
|
||||||
)
|
|
||||||
|
|
||||||
# Original list must stay unchanged
|
|
||||||
self.assertEqual(len(original), 1)
|
|
||||||
# Result list must contain the extra entry
|
|
||||||
self.assertEqual(len(result), 2)
|
|
||||||
self.assertIn(
|
|
||||||
{"source": "ldap.example.com", "target": "lam.example.com"}, result
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_keeps_list_unchanged_when_group_absent(self):
|
|
||||||
original = [{"source": "a", "target": "b"}]
|
|
||||||
result = self.add_redirect(
|
|
||||||
original,
|
|
||||||
group="lam",
|
|
||||||
source="ldap.example.com",
|
|
||||||
target="lam.example.com",
|
|
||||||
group_names=["unrelated"],
|
|
||||||
)
|
|
||||||
|
|
||||||
# No new entries
|
|
||||||
self.assertEqual(result, original)
|
|
||||||
# But ensure a new list object was returned (no in-place mutation)
|
|
||||||
self.assertIsNot(result, original)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
unittest.main()
|
|
Loading…
x
Reference in New Issue
Block a user