mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-08-29 23:08:06 +02:00
Refactor Akaunting role and CSP handling
- Improved CSP filter to properly include web-svc-cdn and use protocol-aware domains - Added Todo.md with redis and OIDC notes - Enhanced Akaunting role config with CSP flags and redis option - Updated schema to include app_key validation - Reworked tasks to handle first-run marker logic cleanly - Fixed docker-compose template (marker, healthcheck, setup flag) - Expanded env.j2 with cache, email, proxy, and redis options - Added javascript.js.j2 template for SSO warning - Introduced structured vars for Akaunting role - Removed deprecated update-repository-with-files.yml task See conversation: https://chatgpt.com/share/68af00df-2c74-800f-90b6-6ac5b29acdcb
This commit is contained in:
@@ -131,14 +131,18 @@ class FilterModule(object):
|
|||||||
flags = self.get_csp_flags(applications, application_id, directive)
|
flags = self.get_csp_flags(applications, application_id, directive)
|
||||||
tokens += flags
|
tokens += flags
|
||||||
|
|
||||||
# Matomo integration
|
|
||||||
if (
|
if directive in ['script-src-elem', 'connect-src']:
|
||||||
self.is_feature_enabled(applications, matomo_feature_name, application_id)
|
# Matomo integration
|
||||||
and directive in ['script-src-elem', 'connect-src']
|
if self.is_feature_enabled(applications, matomo_feature_name, application_id):
|
||||||
):
|
matomo_domain = domains.get('web-app-matomo')[0]
|
||||||
matomo_domain = domains.get('web-app-matomo')[0]
|
if matomo_domain:
|
||||||
if matomo_domain:
|
tokens.append(f"{web_protocol}://{matomo_domain}")
|
||||||
tokens.append(f"{web_protocol}://{matomo_domain}")
|
|
||||||
|
# Allow the loading of js from the cdn
|
||||||
|
if self.is_feature_enabled(applications, 'logout', application_id) or self.is_feature_enabled(applications, 'desktop', application_id):
|
||||||
|
domain = domains.get('web-svc-cdn')[0]
|
||||||
|
tokens.append(f"{web_protocol}://{domain}")
|
||||||
|
|
||||||
# ReCaptcha integration: allow loading scripts from Google if feature enabled
|
# ReCaptcha integration: allow loading scripts from Google if feature enabled
|
||||||
if self.is_feature_enabled(applications, 'recaptcha', application_id):
|
if self.is_feature_enabled(applications, 'recaptcha', application_id):
|
||||||
@@ -146,12 +150,6 @@ class FilterModule(object):
|
|||||||
tokens.append('https://www.gstatic.com')
|
tokens.append('https://www.gstatic.com')
|
||||||
tokens.append('https://www.google.com')
|
tokens.append('https://www.google.com')
|
||||||
|
|
||||||
# Allow the loading of js from the cdn
|
|
||||||
if directive == 'script-src-elem':
|
|
||||||
if self.is_feature_enabled(applications, 'logout', application_id) or self.is_feature_enabled(applications, 'desktop', application_id):
|
|
||||||
domain = domains.get('web-svc-cdn')[0]
|
|
||||||
tokens.append(f"{domain}")
|
|
||||||
|
|
||||||
if directive == 'frame-ancestors':
|
if directive == 'frame-ancestors':
|
||||||
# Enable loading via ancestors
|
# Enable loading via ancestors
|
||||||
if self.is_feature_enabled(applications, 'desktop', application_id):
|
if self.is_feature_enabled(applications, 'desktop', application_id):
|
||||||
@@ -163,11 +161,11 @@ class FilterModule(object):
|
|||||||
|
|
||||||
# Allow logout via infinito logout proxy
|
# Allow logout via infinito logout proxy
|
||||||
domain = domains.get('web-svc-logout')[0]
|
domain = domains.get('web-svc-logout')[0]
|
||||||
tokens.append(f"{domain}")
|
tokens.append(f"{web_protocol}://{domain}")
|
||||||
|
|
||||||
# Allow logout via keycloak app
|
# Allow logout via keycloak app
|
||||||
domain = domains.get('web-app-keycloak')[0]
|
domain = domains.get('web-app-keycloak')[0]
|
||||||
tokens.append(f"{domain}")
|
tokens.append(f"{web_protocol}://{domain}")
|
||||||
|
|
||||||
# whitelist
|
# whitelist
|
||||||
tokens += self.get_csp_whitelist(applications, application_id, directive)
|
tokens += self.get_csp_whitelist(applications, application_id, directive)
|
||||||
|
3
roles/web-app-akaunting/Todo.md
Normal file
3
roles/web-app-akaunting/Todo.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# To-dos
|
||||||
|
- Enable redis
|
||||||
|
- Enable OIDC
|
@@ -7,11 +7,26 @@ features:
|
|||||||
css: true
|
css: true
|
||||||
desktop: true
|
desktop: true
|
||||||
central_database: true
|
central_database: true
|
||||||
logout: true
|
logout: true
|
||||||
|
javascript: true
|
||||||
server:
|
server:
|
||||||
domains:
|
domains:
|
||||||
canonical:
|
canonical:
|
||||||
- "accounting.{{ PRIMARY_DOMAIN }}"
|
- "accounting.{{ PRIMARY_DOMAIN }}"
|
||||||
|
csp:
|
||||||
|
flags:
|
||||||
|
script-src-elem:
|
||||||
|
unsafe-inline: true
|
||||||
|
script-src:
|
||||||
|
unsafe-inline: true
|
||||||
|
unsafe-eval: true
|
||||||
|
style-src:
|
||||||
|
unsafe-inline: true
|
||||||
|
whitelist:
|
||||||
|
font-src:
|
||||||
|
- "data:"
|
||||||
|
connect-src:
|
||||||
|
- https://akaunting.com
|
||||||
docker:
|
docker:
|
||||||
services:
|
services:
|
||||||
database:
|
database:
|
||||||
@@ -22,6 +37,8 @@ docker:
|
|||||||
image: docker.io/akaunting/akaunting
|
image: docker.io/akaunting/akaunting
|
||||||
version: latest
|
version: latest
|
||||||
name: akaunting
|
name: akaunting
|
||||||
|
redis:
|
||||||
|
enabled: false # Set to true to activate redis for akaunting
|
||||||
volumes:
|
volumes:
|
||||||
data: akaunting_data
|
data: akaunting_data
|
||||||
credentials: {}
|
credentials: {}
|
||||||
|
@@ -2,4 +2,8 @@ credentials:
|
|||||||
setup_admin_password:
|
setup_admin_password:
|
||||||
description: "Initial admin user password for Akaunting"
|
description: "Initial admin user password for Akaunting"
|
||||||
algorithm: "sha256"
|
algorithm: "sha256"
|
||||||
validation: "^[a-f0-9]{64}$"
|
validation: "^[a-f0-9]{64}$"
|
||||||
|
app_key:
|
||||||
|
description: "Laravel application key (base64 encoded 32-byte key, prefixed with 'base64:')"
|
||||||
|
algorithm: "base64_prefixed_32"
|
||||||
|
validation: "^base64:[A-Za-z0-9+/=]{43,}$"
|
@@ -1,18 +1,24 @@
|
|||||||
---
|
---
|
||||||
|
- name: "Akaunting | Check if first run (marker exists?)"
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ AKAUNTING_SETUP_MARKER }}"
|
||||||
|
register: akaunting_marker_stat
|
||||||
|
|
||||||
|
- name: "Akaunting | Decide if setup should be enabled"
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
akaunting_setup_enabled: "{{ not akaunting_marker_stat.stat.exists }}"
|
||||||
|
|
||||||
- name: "For '{{ application_id }}': load docker, db and proxy"
|
- name: "For '{{ application_id }}': load docker, db and proxy"
|
||||||
include_role:
|
include_role:
|
||||||
name: cmp-db-docker-proxy
|
name: cmp-db-docker-proxy
|
||||||
|
|
||||||
- name: "include tasks update-repository-with-files.yml"
|
|
||||||
include_tasks: utils/update-repository-with-files.yml
|
|
||||||
vars:
|
vars:
|
||||||
detached_files:
|
# Forward flag into compose templating
|
||||||
- "docker-compose.yml"
|
cmp_extra_facts:
|
||||||
|
akaunting_setup_enabled: "{{ akaunting_setup_enabled }}"
|
||||||
|
|
||||||
- name: "For '{{ application_id }}': create {{ docker_compose.files.env }}"
|
- name: "Akaunting | Create first-run marker to disable future setup"
|
||||||
template:
|
ansible.builtin.file:
|
||||||
src: "env.j2"
|
path: "{{ AKAUNTING_SETUP_MARKER }}"
|
||||||
dest: "{{ docker_compose.files.env }}"
|
state: touch
|
||||||
mode: "0770"
|
mode: "0644"
|
||||||
force: yes
|
when: akaunting_setup_enabled | bool
|
||||||
notify: docker compose up
|
|
@@ -2,22 +2,26 @@
|
|||||||
application:
|
application:
|
||||||
|
|
||||||
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
||||||
container_name: {{ akaunting_name }}
|
{% set container_port = 80 %}
|
||||||
image: "{{ akaunting_image }}:{{ akaunting_version }}"
|
container_name: {{ AKAUNTING_CONTAINER }}
|
||||||
|
image: "{{ AKAUNTING_IMAGE }}:{{ AKAUNTING_VERSION }}"
|
||||||
build:
|
build:
|
||||||
context: .
|
context: {{ docker_repository_path }}
|
||||||
|
dockerfile: Dockerfile
|
||||||
ports:
|
ports:
|
||||||
- 127.0.0.1:{{ ports.localhost.http[application_id] }}:80
|
- 127.0.0.1:{{ ports.localhost.http[application_id] }}:{{ container_port }}
|
||||||
volumes:
|
volumes:
|
||||||
- data:/var/www/html
|
- data:/var/www/html
|
||||||
|
{% if akaunting_setup_enabled | bool %}
|
||||||
environment:
|
environment:
|
||||||
- AKAUNTING_SETUP
|
- AKAUNTING_SETUP=true
|
||||||
|
{% endif %}
|
||||||
|
{% include 'roles/docker-container/templates/healthcheck/tcp.yml.j2' %}
|
||||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||||
{% include 'roles/docker-container/templates/depends_on/dmbs_excl.yml.j2' %}
|
{% include 'roles/docker-container/templates/depends_on/dmbs_excl.yml.j2' %}
|
||||||
|
|
||||||
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
||||||
data:
|
data:
|
||||||
name: {{ akaunting_volume }}
|
name: {{ AKAUNTING_VOLUME }}
|
||||||
|
|
||||||
|
|
||||||
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
@@ -1,22 +1,55 @@
|
|||||||
# You should change this to match your reverse proxy DNS name and protocol
|
# https://github.com/akaunting/akaunting/blob/master/.env.example
|
||||||
APP_URL={{ domains | get_url(application_id, WEB_PROTOCOL) }}
|
APP_URL={{ AKAUNTING_URL }}
|
||||||
|
|
||||||
|
# Locales
|
||||||
LOCALE={{ HOST_LL }}
|
LOCALE={{ HOST_LL }}
|
||||||
|
TIMEZONE={{ HOST_TIMEZONE }}
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
APP_DEBUG={{ MODE_DEBUG | lower }}
|
||||||
|
APP_ENV={{ ENVIRONMENT }}
|
||||||
|
|
||||||
# Don't change this unless you rename your database container or use rootless podman, in case of using rootless podman you should set it to 127.0.0.1 (NOT localhost)
|
# Don't change this unless you rename your database container or use rootless podman, in case of using rootless podman you should set it to 127.0.0.1 (NOT localhost)
|
||||||
DB_HOST={{ database_host }}
|
DB_HOST={{ database_host }}
|
||||||
|
|
||||||
# Change these to match env/db.env
|
|
||||||
DB_DATABASE={{ database_name }}
|
DB_DATABASE={{ database_name }}
|
||||||
DB_USERNAME={{ database_username }}
|
DB_USERNAME={{ database_username }}
|
||||||
DB_PASSWORD={{ database_password }}
|
DB_PASSWORD={{ database_password }}
|
||||||
|
DB_PORT={{ database_port }}
|
||||||
# You should change this to a random string of three numbers or letters followed by an underscore
|
DB_CONNECTION=mysql
|
||||||
DB_PREFIX=asd_
|
DB_PREFIX=asd_
|
||||||
|
|
||||||
|
# Proxy
|
||||||
|
TRUSTED_PROXIES=*
|
||||||
|
TRUSTED_HEADERS=X_FORWARDED_FOR,X_FORWARDED_HOST,X_FORWARDED_PORT,X_FORWARDED_PROTO
|
||||||
|
|
||||||
# These define the first company to exist on this instance. They are only used during setup.
|
# These define the first company to exist on this instance. They are only used during setup.
|
||||||
COMPANY_NAME={{applications | get_app_conf(application_id, 'company.name', True)}}
|
COMPANY_NAME={{ AKAUNTING_COMPANY_NAME }}
|
||||||
COMPANY_EMAIL={{applications | get_app_conf(application_id, 'company.email', True)}}
|
COMPANY_EMAIL={{ AKAUNTING_COMPANY_EMAIL }}
|
||||||
|
|
||||||
|
# Credentials
|
||||||
|
APP_KEY={{ AKAUNTING_APP_KEY }}
|
||||||
|
|
||||||
# This will be the first administrative user created on setup.
|
# This will be the first administrative user created on setup.
|
||||||
ADMIN_EMAIL={{applications.akaunting.setup_admin_email}}
|
ADMIN_EMAIL={{ AKAUNTING_ADMIN_EMAIL }}
|
||||||
ADMIN_PASSWORD={{applications | get_app_conf(application_id, 'credentials.setup_admin_password', True)}}
|
ADMIN_PASSWORD={{ AKAUNTING_ADMIN_PASSWORD }}
|
||||||
|
|
||||||
|
# Cache
|
||||||
|
CACHE_DRIVER={{ AKAUNTING_CACHE_DRIVER }}
|
||||||
|
SESSION_DRIVER={{ AKAUNTING_CACHE_DRIVER }}
|
||||||
|
QUEUE_CONNECTION={{ 'sync' if AKAUNTING_CACHE_DRIVER == 'file' else AKAUNTING_CACHE_DRIVER }}
|
||||||
|
{% if AKAUNTING_CACHE_DRIVER == 'redis' %}
|
||||||
|
REDIS_CLIENT=phpredis
|
||||||
|
REDIS_HOST=redis
|
||||||
|
REDIS_PASSWORD=null
|
||||||
|
REDIS_PORT=6379
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
# Email
|
||||||
|
MAIL_MAILER={{ 'smtp' if SYSTEM_EMAIL.SMTP else 'sendmail' }}
|
||||||
|
MAIL_HOST={{ SYSTEM_EMAIL.HOST }}
|
||||||
|
MAIL_PORT={{ SYSTEM_EMAIL.PORT }}
|
||||||
|
MAIL_USERNAME={{ users['no-reply'].email }}
|
||||||
|
MAIL_PASSWORD={{ users['no-reply'].mailu_token }}
|
||||||
|
MAIL_ENCRYPTION={{ SYSTEM_EMAIL.TLS | ternary("tls","null") }}
|
||||||
|
MAIL_FROM_ADDRESS={{ AKAUNTING_COMPANY_EMAIL }}
|
||||||
|
MAIL_FROM_NAME={{ AKAUNTING_COMPANY_NAME }}
|
||||||
|
1
roles/web-app-akaunting/templates/javascript.js.j2
Normal file
1
roles/web-app-akaunting/templates/javascript.js.j2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{% include 'templates/roles/web-app/templates/javascripts/sso_warning.js.j2' %}
|
@@ -1,8 +1,28 @@
|
|||||||
application_id: "web-app-akaunting"
|
|
||||||
database_type: "mariadb"
|
# General
|
||||||
database_password: "{{ applications | get_app_conf(application_id, 'credentials.database_password', True) }}"
|
application_id: "web-app-akaunting"
|
||||||
docker_repository_address: "https://github.com/akaunting/docker.git"
|
js_application_name: "Akaunting"
|
||||||
akaunting_version: "{{ applications | get_app_conf(application_id, 'docker.services.akaunting.version', True) }}"
|
|
||||||
akaunting_image: "{{ applications | get_app_conf(application_id, 'docker.services.akaunting.image', True) }}"
|
# Database
|
||||||
akaunting_name: "{{ applications | get_app_conf(application_id, 'docker.services.akaunting.name', True) }}"
|
database_type: "mariadb"
|
||||||
akaunting_volume: "{{ applications | get_app_conf(application_id, 'docker.volumes.data', True) }}"
|
database_password: "{{ applications | get_app_conf(application_id, 'credentials.database_password') }}"
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
docker_repository_address: "https://github.com/akaunting/docker.git"
|
||||||
|
docker_pull_git_repository: true
|
||||||
|
docker_repository_branch: "master"
|
||||||
|
docker_compose_skipp_file_creation: false
|
||||||
|
|
||||||
|
# Akaunting
|
||||||
|
AKAUNTING_URL: "{{ domains | get_url(application_id, WEB_PROTOCOL) }}"
|
||||||
|
AKAUNTING_VERSION: "{{ applications | get_app_conf(application_id, 'docker.services.akaunting.version') }}"
|
||||||
|
AKAUNTING_IMAGE: "{{ applications | get_app_conf(application_id, 'docker.services.akaunting.image') }}"
|
||||||
|
AKAUNTING_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.akaunting.name') }}"
|
||||||
|
AKAUNTING_VOLUME: "{{ applications | get_app_conf(application_id, 'docker.volumes.data') }}"
|
||||||
|
AKAUNTING_COMPANY_NAME: "{{ applications | get_app_conf(application_id, 'company.name') }}"
|
||||||
|
AKAUNTING_COMPANY_EMAIL: "{{ applications | get_app_conf(application_id, 'company.email') }}"
|
||||||
|
AKAUNTING_ADMIN_EMAIL: "{{ applications | get_app_conf(application_id, 'setup_admin_email') }}"
|
||||||
|
AKAUNTING_ADMIN_PASSWORD: "{{ applications | get_app_conf(application_id, 'credentials.setup_admin_password') }}"
|
||||||
|
AKAUNTING_SETUP_MARKER: "/var/lib/docker/volumes/{{ AKAUNTING_VOLUME }}/_data/.akaunting_installed"
|
||||||
|
AKAUNTING_APP_KEY: "{{ applications | get_app_conf(application_id, 'credentials.app_key') }}"
|
||||||
|
AKAUNTING_CACHE_DRIVER: "{{ 'redis' if applications | get_app_conf(application_id, 'docker.services.redis.enabled') else 'file' }}"
|
@@ -1,38 +0,0 @@
|
|||||||
# It isn't best practice to use this task
|
|
||||||
# Better load the repositories into /opt/docker/[servicename]/services, build them there and then use a docker-compose file for customizing
|
|
||||||
# @todo Refactor\Remove
|
|
||||||
# @deprecated
|
|
||||||
- name: "Merge detached_files with applications | get_app_conf('web-app-oauth2-proxy','configuration_file')"
|
|
||||||
set_fact:
|
|
||||||
merged_detached_files: "{{ detached_files + [applications | get_app_conf('web-app-oauth2-proxy','configuration_file')] }}"
|
|
||||||
when: "{{ applications | get_app_conf(application_id,'features.oauth2')"
|
|
||||||
|
|
||||||
- name: "backup detached files"
|
|
||||||
command: >
|
|
||||||
mv "{{ docker_compose.directories.instance }}{{ item }}" "/tmp/{{ application_id }}-{{ item }}.backup"
|
|
||||||
args:
|
|
||||||
removes: "{{ docker_compose.directories.instance }}{{ item }}"
|
|
||||||
become: true
|
|
||||||
loop: "{{ merged_detached_files | default(detached_files) }}"
|
|
||||||
|
|
||||||
- name: checkout repository
|
|
||||||
ansible.builtin.shell: git checkout .
|
|
||||||
become: true
|
|
||||||
args:
|
|
||||||
chdir: "{{ docker_compose.directories.instance }}"
|
|
||||||
ignore_errors: true
|
|
||||||
|
|
||||||
- name: "restore detached files"
|
|
||||||
command: >
|
|
||||||
mv "/tmp/{{ application_id }}-{{ item }}.backup" "{{ docker_compose.directories.instance }}{{ item }}"
|
|
||||||
args:
|
|
||||||
removes: "/tmp/{{ application_id }}-{{ item }}.backup"
|
|
||||||
become: true
|
|
||||||
loop: "{{ merged_detached_files | default(detached_files) }}"
|
|
||||||
|
|
||||||
- name: "copy {{ detached_files }} templates to server"
|
|
||||||
template:
|
|
||||||
src: "{{ item }}.j2"
|
|
||||||
dest: "{{ docker_compose.directories.instance }}{{ item }}"
|
|
||||||
loop: "{{ detached_files }}"
|
|
||||||
notify: docker compose up
|
|
Reference in New Issue
Block a user