also refactor nixos modules
This commit is contained in:
parent
2471562583
commit
b37c5c0cbd
44 changed files with 10 additions and 2 deletions
733
modules/nixos/services/authentication/zitadel.nix
Normal file
733
modules/nixos/services/authentication/zitadel.nix
Normal file
|
|
@ -0,0 +1,733 @@
|
|||
{ config, lib, pkgs, self, ... }:
|
||||
let
|
||||
inherit (lib) mkIf mkEnableOption mkOption types toUpper toSentenceCase nameValuePair mapAttrs mapAttrs' concatMapAttrs concatMapStringsSep filterAttrsRecursive listToAttrs imap0 head drop length literalExpression attrNames;
|
||||
inherit ((import ../../../../../lib/strings { inherit lib;}).strings) toSnakeCase;
|
||||
|
||||
cfg = config.sneeuwvlok.services.authentication.zitadel;
|
||||
|
||||
database = "zitadel";
|
||||
in
|
||||
{
|
||||
options.sneeuwvlok.services.authentication.zitadel = {
|
||||
enable = mkEnableOption "Zitadel";
|
||||
|
||||
organization = mkOption {
|
||||
type = types.attrsOf (types.submodule ({ name, ... }: {
|
||||
options =
|
||||
let
|
||||
org = name;
|
||||
in
|
||||
{
|
||||
isDefault = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
example = "true";
|
||||
description = ''
|
||||
True sets the '${org}' org as default org for the instance. Only one org can be default org.
|
||||
Nothing happens if you set it to false until you set another org as default org.
|
||||
'';
|
||||
};
|
||||
|
||||
project = mkOption {
|
||||
default = {};
|
||||
type = types.attrsOf (types.submodule {
|
||||
options = {
|
||||
hasProjectCheck = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
example = "true";
|
||||
description = ''
|
||||
ZITADEL checks if the org of the user has permission to this project.
|
||||
'';
|
||||
};
|
||||
|
||||
privateLabelingSetting = mkOption {
|
||||
type = types.nullOr (types.enum [ "unspecified" "enforceProjectResourceOwnerPolicy" "allowLoginUserResourceOwnerPolicy" ]);
|
||||
default = null;
|
||||
example = "enforceProjectResourceOwnerPolicy";
|
||||
description = ''
|
||||
Defines from where the private labeling should be triggered,
|
||||
|
||||
supported values:
|
||||
- unspecified
|
||||
- enforceProjectResourceOwnerPolicy
|
||||
- allowLoginUserResourceOwnerPolicy
|
||||
'';
|
||||
};
|
||||
|
||||
projectRoleAssertion = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
example = "true";
|
||||
description = ''
|
||||
Describes if roles of user should be added in token.
|
||||
'';
|
||||
};
|
||||
|
||||
projectRoleCheck = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
example = "true";
|
||||
description = ''
|
||||
ZITADEL checks if the user has at least one on this project.
|
||||
'';
|
||||
};
|
||||
|
||||
role = mkOption {
|
||||
default = {};
|
||||
type = types.attrsOf (types.submodule ({ name, ... }: {
|
||||
options =
|
||||
let
|
||||
roleName = name;
|
||||
in
|
||||
{
|
||||
displayName = mkOption {
|
||||
type = types.str;
|
||||
default = toSentenceCase name;
|
||||
example = "RoleName";
|
||||
description = ''
|
||||
Name used for project role.
|
||||
'';
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "some_group";
|
||||
description = ''
|
||||
Group used for project role.
|
||||
'';
|
||||
};
|
||||
};
|
||||
}));
|
||||
};
|
||||
|
||||
assign = mkOption {
|
||||
default = {};
|
||||
type = types.attrsOf (types.listOf types.str);
|
||||
};
|
||||
|
||||
application = mkOption {
|
||||
default = {};
|
||||
type = types.attrsOf (types.submodule {
|
||||
options = {
|
||||
redirectUris = mkOption {
|
||||
type = types.nonEmptyListOf types.str;
|
||||
example = ''
|
||||
[ "https://example.com/redirect/url" ]
|
||||
'';
|
||||
description = ''
|
||||
.
|
||||
'';
|
||||
};
|
||||
|
||||
grantTypes = mkOption {
|
||||
type = types.nonEmptyListOf (types.enum [ "authorizationCode" "implicit" "refreshToken" "deviceCode" "tokenExchange" ]);
|
||||
example = ''
|
||||
[ "authorizationCode" ]
|
||||
'';
|
||||
description = ''
|
||||
.
|
||||
'';
|
||||
};
|
||||
|
||||
responseTypes = mkOption {
|
||||
type = types.nonEmptyListOf (types.enum [ "code" "idToken" "idTokenToken" ]);
|
||||
example = ''
|
||||
[ "code" ]
|
||||
'';
|
||||
description = ''
|
||||
.
|
||||
'';
|
||||
};
|
||||
|
||||
exportMap =
|
||||
let
|
||||
strOpt = mkOption { type = types.nullOr types.str; default = null; };
|
||||
in
|
||||
mkOption {
|
||||
type = types.submodule { options = { client_id = strOpt; client_secret = strOpt; }; };
|
||||
default = {};
|
||||
example = literalExpression ''
|
||||
{
|
||||
client_id = "SSO_CLIENT_ID";
|
||||
client_secret = "SSO_CLIENT_SECRET";
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
Remap the outputted variables to another key.
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
default = {};
|
||||
type = types.attrsOf (types.submodule ({ name, ... }: {
|
||||
options =
|
||||
let
|
||||
username = name;
|
||||
in
|
||||
{
|
||||
email = mkOption {
|
||||
type = types.str;
|
||||
example = "someone@some.domain";
|
||||
description = ''
|
||||
Username.
|
||||
'';
|
||||
};
|
||||
|
||||
userName = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = username;
|
||||
example = "some_user_name";
|
||||
description = ''
|
||||
Username. Default value is the key of the config object you created, you can overwrite that by setting this option
|
||||
'';
|
||||
};
|
||||
|
||||
firstName = mkOption {
|
||||
type = types.str;
|
||||
example = "John";
|
||||
description = ''
|
||||
First name of the user.
|
||||
'';
|
||||
};
|
||||
|
||||
lastName = mkOption {
|
||||
type = types.str;
|
||||
example = "Doe";
|
||||
description = ''
|
||||
Last name of the user.
|
||||
'';
|
||||
};
|
||||
|
||||
roles = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
example = "[ \"ORG_OWNER\" ]";
|
||||
description = ''
|
||||
List of roles granted to organisation.
|
||||
'';
|
||||
};
|
||||
|
||||
instanceRoles = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
example = "[ \"IAM_OWNER\" ]";
|
||||
description = ''
|
||||
List of roles granted to instance.
|
||||
'';
|
||||
};
|
||||
};
|
||||
}));
|
||||
};
|
||||
|
||||
action = mkOption {
|
||||
default = {};
|
||||
type = types.attrsOf (types.submodule ({ name, ... }: {
|
||||
options = {
|
||||
script = mkOption {
|
||||
type = types.str;
|
||||
example = ''
|
||||
(ctx, api) => {
|
||||
api.v1.claims.setClaim('some_claim', 'some_value');
|
||||
};
|
||||
'';
|
||||
description = ''
|
||||
The script to run. This must be a function that receives 2 parameters, and returns void. During the creation of the action's script this module simly does `const {{name}} = {{script}}`.
|
||||
'';
|
||||
};
|
||||
|
||||
timeout = mkOption {
|
||||
type = (types.ints.between 0 20);
|
||||
default = 10;
|
||||
example = "10";
|
||||
description = ''
|
||||
After which time the action will be terminated if not finished.
|
||||
'';
|
||||
};
|
||||
|
||||
allowedToFail = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
example = "true";
|
||||
description = ''
|
||||
Allowed to fail.
|
||||
'';
|
||||
};
|
||||
};
|
||||
}));
|
||||
};
|
||||
|
||||
triggers = mkOption {
|
||||
default = [];
|
||||
type = types.listOf (types.submodule {
|
||||
options = {
|
||||
flowType = mkOption {
|
||||
type = types.enum [ "authentication" "customiseToken" "internalAuthentication" "samlResponse" ];
|
||||
example = "customiseToken";
|
||||
description = ''
|
||||
Type of the flow to which the action triggers belong.
|
||||
'';
|
||||
};
|
||||
|
||||
triggerType = mkOption {
|
||||
type = types.enum [ "postAuthentication" "preCreation" "postCreation" "preUserinfoCreation" "preAccessTokenCreation" "preSamlResponse" ];
|
||||
example = "postAuthentication";
|
||||
description = ''
|
||||
Trigger type on when the actions get triggered.
|
||||
'';
|
||||
};
|
||||
|
||||
actions = mkOption {
|
||||
type = types.nonEmptyListOf types.str;
|
||||
example = ''[ "action_name" ]'';
|
||||
description = ''
|
||||
Names of actions to trigger
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
}));
|
||||
};
|
||||
};
|
||||
|
||||
config = let
|
||||
_refTypeMap = {
|
||||
org = { type = "org"; };
|
||||
project = { type = "project"; };
|
||||
user = { type = "user"; tfType = "human_user"; };
|
||||
};
|
||||
|
||||
mapRef' = { type, tfType ? type }: name: { "${type}Id" = "\${ resource.zitadel_${tfType}.${toSnakeCase name}.id }"; };
|
||||
mapRef = type: name: mapRef' (_refTypeMap.${type}) name;
|
||||
mapEnum = prefix: value: "${prefix}_${value |> toSnakeCase |> toUpper}";
|
||||
|
||||
mapValue = type: value: ({
|
||||
appType = mapEnum "OIDC_APP_TYPE" value;
|
||||
grantTypes = map (t: mapEnum "OIDC_GRANT_TYPE" t) value;
|
||||
responseTypes = map (t: mapEnum "OIDC_RESPONSE_TYPE" t) value;
|
||||
authMethodType = mapEnum "OIDC_AUTH_METHOD_TYPE" value;
|
||||
|
||||
flowType = mapEnum "FLOW_TYPE" value;
|
||||
triggerType = mapEnum "TRIGGER_TYPE" value;
|
||||
accessTokenType = mapEnum "OIDC_TOKEN_TYPE" value;
|
||||
}."${type}" or value);
|
||||
|
||||
toResource = name: value: nameValuePair
|
||||
(toSnakeCase name)
|
||||
(lib.mapAttrs' (k: v: nameValuePair (toSnakeCase k) (mapValue k v)) value);
|
||||
|
||||
withRef = type: name: attrs: attrs // (mapRef type name);
|
||||
|
||||
select = keys: callback: set:
|
||||
if (length keys) == 0 then
|
||||
mapAttrs' callback set
|
||||
else let key = head keys; in
|
||||
concatMapAttrs (k: v: select (drop 1 keys) (callback k) (v.${key} or {})) set
|
||||
;
|
||||
|
||||
append = attrList: set: set // (listToAttrs attrList);
|
||||
|
||||
config' = config;
|
||||
|
||||
# this is a nix package, the generated json file to be exact
|
||||
terraformConfiguration = self.inputs.terranix.lib.terranixConfiguration {
|
||||
system = pkgs.stdenv.hostPlatform.system;
|
||||
|
||||
modules = [
|
||||
({ config, lib, ... }: {
|
||||
config =
|
||||
let
|
||||
forEach = src: key: set:
|
||||
let
|
||||
_key = concatMapStringsSep "_" (k: "\${item.${k}}") key;
|
||||
in
|
||||
{
|
||||
forEach = lib.tfRef ''{
|
||||
for item in ${src} :
|
||||
"''${item.org}_''${item.name}" => item
|
||||
}'';
|
||||
}
|
||||
// set;
|
||||
in
|
||||
{
|
||||
terraform.required_providers.zitadel = {
|
||||
source = "zitadel/zitadel";
|
||||
version = "2.2.0";
|
||||
};
|
||||
|
||||
provider.zitadel = {
|
||||
domain = "auth.kruining.eu";
|
||||
insecure = "false";
|
||||
jwt_profile_file = "/var/lib/zitadel/machine-key.json";
|
||||
};
|
||||
|
||||
locals = {
|
||||
extra_users = lib.tfRef "
|
||||
flatten([ for org, users in jsondecode(file(\"${config'.sops.secrets."zitadel/users".path}\")): [
|
||||
for name, details in users: {
|
||||
org = org
|
||||
name = name
|
||||
email = details.email
|
||||
firstName = details.firstName
|
||||
lastName = details.lastName
|
||||
}
|
||||
] ])
|
||||
";
|
||||
orgs = cfg.organization |> mapAttrs (org: _: lib.tfRef "resource.zitadel_org.${org}.id");
|
||||
};
|
||||
|
||||
resource = {
|
||||
# Organizations
|
||||
zitadel_org = cfg.organization |> select [] (name: { isDefault, ... }:
|
||||
{ inherit name isDefault; }
|
||||
|> toResource name
|
||||
);
|
||||
|
||||
# Projects per organization
|
||||
zitadel_project = cfg.organization |> select [ "project" ] (org: name: { hasProjectCheck, privateLabelingSetting, projectRoleAssertion, projectRoleCheck, ... }:
|
||||
{
|
||||
inherit name hasProjectCheck privateLabelingSetting projectRoleAssertion projectRoleCheck;
|
||||
}
|
||||
|> withRef "org" org
|
||||
|> toResource "${org}_${name}"
|
||||
);
|
||||
|
||||
# Each OIDC app per project
|
||||
zitadel_application_oidc = cfg.organization |> select [ "project" "application" ] (org: project: name: { redirectUris, grantTypes, responseTypes, ...}:
|
||||
{
|
||||
inherit name redirectUris grantTypes responseTypes;
|
||||
|
||||
accessTokenRoleAssertion = true;
|
||||
idTokenRoleAssertion = true;
|
||||
accessTokenType = "JWT";
|
||||
}
|
||||
|> withRef "org" org
|
||||
|> withRef "project" "${org}_${project}"
|
||||
|> toResource "${org}_${project}_${name}"
|
||||
);
|
||||
|
||||
# Each project role
|
||||
zitadel_project_role = cfg.organization |> select [ "project" "role" ] (org: project: name: value:
|
||||
{ inherit (value) displayName group; roleKey = name; }
|
||||
|> withRef "org" org
|
||||
|> withRef "project" "${org}_${project}"
|
||||
|> toResource "${org}_${project}_${name}"
|
||||
);
|
||||
|
||||
# Each project role assignment
|
||||
zitadel_user_grant = cfg.organization |> select [ "project" "assign" ] (org: project: user: roles:
|
||||
{ roleKeys = roles; }
|
||||
|> withRef "org" org
|
||||
|> withRef "project" "${org}_${project}"
|
||||
|> withRef "user" "${org}_${user}"
|
||||
|> toResource "${org}_${project}_${user}"
|
||||
);
|
||||
|
||||
# Users
|
||||
zitadel_human_user =
|
||||
cfg.organization
|
||||
|> select [ "user" ] (org: name: { email, userName, firstName, lastName, ... }:
|
||||
{
|
||||
inherit email userName firstName lastName;
|
||||
|
||||
isEmailVerified = true;
|
||||
}
|
||||
|> withRef "org" org
|
||||
|> toResource "${org}_${name}"
|
||||
)
|
||||
|> append [
|
||||
(forEach "local.extra_users" [ "org" "name" ] {
|
||||
orgId = lib.tfRef "local.orgs[each.value.org]";
|
||||
userName = lib.tfRef "each.value.name";
|
||||
email = lib.tfRef "each.value.email";
|
||||
firstName = lib.tfRef "each.value.firstName";
|
||||
lastName = lib.tfRef "each.value.lastName";
|
||||
|
||||
isEmailVerified = true;
|
||||
}
|
||||
|> toResource "extraUsers")
|
||||
]
|
||||
;
|
||||
|
||||
# Global user roles
|
||||
zitadel_instance_member =
|
||||
cfg.organization
|
||||
|> filterAttrsRecursive (n: v: !(v ? "instanceRoles" && (length v.instanceRoles) == 0))
|
||||
|> select [ "user" ] (org: name: { instanceRoles, ... }:
|
||||
{ roles = instanceRoles; }
|
||||
|> withRef "user" "${org}_${name}"
|
||||
|> toResource "${org}_${name}"
|
||||
);
|
||||
|
||||
# Organazation specific roles
|
||||
zitadel_org_member =
|
||||
cfg.organization
|
||||
|> filterAttrsRecursive (n: v: !(v ? "roles" && (length v.roles) == 0))
|
||||
|> select [ "user" ] (org: name: { roles, ... }:
|
||||
{ inherit roles; }
|
||||
|> withRef "org" org
|
||||
|> withRef "user" "${org}_${name}"
|
||||
|> toResource "${org}_${name}"
|
||||
);
|
||||
|
||||
# Organazation's actions
|
||||
zitadel_action = cfg.organization |> select [ "action" ] (org: name: { timeout, allowedToFail, script, ...}:
|
||||
{
|
||||
inherit allowedToFail name;
|
||||
timeout = "${toString timeout}s";
|
||||
script = "const ${name} = ${script}";
|
||||
}
|
||||
|> withRef "org" org
|
||||
|> toResource "${org}_${name}"
|
||||
);
|
||||
|
||||
# Organazation's action assignments
|
||||
zitadel_trigger_actions =
|
||||
cfg.organization
|
||||
|> concatMapAttrs (org: { triggers, ... }:
|
||||
triggers
|
||||
|> imap0 (i: { flowType, triggerType, actions, ... }: (let name = "trigger_${toString i}"; in
|
||||
{
|
||||
inherit flowType triggerType;
|
||||
|
||||
actionIds =
|
||||
actions
|
||||
|> map (action: (lib.tfRef "zitadel_action.${org}_${toSnakeCase action}.id"));
|
||||
}
|
||||
|> withRef "org" org
|
||||
|> toResource "${org}_${name}"
|
||||
))
|
||||
|> listToAttrs
|
||||
);
|
||||
|
||||
# SMTP config
|
||||
zitadel_smtp_config.default = {
|
||||
sender_address = "chris@kruining.eu";
|
||||
sender_name = "no-reply (Zitadel)";
|
||||
tls = true;
|
||||
host = "black-mail.nl:587";
|
||||
user = "chris@kruining.eu";
|
||||
password = lib.tfRef "file(\"${config'.sops.secrets."zitadel/email".path}\")";
|
||||
set_active = true;
|
||||
};
|
||||
|
||||
# Client credentials per app
|
||||
local_sensitive_file = cfg.organization |> select [ "project" "application" ] (org: project: name: { exportMap, ... }:
|
||||
nameValuePair "${org}_${project}_${name}" {
|
||||
content = ''
|
||||
${if exportMap.client_id != null then exportMap.client_id else "CLIENT_ID"}=${lib.tfRef "resource.zitadel_application_oidc.${org}_${project}_${name}.client_id"}
|
||||
${if exportMap.client_secret != null then exportMap.client_secret else "CLIENT_SECRET"}=${lib.tfRef "resource.zitadel_application_oidc.${org}_${project}_${name}.client_secret"}
|
||||
'';
|
||||
filename = "/var/lib/zitadel/clients/${org}_${project}_${name}";
|
||||
}
|
||||
);
|
||||
};
|
||||
};
|
||||
})
|
||||
];
|
||||
};
|
||||
in
|
||||
mkIf cfg.enable {
|
||||
sneeuwvlok.services = {
|
||||
persistance.postgresql.enable = true;
|
||||
|
||||
networking.caddy = {
|
||||
hosts = {
|
||||
"auth.kruining.eu" = ''
|
||||
reverse_proxy h2c://[::1]:9092
|
||||
'';
|
||||
};
|
||||
extraConfig = ''
|
||||
(auth) {
|
||||
forward_auth h2c://[::1]:9092 {
|
||||
uri /api/authz/forward-auth
|
||||
copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
|
||||
}
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
zitadel
|
||||
];
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /tmp/zitadelApplyTerraform 0755 zitadel zitadel -"
|
||||
"d /var/lib/zitadel/clients 0755 zitadel zitadel -"
|
||||
];
|
||||
|
||||
systemd.services.zitadelApplyTerraform = {
|
||||
description = "Zitadel terraform apply";
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
wants = [ "zitadel.service" ];
|
||||
|
||||
script =
|
||||
let
|
||||
tofu = lib.getExe pkgs.opentofu;
|
||||
in
|
||||
lib.replaceStrings ["\r"] [""] ''
|
||||
if [ "$(systemctl is-active zitadel)" != "active" ]; then
|
||||
echo "Zitadel is not running"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Print the path to the source for easier debugging
|
||||
echo "config location: ${terraformConfiguration}"
|
||||
|
||||
# Copy infra code into workspace
|
||||
cp -f ${terraformConfiguration} config.tf.json
|
||||
|
||||
# Initialize OpenTofu
|
||||
${tofu} init
|
||||
|
||||
# Run the infrastructure code
|
||||
${tofu} plan -refresh=false -out=tfplan
|
||||
${tofu} apply -auto-approve tfplan
|
||||
'';
|
||||
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
User = "zitadel";
|
||||
Group = "zitadel";
|
||||
|
||||
WorkingDirectory = "/tmp/zitadelApplyTerraform";
|
||||
};
|
||||
};
|
||||
|
||||
services = {
|
||||
zitadel = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
masterKeyFile = config.sops.secrets."zitadel/masterKey".path;
|
||||
tlsMode = "external";
|
||||
settings = {
|
||||
Port = 9092;
|
||||
|
||||
ExternalDomain = "auth.kruining.eu";
|
||||
ExternalPort = 443;
|
||||
ExternalSecure = true;
|
||||
|
||||
Metrics.Type = "otel";
|
||||
Tracing.Type = "otel";
|
||||
Telemetry.Enabled = true;
|
||||
|
||||
SystemDefaults = {
|
||||
PasswordHasher.Hasher.Algorithm = "argon2id";
|
||||
SecretHasher.Hasher.Algorithm = "argon2id";
|
||||
};
|
||||
|
||||
Database.postgres = {
|
||||
Host = "localhost";
|
||||
# Zitadel will report error if port is not set
|
||||
Port = 5432;
|
||||
Database = database;
|
||||
User = {
|
||||
Username = database;
|
||||
SSL.Mode = "disable";
|
||||
};
|
||||
Admin = {
|
||||
Username = "postgres";
|
||||
SSL.Mode = "disable";
|
||||
};
|
||||
};
|
||||
};
|
||||
steps = {
|
||||
FirstInstance = {
|
||||
# Not sure, this option seems to be mostly irrelevant
|
||||
InstanceName = "eu";
|
||||
|
||||
MachineKeyPath = "/var/lib/zitadel/machine-key.json";
|
||||
# PatPath = "/var/lib/zitadel/machine-key.pat";
|
||||
# LoginClientPatPath = "/var/lib/zitadel/machine-key.json";
|
||||
|
||||
Org = {
|
||||
Name = "kruining";
|
||||
|
||||
Human = {
|
||||
UserName = "chris";
|
||||
FirstName = "Chris";
|
||||
LastName = "Kruining";
|
||||
Email = {
|
||||
Address = "chris@kruining.eu";
|
||||
Verified = true;
|
||||
};
|
||||
Password = "KaasIsAwesome1!";
|
||||
};
|
||||
|
||||
Machine = {
|
||||
Machine = {
|
||||
Username = "terraform-service-user";
|
||||
Name = "Terraform";
|
||||
};
|
||||
MachineKey = { ExpirationDate = "2026-01-01T00:00:00Z"; Type = 1; };
|
||||
# Pat = { ExpirationDate = "2026-01-01T00:00:00Z"; };
|
||||
};
|
||||
|
||||
# LoginClient.Machine = {
|
||||
# Username = "terraform-service-user";
|
||||
# Name = "Terraform";
|
||||
# };
|
||||
};
|
||||
};
|
||||
};
|
||||
# extraStepsPaths = [
|
||||
# config.sops.templates."secrets.yaml".path
|
||||
# ];
|
||||
};
|
||||
|
||||
postgresql = {
|
||||
enable = true;
|
||||
ensureDatabases = [ database ];
|
||||
ensureUsers = [
|
||||
{
|
||||
name = database;
|
||||
ensureDBOwnership = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
||||
|
||||
# Secrets
|
||||
sops = {
|
||||
secrets = {
|
||||
"zitadel/masterKey" = {
|
||||
owner = "zitadel";
|
||||
group = "zitadel";
|
||||
restartUnits = [ "zitadel.service" ]; #EMGDB#6O$8qpGoLI1XjhUhnng1san@0
|
||||
};
|
||||
|
||||
"zitadel/email" = {
|
||||
owner = "zitadel";
|
||||
group = "zitadel";
|
||||
key = "email/chris_kruining_eu";
|
||||
restartUnits = [ "zitadel.service" ];
|
||||
};
|
||||
|
||||
"zitadel/users" = {
|
||||
owner = "zitadel";
|
||||
group = "zitadel";
|
||||
restartUnits = [ "zitadelApplyTerraform.service" ];
|
||||
};
|
||||
};
|
||||
|
||||
templates = {
|
||||
"users.yml" = {
|
||||
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue