[timetagger,traefik] Adds timetagger back, and attempts to put it behind an oauth proxy, but the traefik config isn't quite right.
This commit is contained in:
@@ -11,11 +11,9 @@ focalboard:
|
||||
offen:
|
||||
secret: ENC[AES256_GCM,data:sH2siPc/QH1O2M7ZlJwqhqlHRIeLIG9r,iv:eD29ALx2ji0rm1t9j6RulTZT3f6VLK7dxpPOze3qDKA=,tag:zqJTgT2UeA/ecBS4VremUw==,type:str]
|
||||
smtp-token: ENC[AES256_GCM,data:ZTfe65g3JykPvG2l0AN8UQ==,iv:GTruGo/vcP+imfJyqB3NX9ic8dz5jvTEh6SF+OeqMDM=,tag:kgwd59pG/WUt8OAaVzi39Q==,type:str]
|
||||
traefik:
|
||||
oauth2-client-secret: ENC[AES256_GCM,data:c4zU/oH/buelobBo/47V74Xwm6MU76C+UPCFpuARYU8=,iv:7dl0MIjza6B8FGl3i3O/4CxKxlp4z2S+H82xSLUg5iQ=,tag:63E/RNjiw56evIxfhF8acg==,type:str]
|
||||
sops:
|
||||
kms: []
|
||||
gcp_kms: []
|
||||
azure_kv: []
|
||||
hc_vault: []
|
||||
age:
|
||||
- recipient: age1yvdzvuvu5wqztcx6ll2xk6x547uuyqy735tjjdd7zftkz53jsf9qf5ahue
|
||||
enc: |
|
||||
@@ -35,8 +33,7 @@ sops:
|
||||
by9aNFY4dXNxaWxnTXFTQS9reHhuQWMKh5rZ93nFtBV9EpFVRp+E+GXZ6xzVy2Jw
|
||||
vFh4deGcAb60q4odSaeWfk1Dr7L9Ua69oK9omjbCNUt+P7Kwlfca7Q==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2025-04-28T23:33:42Z"
|
||||
mac: ENC[AES256_GCM,data:cZkRcGV5/CPPVUdTDekwC8UjO6K348sBsS7NvR8wnoXS0AmSZsqN594nkvoc0VccM55Hwnm4jZxY56OV+UFMya1IRIkTo6LJRb88/CgZ8bjz30ACe33FKgJfCugimUDKsekbgNX1UFg1DVbqYK9/N4fcEBSxV3Xmzy5QGnQ/8KU=,iv:EprUHNtU5w7569ADMOxw+izDAL22A5OrB12T9iyHxKU=,tag:kRvyUEZwd/RttKdFOY2bJQ==,type:str]
|
||||
pgp: []
|
||||
lastmodified: "2025-05-29T23:38:06Z"
|
||||
mac: ENC[AES256_GCM,data:SImZMvXfUUt2IznmthXEG2o2cUUn485+D44OFZZc3WGmvrPIJ8WjT50HUu3fCEWmwO4mFv1VVBAr/SkN3s6VDgCh7Nbe4clp3zpgyiWamiXlvJP2Y0k1W+KjTd/AQ7jLig9D9UDXHVRBy5rZ5iLZdjZ6s5WQ8rfztcbMpkdi9yk=,iv:XVHHpwFkkhzbO3wYoerIcDeA5Io0GeLxzR2AT+BQij8=,tag:go/0xd1FhG/5h+TXA4xxHg==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.9.4
|
||||
version: 3.10.2
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
./containers/searxng.nix
|
||||
./containers/shared-postgres.nix
|
||||
./containers/synapse.nix
|
||||
./containers/timetagger.nix
|
||||
./containers/traefik.nix
|
||||
./containers/users.nix
|
||||
];
|
||||
@@ -50,7 +51,8 @@
|
||||
inherit (import ./containers/lib.nix config)
|
||||
hostRuleHavenisms
|
||||
localHostRuleHavenisms
|
||||
havenisms;
|
||||
havenisms
|
||||
;
|
||||
in
|
||||
{
|
||||
jellyfin = {
|
||||
|
||||
@@ -21,41 +21,62 @@ in
|
||||
havenisms
|
||||
blazestar
|
||||
terakoda
|
||||
terakoda_net;
|
||||
terakoda_net
|
||||
;
|
||||
|
||||
mkContainer = {
|
||||
image,
|
||||
hostName,
|
||||
port,
|
||||
homepageOpts ? {},
|
||||
dependsOn ? [],
|
||||
domain ? havenisms,
|
||||
ports ? [],
|
||||
volumes ? [],
|
||||
environment ? {},
|
||||
environmentFiles ? [],
|
||||
public ? false,
|
||||
user ? null,
|
||||
extraOptions ? [],
|
||||
}:
|
||||
mkContainer =
|
||||
{
|
||||
image,
|
||||
hostName,
|
||||
port,
|
||||
homepageOpts ? { },
|
||||
dependsOn ? [ ],
|
||||
domain ? havenisms,
|
||||
ports ? [ ],
|
||||
volumes ? [ ],
|
||||
environment ? { },
|
||||
environmentFiles ? [ ],
|
||||
public ? false,
|
||||
user ? null,
|
||||
extraOptions ? [ ],
|
||||
oauthProxy ? false,
|
||||
}:
|
||||
let
|
||||
routerRule = if public then hostRule hostName domain else localHostRule hostName domain;
|
||||
homepageLabels = if homepageOpts == {} then {} else {
|
||||
"homepage.group" = "${homepageOpts.group}";
|
||||
"homepage.name" = "${homepageOpts.name}";
|
||||
"homepage.icon" = "${homepageOpts.icon}";
|
||||
"homepage.href" = "https://${hostName}.${domain}";
|
||||
"homepage.description" = "${homepageOpts.description}";
|
||||
};
|
||||
homepageLabels =
|
||||
if homepageOpts == { } then
|
||||
{ }
|
||||
else
|
||||
{
|
||||
"homepage.group" = "${homepageOpts.group}";
|
||||
"homepage.name" = "${homepageOpts.name}";
|
||||
"homepage.icon" = "${homepageOpts.icon}";
|
||||
"homepage.href" = "https://${hostName}.${domain}";
|
||||
"homepage.description" = "${homepageOpts.description}";
|
||||
};
|
||||
oauthLabels =
|
||||
if oauthProxy then { "traefik.http.routers.${hostName}.middlewares" = "oidc-auth@file"; } else { };
|
||||
in
|
||||
{
|
||||
inherit image dependsOn volumes environment environmentFiles ports user extraOptions;
|
||||
inherit
|
||||
image
|
||||
dependsOn
|
||||
volumes
|
||||
environment
|
||||
environmentFiles
|
||||
ports
|
||||
user
|
||||
extraOptions
|
||||
;
|
||||
autoStart = true;
|
||||
labels = {
|
||||
"traefik.enable" = "true";
|
||||
"traefik.http.routers.${hostName}.rule" = "${routerRule}";
|
||||
"traefik.http.services.${hostName}.loadbalancer.server.port" = "${toString port}";
|
||||
} // homepageLabels;
|
||||
labels =
|
||||
{
|
||||
"traefik.enable" = "true";
|
||||
"traefik.http.routers.${hostName}.rule" = "${routerRule}";
|
||||
"traefik.http.services.${hostName}.loadbalancer.server.port" = "${toString port}";
|
||||
}
|
||||
// oauthLabels
|
||||
// homepageLabels;
|
||||
};
|
||||
|
||||
# Creates a MariaDB container for a specific app. It should be safe to give
|
||||
@@ -64,60 +85,64 @@ in
|
||||
# user.
|
||||
#
|
||||
# Note that this returns a _module_ so that it can be imported and provide many different config values.
|
||||
mkMariaDbContainer = {
|
||||
name,
|
||||
uid,
|
||||
gid,
|
||||
passwordSecret,
|
||||
directory,
|
||||
}:
|
||||
{ config, ... }:
|
||||
{
|
||||
virtualisation.oci-containers.containers."${name}-mariadb" = {
|
||||
image = "lscr.io/linuxserver/mariadb:latest";
|
||||
autoStart = true;
|
||||
ports = [ "3306:3306" ];
|
||||
volumes = [
|
||||
"${directory}:/config"
|
||||
"${config.sops.secrets.mariadb_root_password.path}:/run/secrets/mariadb_root_password"
|
||||
"${config.sops.secrets."${passwordSecret}".path}:/run/secrets/mariadb_password"
|
||||
];
|
||||
environment = {
|
||||
PUID = "${toString uid}";
|
||||
PGID = "${toString gid}";
|
||||
MYSQL_USER = name;
|
||||
MYSQL_DATABASE = name;
|
||||
FILE__MYSQL_ROOT_PASSWORD = "/run/secrets/mariadb_root_password";
|
||||
FILE__MYSQL_PASSWORD = "/run/secrets/mariadb_password";
|
||||
mkMariaDbContainer =
|
||||
{
|
||||
name,
|
||||
uid,
|
||||
gid,
|
||||
passwordSecret,
|
||||
directory,
|
||||
}:
|
||||
{ config, ... }:
|
||||
{
|
||||
virtualisation.oci-containers.containers."${name}-mariadb" = {
|
||||
image = "lscr.io/linuxserver/mariadb:latest";
|
||||
autoStart = true;
|
||||
ports = [ "3306:3306" ];
|
||||
volumes = [
|
||||
"${directory}:/config"
|
||||
"${config.sops.secrets.mariadb_root_password.path}:/run/secrets/mariadb_root_password"
|
||||
"${config.sops.secrets."${passwordSecret}".path}:/run/secrets/mariadb_password"
|
||||
];
|
||||
environment = {
|
||||
PUID = "${toString uid}";
|
||||
PGID = "${toString gid}";
|
||||
MYSQL_USER = name;
|
||||
MYSQL_DATABASE = name;
|
||||
FILE__MYSQL_ROOT_PASSWORD = "/run/secrets/mariadb_root_password";
|
||||
FILE__MYSQL_PASSWORD = "/run/secrets/mariadb_password";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
mkPostgresContainer = {
|
||||
name,
|
||||
uid,
|
||||
gid,
|
||||
passwordSecret,
|
||||
directory,
|
||||
containerName ? "${name}-postgres",
|
||||
databaseName ? name,
|
||||
username ? name,
|
||||
}: { config, ... }: {
|
||||
virtualisation.oci-containers.containers."${containerName}" = {
|
||||
image = "postgres";
|
||||
autoStart = true;
|
||||
volumes = [
|
||||
# Note that data must be mounted at this location to persist.
|
||||
# See https://github.com/docker-library/docs/blob/master/postgres/README.md#pgdata
|
||||
"${directory}:/var/lib/postgresql/data"
|
||||
"${config.sops.secrets."${passwordSecret}".path}:/run/secrets/postgres_password"
|
||||
];
|
||||
user = "${toString uid}:${toString gid}";
|
||||
environment = {
|
||||
POSTGRES_USER = username;
|
||||
POSTGRES_DB = databaseName;
|
||||
POSTGRES_PASSWORD_FILE = "/run/secrets/postgres_password";
|
||||
mkPostgresContainer =
|
||||
{
|
||||
name,
|
||||
uid,
|
||||
gid,
|
||||
passwordSecret,
|
||||
directory,
|
||||
containerName ? "${name}-postgres",
|
||||
databaseName ? name,
|
||||
username ? name,
|
||||
}:
|
||||
{ config, ... }:
|
||||
{
|
||||
virtualisation.oci-containers.containers."${containerName}" = {
|
||||
image = "postgres";
|
||||
autoStart = true;
|
||||
volumes = [
|
||||
# Note that data must be mounted at this location to persist.
|
||||
# See https://github.com/docker-library/docs/blob/master/postgres/README.md#pgdata
|
||||
"${directory}:/var/lib/postgresql/data"
|
||||
"${config.sops.secrets."${passwordSecret}".path}:/run/secrets/postgres_password"
|
||||
];
|
||||
user = "${toString uid}:${toString gid}";
|
||||
environment = {
|
||||
POSTGRES_USER = username;
|
||||
POSTGRES_DB = databaseName;
|
||||
POSTGRES_PASSWORD_FILE = "/run/secrets/postgres_password";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
50
system/hosts/mcp/containers/oauth2proxy.nix
Normal file
50
system/hosts/mcp/containers/oauth2proxy.nix
Normal file
@@ -0,0 +1,50 @@
|
||||
{ config, ... }:
|
||||
let
|
||||
inherit (import ./lib.nix config) mkContainer blazestar havenisms;
|
||||
in
|
||||
{
|
||||
sops.secrets = {
|
||||
"oauth2-proxy/cookie-secret" = {
|
||||
restartUnits = [ "podman-oauth2-proxy.service" ];
|
||||
mode = "0400";
|
||||
};
|
||||
"oauth2-proxy/client-secret" = {
|
||||
restartUnits = [ "podman-oauth2-proxy.service" ];
|
||||
mode = "0400";
|
||||
};
|
||||
};
|
||||
|
||||
sops.templates."oauth2-proxy.env".content = ''
|
||||
OAUTH2_PROXY_HTTP_ADDRESS='0.0.0.0:4180'
|
||||
OAUTH2_PROXY_COOKIE_SECRET='${config.sops.placehoder."oauth2-proxy/cookie-secret"}'
|
||||
OAUTH2_PROXY_COOKIE_DOMAINS='.${blazestar} .${havenisms}'
|
||||
OAUTH2_PROXY_WHITELIST_DOMAINS='.${blazestar} .${havenisms}'
|
||||
OAUTH2_PROXY_PROVIDER='oidc'
|
||||
OAUTH2_PROXY_CLIENT_ID='oauth2-proxy'
|
||||
OAUTH2_PROXY_CLIENT_SECRET='${config.sops.placehoder."oauth2-proxy/client-secret"}'
|
||||
OAUTH2_PROXY_EMAIL_DOMAINS='*'
|
||||
OAUTH2_PROXY_OIDC_ISSUER_URL='https://auth.${blazestar}/realms/master'
|
||||
OAUTH2_PROXY_REDIRECT_URL='https://auth.${blazestar}/oauth2/callback'
|
||||
OAUTH2_PROXY_COOKIE_CSRF_PER_REQUEST=true
|
||||
OAUTH2_PROXY_COOKIE_CSRF_EXPIRE='5m'
|
||||
OAUTH2_PROXY_CUSTOM_TEMPLATES_DIR="/templates"
|
||||
OAUTH2_PROXY_REVERSE_PROXY=true
|
||||
'';
|
||||
|
||||
virtualisation.oci-containers.containers.oauth2-proxy = mkContainer {
|
||||
image = "quay.io/oauth2-proxy/oauth2-proxy";
|
||||
hostName = "oauth";
|
||||
domain = blazestar;
|
||||
port = "4180";
|
||||
homepageOpts = {
|
||||
group = "Infra";
|
||||
name = "OAuth2-Proxy";
|
||||
icon = "oauth2-proxy.png";
|
||||
description = "An OAuth2 Reverse Proxy";
|
||||
};
|
||||
volumes = [
|
||||
];
|
||||
environment = {
|
||||
};
|
||||
};
|
||||
}
|
||||
31
system/hosts/mcp/containers/timetagger.nix
Normal file
31
system/hosts/mcp/containers/timetagger.nix
Normal file
@@ -0,0 +1,31 @@
|
||||
{ config, ... }:
|
||||
let
|
||||
inherit (import ./lib.nix config) mkContainer terakoda_net;
|
||||
in
|
||||
{
|
||||
virtualisation.oci-containers.containers.timetagger = mkContainer {
|
||||
image = "ghcr.io/almarklein/timetagger:v24.12.2";
|
||||
hostName = "time";
|
||||
domain = terakoda_net;
|
||||
port = "80";
|
||||
oauthProxy = true;
|
||||
homepageOpts = {
|
||||
group = "Apps";
|
||||
name = "TimeTagger";
|
||||
icon = "timetagger.png";
|
||||
description = "Time tracker";
|
||||
|
||||
};
|
||||
volumes = [
|
||||
"/tank/config/timetagger:/data"
|
||||
];
|
||||
environment = {
|
||||
TIMETAGGER_BIND = "0.0.0.0:80";
|
||||
TIMETAGGER_DATADIR = "/data";
|
||||
TIMETAGGER_LOG_LEVEL = "debug";
|
||||
TIMETAGGER_PROXY_AUTH_ENABLED = "True";
|
||||
TIMETAGGER_PROXY_AUTH_TRUSTED = "10.88.0.0/16";
|
||||
TIMETAGGER_PROXY_AUTH_HEADER = "X-Remote-User";
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -5,28 +5,62 @@ let
|
||||
name = "traefik-config";
|
||||
path = ./traefik;
|
||||
};
|
||||
in {
|
||||
virtualisation.oci-containers.containers.traefik = mkContainer {
|
||||
image = "traefik";
|
||||
hostName = "proxy";
|
||||
port = 8080;
|
||||
domain = blazestar;
|
||||
public = false;
|
||||
ports = [
|
||||
"80:80"
|
||||
"443:443"
|
||||
];
|
||||
volumes =
|
||||
[
|
||||
"/var/run/podman/podman.sock:/var/run/docker.sock:ro"
|
||||
"${traefikConfigDir}:/etc/traefik"
|
||||
"/tank/config/traefik/acme:/etc/traefik/acme"
|
||||
];
|
||||
homepageOpts = {
|
||||
name = "Traefik";
|
||||
icon = "traefik.svg";
|
||||
group = "Infra";
|
||||
description = "Reverse Proxy";
|
||||
};
|
||||
in
|
||||
{
|
||||
|
||||
sops.secrets = {
|
||||
"traefik/oauth2-client-secret" = {
|
||||
restartUnits = [ "podman-traefik.service" ];
|
||||
mode = "0400";
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
sops.templates."traefik/oauth2-config.yaml".content = ''
|
||||
experimental:
|
||||
plugins:
|
||||
traefik-oidc-auth:
|
||||
moduleName: "github.com/sevensolutions/traefik-oidc-auth"
|
||||
version: "v0.11.0"
|
||||
|
||||
http:
|
||||
middlewares:
|
||||
oidc-auth:
|
||||
plugin:
|
||||
traefik-oidc-auth:
|
||||
Provider:
|
||||
Url: "https://auth.blazestar.net/"
|
||||
ClientId: "3e3f7d9a-a684-4412-866c-ea7281954a9f"
|
||||
ClientSecret: "${config.sops.placeholder."traefik/oauth2-client-secret"}"
|
||||
TokenValidation: "IdToken"
|
||||
Scopes: ["openid", "profile", "email"]
|
||||
'';
|
||||
|
||||
virtualisation.oci-containers.containers.traefik = mkContainer {
|
||||
image = "traefik";
|
||||
hostName = "proxy";
|
||||
port = 8080;
|
||||
domain = blazestar;
|
||||
public = false;
|
||||
ports = [
|
||||
"80:80"
|
||||
"443:443"
|
||||
];
|
||||
volumes = [
|
||||
"/var/run/podman/podman.sock:/var/run/docker.sock:ro"
|
||||
# All the configs from the config directory
|
||||
"${traefikConfigDir}:/etc/traefik"
|
||||
# Oauth2 config containing secrets
|
||||
"${config.sops.templates."traefik/oauth2-config.yaml".path}:/etc/traefik/dynamic/oauth2-config.yaml"
|
||||
# Persistent storage for acme certificates
|
||||
# TODO: It may be possible to just use docker storage because persistence
|
||||
# is not critical when the cert can just be renewed.
|
||||
"/tank/config/traefik/acme:/etc/traefik/acme"
|
||||
];
|
||||
homepageOpts = {
|
||||
name = "Traefik";
|
||||
icon = "traefik.svg";
|
||||
group = "Infra";
|
||||
description = "Reverse Proxy";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ providers:
|
||||
docker:
|
||||
exposedByDefault: false
|
||||
file:
|
||||
directory: /etc/traefik/static
|
||||
directory: /etc/traefik/dynamic
|
||||
watch: true
|
||||
|
||||
certificatesResolvers:
|
||||
|
||||
Reference in New Issue
Block a user