zammad-hr setup finished

This commit is contained in:
sascha.koenig 2025-11-18 15:26:59 +01:00
parent 7bbcd5ae4e
commit 07a3d1e6b8
3 changed files with 105 additions and 46 deletions

View File

@ -4,7 +4,6 @@
pkgs, pkgs,
... ...
}: let }: let
# Instance identifier - macht es einfach, später eine zweite Instanz zu erstellen
instanceName = "hr"; instanceName = "hr";
serviceName = "zammad-${instanceName}"; serviceName = "zammad-${instanceName}";
@ -15,32 +14,39 @@
envFileProd = config.age.secrets."${serviceName}-env-prod".path; envFileProd = config.age.secrets."${serviceName}-env-prod".path;
envFileCommon = config.age.secrets."${serviceName}-env".path; envFileCommon = config.age.secrets."${serviceName}-env".path;
# Zammad version
zammadVersion = "6.5.2-22"; zammadVersion = "6.5.2-22";
zammadImage = "ghcr.io/zammad/zammad:${zammadVersion}"; zammadImage = "ghcr.io/zammad/zammad:${zammadVersion}";
# IP-Basis für diese Instanz (HR: .30-.38, später IT: .40-.48)
ipBase = "10.89.0"; ipBase = "10.89.0";
ipOffset = 30; # HR startet bei .30, IT würde bei .40 starten ipOffset = 40;
# Domain-Konfiguration
zammadDomain = "hr-ticket.az-gruppe.com";
# Shared environment variables
sharedEnvironment = { sharedEnvironment = {
MEMCACHE_SERVERS = "${serviceName}-memcached:11211"; MEMCACHE_SERVERS = "zammad-memcached:11211";
POSTGRESQL_DB = "zammad_${instanceName}"; POSTGRESQL_DB = "zammad_${instanceName}";
POSTGRESQL_HOST = "10.89.0.1"; # Host PostgreSQL POSTGRESQL_HOST = "10.89.0.1";
POSTGRESQL_USER = "zammad_${instanceName}"; POSTGRESQL_USER = "zammad_${instanceName}";
POSTGRESQL_PORT = "5432"; POSTGRESQL_PORT = "5432";
POSTGRESQL_OPTIONS = "?pool=50"; POSTGRESQL_OPTIONS = "?pool=50";
REDIS_URL = "redis://${serviceName}-redis:6379"; REDIS_URL = "redis://zammad-redis:6379";
TZ = "Europe/Berlin"; TZ = "Europe/Berlin";
BACKUP_DIR = "/var/tmp/zammad"; BACKUP_DIR = "/var/tmp/zammad";
BACKUP_TIME = "03:00"; BACKUP_TIME = "03:00";
HOLD_DAYS = "10"; HOLD_DAYS = "10";
ELASTICSEARCH_ENABLED = "true"; ELASTICSEARCH_ENABLED = "true";
ELASTICSEARCH_HOST = "${serviceName}-elasticsearch"; ELASTICSEARCH_HOST = "zammad-elasticsearch";
ELASTICSEARCH_PORT = "9200"; ELASTICSEARCH_PORT = "9200";
ELASTICSEARCH_NAMESPACE = "zammad_${instanceName}"; ELASTICSEARCH_NAMESPACE = "zammad_${instanceName}";
NGINX_PORT = "8080"; NGINX_PORT = "8080";
# CSRF & Reverse Proxy Settings
NGINX_SERVER_SCHEME = "https";
NGINX_SERVER_NAME = zammadDomain;
ZAMMAD_HTTP_TYPE = "https";
ZAMMAD_FQDN = zammadDomain;
RAILS_TRUSTED_PROXIES = "['127.0.0.1', '::1', '10.89.0.0/24']";
}; };
in { in {
virtualisation.oci-containers = { virtualisation.oci-containers = {
@ -56,6 +62,7 @@ in {
extraOptions = [ extraOptions = [
"--ip=${ipBase}.${toString ipOffset}" "--ip=${ipBase}.${toString ipOffset}"
"--network=web" "--network=web"
"--network-alias=zammad-elasticsearch"
]; ];
ports = ["127.0.0.1:${toString elasticsearchPort}:9200"]; ports = ["127.0.0.1:${toString elasticsearchPort}:9200"];
}; };
@ -67,6 +74,7 @@ in {
extraOptions = [ extraOptions = [
"--ip=${ipBase}.${toString (ipOffset + 1)}" "--ip=${ipBase}.${toString (ipOffset + 1)}"
"--network=web" "--network=web"
"--network-alias=zammad-memcached"
]; ];
}; };
@ -77,23 +85,7 @@ in {
extraOptions = [ extraOptions = [
"--ip=${ipBase}.${toString (ipOffset + 2)}" "--ip=${ipBase}.${toString (ipOffset + 2)}"
"--network=web" "--network=web"
]; "--network-alias=zammad-redis"
};
containers."${serviceName}-init" = {
image = zammadImage;
autoStart = false;
cmd = ["zammad-init"];
environment = sharedEnvironment;
environmentFiles = [envFileCommon envFileProd];
volumes = ["${serviceName}_storage:/opt/zammad/storage"];
dependsOn = ["${serviceName}-memcached" "${serviceName}-redis"];
extraOptions = [
"--ip=${ipBase}.${toString (ipOffset + 3)}"
"--network=web"
"--add-host=postgres:10.89.0.1"
"--user=0:0"
"--restart=on-failure"
]; ];
}; };
@ -109,6 +101,7 @@ in {
"--ip=${ipBase}.${toString (ipOffset + 4)}" "--ip=${ipBase}.${toString (ipOffset + 4)}"
"--network=web" "--network=web"
"--add-host=postgres:10.89.0.1" "--add-host=postgres:10.89.0.1"
"--network-alias=zammad-railsserver"
]; ];
}; };
@ -139,6 +132,7 @@ in {
"--ip=${ipBase}.${toString (ipOffset + 6)}" "--ip=${ipBase}.${toString (ipOffset + 6)}"
"--network=web" "--network=web"
"--add-host=postgres:10.89.0.1" "--add-host=postgres:10.89.0.1"
"--network-alias=zammad-websocket"
]; ];
}; };
@ -165,7 +159,6 @@ in {
environment = sharedEnvironment; environment = sharedEnvironment;
environmentFiles = [envFileCommon envFileProd]; environmentFiles = [envFileCommon envFileProd];
volumes = [ volumes = [
"${serviceName}_backup:/var/tmp/zammad"
"${serviceName}_storage:/opt/zammad/storage:ro" "${serviceName}_storage:/opt/zammad/storage:ro"
"/var/backup/${serviceName}:/var/tmp/zammad:rw" "/var/backup/${serviceName}:/var/tmp/zammad:rw"
]; ];
@ -179,6 +172,64 @@ in {
}; };
}; };
# Init als oneshot systemd-Service
systemd.services."${serviceName}-init" = {
description = "Zammad ${instanceName} Database Initialization";
after = [
"podman-${serviceName}-memcached.service"
"podman-${serviceName}-redis.service"
"podman-${serviceName}-elasticsearch.service"
];
requires = [
"podman-${serviceName}-memcached.service"
"podman-${serviceName}-redis.service"
];
wantedBy = [];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
User = "root";
Group = "root";
};
script = ''
set -euo pipefail
echo "Starting Zammad ${instanceName} database initialization..."
${pkgs.podman}/bin/podman run --rm \
--name ${serviceName}-init-oneshot \
--network web \
--ip ${ipBase}.${toString (ipOffset + 3)} \
--add-host=postgres:10.89.0.1 \
--user 0:0 \
--env-file ${envFileCommon} \
--env-file ${envFileProd} \
--env MEMCACHE_SERVERS=zammad-memcached:11211 \
--env POSTGRESQL_DB=zammad_${instanceName} \
--env POSTGRESQL_HOST=10.89.0.1 \
--env POSTGRESQL_USER=zammad_${instanceName} \
--env POSTGRESQL_PORT=5432 \
--env POSTGRESQL_OPTIONS='?pool=50' \
--env REDIS_URL=redis://zammad-redis:6379 \
--env TZ=Europe/Berlin \
--env ELASTICSEARCH_ENABLED=true \
--env ELASTICSEARCH_HOST=zammad-elasticsearch \
--env ELASTICSEARCH_PORT=9200 \
--env ELASTICSEARCH_NAMESPACE=zammad_${instanceName} \
--env NGINX_SERVER_SCHEME=https \
--env NGINX_SERVER_NAME=${zammadDomain} \
--env ZAMMAD_HTTP_TYPE=https \
--env ZAMMAD_FQDN=${zammadDomain} \
-v ${serviceName}_storage:/opt/zammad/storage \
${zammadImage} \
zammad-init
echo "Zammad ${instanceName} initialization completed successfully"
'';
};
# Backup retention service # Backup retention service
systemd.services."${serviceName}-backup-cleanup" = { systemd.services."${serviceName}-backup-cleanup" = {
serviceConfig = { serviceConfig = {
@ -194,12 +245,10 @@ in {
echo "Starting ${serviceName} backup cleanup at $(date)" echo "Starting ${serviceName} backup cleanup at $(date)"
# Ensure backup directory exists
mkdir -p "$BACKUP_DIR" mkdir -p "$BACKUP_DIR"
chown root:root "$BACKUP_DIR" chown root:root "$BACKUP_DIR"
chmod 750 "$BACKUP_DIR" chmod 750 "$BACKUP_DIR"
# Remove backups older than HOLD_DAYS
${pkgs.findutils}/bin/find "$BACKUP_DIR" -type f -name "*.gz" -mtime +$HOLD_DAYS -delete ${pkgs.findutils}/bin/find "$BACKUP_DIR" -type f -name "*.gz" -mtime +$HOLD_DAYS -delete
echo "Current backups:" echo "Current backups:"
@ -218,7 +267,7 @@ in {
}; };
}; };
# Traefik configuration # Traefik configuration with proper headers
services.traefik.dynamicConfigOptions.http = { services.traefik.dynamicConfigOptions.http = {
services.${serviceName}.loadBalancer.servers = [ services.${serviceName}.loadBalancer.servers = [
{ {
@ -226,13 +275,23 @@ in {
} }
]; ];
middlewares."${serviceName}-headers".headers = {
customRequestHeaders = {
X-Forwarded-Proto = "https";
X-Forwarded-Port = "443";
X-Forwarded-Host = zammadDomain;
X-Real-IP = "";
};
};
routers.${serviceName} = { routers.${serviceName} = {
rule = "Host(`hr-ticket.az-gruppe.com`)"; # HR-spezifische Domain rule = "Host(`${zammadDomain}`)";
tls = { tls = {
certResolver = "ionos"; certResolver = "ionos";
}; };
service = serviceName; service = serviceName;
entrypoints = "websecure"; entrypoints = "websecure";
middlewares = ["${serviceName}-headers"];
}; };
}; };
} }

View File

@ -115,6 +115,7 @@
host librechat_rag librechat_rag 10.89.0.0/24 scram-sha-256 host librechat_rag librechat_rag 10.89.0.0/24 scram-sha-256
host librechat_rag_dev librechat_rag_dev 10.89.1.0/24 scram-sha-256 host librechat_rag_dev librechat_rag_dev 10.89.1.0/24 scram-sha-256
host zammad_hr zammad_hr 10.89.0.0/24 scram-sha-256 host zammad_hr zammad_hr 10.89.0.0/24 scram-sha-256
host postgres zammad_hr 10.89.0.0/24 scram-sha-256
host litellm litellm 10.89.0.0/24 scram-sha-256 host litellm litellm 10.89.0.0/24 scram-sha-256
# Deny all other connections # Deny all other connections

View File

@ -1,12 +1,11 @@
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpoVnNlZyBsc0xG YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFpoVnNlZyA2RTVP
VzRzRGMxZ3h4MU1jZkZ4MEQzbUhaS0JYSzZ5eTZ5YXhWZEtTbWxFCnBrT2RKaEdN SU1CQkxJSDlMNkxnTDlYODFHOGRoMjBoY2ZxU2tYaDIvM0haOEQwCnlkdWtlckkz
U1BRRnZBWVYyWVpxTVhVM0d4VzhtbEEzNnoza0xpSEtQbXcKLT4gc3NoLWVkMjU1 aEh4N0Nucm80MWZKUjZzOHFiUUV2WjUyQ2g3UndxQ1NaRFEKLT4gc3NoLWVkMjU1
MTkgQ1NNeWhnIGNySDFJcjN4SEFReG5kUVBTdXVjWEpLRG8rSWVlSkI3WXovWE5q MTkgQ1NNeWhnIE1VZVFURmgreFJSTHJ0N1ZLYU5DSGdUdDh5S1hlN2p2b2dxbm9k
N3dZQVEKaWdJVVJuWUM1MEZGaTQrV3c2YVRSTmRIYk4wdkVnRStuWG5UVGxjQ0FP VjJWWDgKQkQzbjZIdG4zNC9lOU5BYy9mdVBZUzdRQXdlQzJRbGQzWG0zeXhmdG9w
TQotPiAtV3l1P0sjLWdyZWFzZQpGb1gzY0pYNHBGRjUvK2JhWlBDdHZtSnROaU5X SQotPiBSLWdyZWFzZSBwMCBUcTdoIEtSZntRCnQweWJyVUJvL1EKLS0tIEFKQmpt
aVc2L3hCSDJrUnJEdHNaUkFZT1cxK25IKzJza0VTb3hsTEFBCnkxSUJ2TFA3Sllz NkdNVHZHa0lEeTRWWTVndWFBWEhRT1JWYkFjL01GbWExMWQzM0kKRR8z5f9okejC
UGVQY285djc2eXhtU2wrbldtTnkyRGZONgotLS0gV0Y5Qk53TXNhYUozTVZlQjRh 26wOgfMoOYGDe1WKV+pN61IIMvodI6G/JBG2PGnzqDCwib7gzYetS6k/h4FJYMUu
RHRNOWs2RS9jNzA0cW53b09ucGZQSURyYwomvbCCgvqCeAaza/IO2/ih4OOOWJHO VvTX93e8kw==
m579mV6FSO0Ak2+AdYVWC6ddOqQEuqPwTmAkebfrSJ3IajZtczXtj8k=
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----