Compare commits

..

No commits in common. "fbe957b046e179dac825165638612dce0bf2e401" and "cc1ab841c203ada6942ef10ca57035cf6b4a3507" have entirely different histories.

20 changed files with 295 additions and 436 deletions

View file

@ -0,0 +1,9 @@
{ lib, config, ... }:
let
cfg = config.khscodes.infrastructure.mailserver;
in
{
config = lib.mkIf cfg.enable {
khscodes.services.nginx.virtualHosts."${config.khscodes.networking.fqdn}" = { };
};
}

View file

@ -5,11 +5,12 @@
...
}:
let
cfg = config.khscodes."mx.kaareskovgaard.net";
cfg = config.khscodes.infrastructure.mailserver;
fqdn = config.khscodes.networking.fqdn;
in
{
options.khscodes."mx.kaareskovgaard.net" = {
options.khscodes.infrastructure.mailserver = {
enable = lib.mkEnableOption "Enables setting up stuff for a mail server";
domains = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
@ -17,7 +18,6 @@ in
};
imports = [
inputs.simple-nixos-mailserver.nixosModules.mailserver
./accounts.nix
./acme.nix
./dmarc.nix
./dane.nix
@ -29,9 +29,9 @@ in
./prometheus.nix
./openid-connect.nix
./ldap.nix
./roundcube.nix
];
config = {
config = lib.mkIf cfg.enable {
# TODO: Include a similiar rule for openstack
khscodes.infrastructure.hetzner-instance.extraFirewallRules = [
{
direction = "out";
@ -80,7 +80,6 @@ in
)
];
mailserver = {
inherit (cfg) domains;
enable = true;
enableImap = false;
enableImapSsl = true;
@ -88,6 +87,7 @@ in
enableSubmissionSsl = true;
fqdn = config.khscodes.networking.fqdn;
useUTF8FolderNames = true;
domains = cfg.domains;
certificateScheme = "acme";
};
services.fail2ban.jails = {

View file

@ -5,10 +5,7 @@
...
}:
let
cfg = config.khscodes."mx.kaareskovgaard.net";
mount = "mx.kaareskovgaard.net";
mountExpr = "\${ vault_mount.${lib.khscodes.sanitize-terraform-name mount}.path }";
prefixPath = "dkim";
cfg = config.khscodes.infrastructure.mailserver;
opensslStripAsn1 = pkgs.writeTextFile {
name = "openssl-strip-asn1";
executable = true;
@ -51,9 +48,23 @@ let
''''${ replace(trimprefix(trimsuffix(${tls_key}.public_key_pem, ${publicKeyEnd}), ${publicKeyBegin}), "\n", "") }'';
in
{
config = {
options.khscodes.infrastructure.mailserver.dkim = {
vault = {
mount = lib.mkOption {
type = lib.types.str;
};
mountExpr = lib.mkOption {
type = lib.types.str;
default = "\${ vault_mount.${lib.khscodes.sanitize-terraform-name cfg.dkim.vault.mount}.path }";
};
prefixPath = lib.mkOption {
type = lib.types.str;
};
};
};
config = lib.mkIf (cfg.enable) {
khscodes.infrastructure.vault-server-approle.policy = {
"${mount}/data/${prefixPath}/*" = {
"${cfg.dkim.vault.mount}/data/${cfg.dkim.vault.prefixPath}/*" = {
capabilities = [ "read" ];
};
};
@ -131,8 +142,8 @@ in
lib.lists.map (domain: {
name = "${lib.khscodes.sanitize-terraform-name domain}_dkim_rsa";
value = {
mount = mountExpr;
name = prefixPath + "/${domain}/rsa";
mount = cfg.dkim.vault.mountExpr;
name = cfg.dkim.vault.prefixPath + "/${domain}/rsa";
data_json = ''
{ "dkim_private_key": ''${ jsonencode(resource.tls_private_key.${lib.khscodes.sanitize-terraform-name domain}_dkim_rsa.private_key_pem) } }
'';
@ -143,8 +154,8 @@ in
lib.lists.map (domain: {
name = "${lib.khscodes.sanitize-terraform-name domain}_dkim_ed25519";
value = {
mount = mountExpr;
name = prefixPath + "/${domain}/ed25519";
mount = cfg.dkim.vault.mountExpr;
name = cfg.dkim.vault.prefixPath + "/${domain}/ed25519";
data_json = ''
{ "dkim_private_key": ''${ jsonencode(resource.tls_private_key.${lib.khscodes.sanitize-terraform-name domain}_dkim_ed25519.private_key_pem) } }
'';
@ -158,7 +169,7 @@ in
lib.lists.map (domain: [
{
contents = ''
{{- with secret "${mount}/data/${prefixPath}/${domain}/rsa" -}}
{{- with secret "${cfg.dkim.vault.mount}/data/${cfg.dkim.vault.prefixPath}/${domain}/rsa" -}}
{{ .Data.data.dkim_private_key }}
{{- end -}}
'';
@ -174,7 +185,7 @@ in
# rspamd does not like reading ed25519 keys with begin/end pairs, as they get parsed as
# rsa keys
contents = ''
{{- with secret "${mount}/data/${prefixPath}/${domain}/ed25519" -}}
{{- with secret "${cfg.dkim.vault.mount}/data/${cfg.dkim.vault.prefixPath}/${domain}/ed25519" -}}
{{ .Data.data.dkim_private_key }}
{{- end -}}
'';

View file

@ -1,9 +1,9 @@
{ config, lib, ... }:
let
cfg = config.khscodes."mx.kaareskovgaard.net";
cfg = config.khscodes.infrastructure.mailserver;
in
{
config = {
config = lib.mkIf cfg.enable {
khscodes.infrastructure.provisioning.pre.modules = [
{
khscodes.cloudflare.dns.txtRecords = lib.lists.map (domain: {

View file

@ -0,0 +1,96 @@
{ lib, config, ... }:
let
cfg = config.khscodes.infrastructure.mailserver;
secretFile = "/run/secret/dovecot/ldap";
in
{
options.khscodes.infrastructure.mailserver = {
ldap = {
mount = lib.mkOption {
type = lib.types.str;
};
path = lib.mkOption {
type = lib.types.str;
};
};
};
config = lib.mkIf cfg.enable {
mailserver.ldap = {
enable = true;
uris = [ "ldaps://login.kaareskovgaard.net" ];
searchBase = "dc=login,dc=kaareskovgaard,dc=net";
searchScope = "sub";
bind = {
dn = "dn=token";
passwordFile = secretFile;
};
dovecot = {
# Map LDAP uid to dovecot user, and ldap userPassword to dovecot password
passAttrs = "uid=user";
passFilter = "(&(class=account)(memberOf=mail_user)(uid=%u))";
# This filter is used both when receiving mail (thus needing to lookup by mail address, and when authenticating, requriing the lookup by uid.)
# Note that the pass filter only allows looking up by uid, so should still only be able to authenticate using that.
userFilter = "(&(class=account)(memberOf=mail_user)(|(mail=%u)(uid=%u)))";
userAttrs = "uid=user";
};
postfix = {
filter = "(&(class=account)(memberOf=mail_user)(mail=%s))";
mailAttribute = "uid";
uidAttribute = "uid";
};
};
systemd.services = {
dovecot2 = {
unitConfig = {
ConditionPathExists = [ secretFile ];
};
};
postfix = {
unitConfig = {
ConditionPathExists = [ secretFile ];
};
};
postfix-setup = {
unitConfig = {
ConditionPathExists = [ secretFile ];
};
};
};
khscodes.services.vault-agent.templates = [
{
contents = ''
{{- with secret "${cfg.ldap.mount}/data/${cfg.ldap.path}" -}}
{{ .Data.data.apiToken }}
{{- end -}}
'';
destination = secretFile;
owner = "dovecot";
group = "dovecot";
restartUnits = [
"dovecot2.service"
"postfix.service"
];
}
];
# TODO: Include a similiar rule for openstack
khscodes.infrastructure.hetzner-instance.extraFirewallRules = [
{
direction = "out";
protocol = "tcp";
port = 636;
destination_ips = [
"0.0.0.0/0"
"::/0"
];
description = "ldaps";
}
];
khscodes.infrastructure.vault-server-approle.policy = {
"${cfg.ldap.mount}/data/${cfg.ldap.path}" = {
capabilities = [ "read" ];
};
};
};
}

View file

@ -0,0 +1,9 @@
{ config, lib, ... }:
let
cfg = config.khscodes.infrastructure.mailserver;
in
{
config = lib.mkIf cfg.enable {
services.dovecot2.protocols = [ "sieve" ];
};
}

View file

@ -6,7 +6,7 @@
}:
let
fqdn = config.khscodes.networking.fqdn;
cfg = config.khscodes."mx.kaareskovgaard.net";
cfg = config.khscodes.infrastructure.mailserver;
# Increment this if ever changing mta-sts settings.
policyVersion = 2;
mtaStsWellKnown = pkgs.writeTextFile {
@ -20,7 +20,7 @@ let
};
in
{
config = {
config = lib.mkIf cfg.enable {
khscodes.services.nginx.virtualHosts = (
lib.listToAttrs (
lib.lists.map (domain: {

View file

@ -1,15 +1,10 @@
{ config, ... }:
{ config, lib, ... }:
let
cfg = config.khscodes.infrastructure.mailserver;
oauthConfigFile = "/run/secret/dovecot/dovecot-oauth2.conf.ext";
in
{
config = {
khscodes.infrastructure.kanidm-client-application = {
enable = true;
appName = "dovecot";
secretOwner = "root";
perms = "0644";
};
config = lib.mkIf cfg.enable {
khscodes.services.vault-agent.templates = [
{
contents = ''
@ -28,17 +23,6 @@ in
restartUnits = [ "dovecot2.service" ];
}
];
services.roundcube.extraConfig = ''
$config['oauth_provider'] = 'generic';
$config['oauth_provider_name'] = 'Kanidm';
$config['oauth_client_id'] = 'dovecot';
$config['oauth_client_secret'] = file_get_contents("${config.khscodes.infrastructure.kanidm-client-application.secretFile}");
$config['oauth_auth_uri'] = 'https://login.kaareskovgaard.net/ui/oauth2';
$config['oauth_token_uri'] = 'https://login.kaareskovgaard.net/oauth2/token';
$config['oauth_identity_uri'] = 'https://login.kaareskovgaard.net/oauth2/openid/dovecot/userinfo';
$config['oauth_identity_fields'] = ['preferred_username'];
$config['oauth_scope'] = 'email openid profile';
'';
services.dovecot2.extraConfig = ''
auth_mechanisms = $auth_mechanisms oauthbearer xoauth2

View file

@ -1,5 +1,9 @@
{ config, lib, ... }:
let
cfg = config.khscodes.infrastructure.mailserver;
in
{
config = {
config = lib.mkIf cfg.enable {
services.prometheus.exporters.postfix = {
enable = true;

View file

@ -1,9 +1,9 @@
{ config, lib, ... }:
let
cfg = config.khscodes."mx.kaareskovgaard.net";
cfg = config.khscodes.infrastructure.mailserver;
in
{
config = {
config = lib.mkIf cfg.enable {
khscodes.infrastructure.provisioning.pre.modules = [
{
khscodes.cloudflare.dns.txtRecords = lib.lists.map (domain: {

View file

@ -1,9 +1,9 @@
{ config, lib, ... }:
let
cfg = config.khscodes."mx.kaareskovgaard.net";
cfg = config.khscodes.infrastructure.mailserver;
in
{
config = {
config = lib.mkIf cfg.enable {
khscodes.infrastructure.provisioning.pre.modules = [
{
khscodes.cloudflare.dns.txtRecords = lib.lists.map (domain: {

View file

@ -1,46 +1,84 @@
{
inputs,
config,
...
}:
{
imports = [
"${inputs.self}/nix/profiles/nixos/hetzner-server.nix"
./mailserver
# ./testuser.nix
];
khscodes = {
infrastructure = {
hetzner-instance = {
enable = true;
mapRdns = true;
server_type = "cax11";
};
provisioning.pre.modules = [
(
{ ... }:
{
khscodes.vault = {
enable = true;
mount."mx.kaareskovgaard.net" = {
path = "mx.kaareskovgaard.net";
type = "kv";
options = {
version = "2";
};
description = "Secrets used for mx.kaareskovgaard.net";
};
khscodes.infrastructure.provisioning.pre.modules = [
(
{ ... }:
{
khscodes.vault = {
enable = true;
mount."mx.kaareskovgaard.net" = {
path = "mx.kaareskovgaard.net";
type = "kv";
options = {
version = "2";
};
}
)
];
description = "Secrets used for mx.kaareskovgaard.net";
};
};
}
)
];
khscodes.infrastructure = {
kanidm-client-application = {
enable = true;
appName = "dovecot";
secretOwner = "dovecot2";
perms = "0644";
};
"mx.kaareskovgaard.net" = {
hetzner-instance = {
enable = true;
mapRdns = true;
server_type = "cax11";
};
mailserver = {
enable = true;
domains = [
"agerlin-skovgaard.dk"
"agerlinskovgaard.dk"
];
accounts = import ./users.nix;
dkim = {
vault = {
mount = "mx.kaareskovgaard.net";
prefixPath = "dkim";
};
};
ldap = {
mount = "kanidm";
path = "ldap/dovecot";
};
};
};
services.roundcube = {
enable = true;
hostName = "mail.kaareskovgaard.net";
configureNginx = true;
extraConfig = ''
# starttls needed for authentication, so the fqdn required to match
# the certificate
$config['smtp_host'] = "ssl://${config.khscodes.networking.fqdn}";
$config['imap_host'] = "ssl://${config.khscodes.networking.fqdn}";
$config['oauth_provider'] = 'generic';
$config['oauth_provider_name'] = 'Kanidm';
$config['oauth_client_id'] = 'dovecot';
$config['oauth_client_secret'] = file_get_contents("${config.khscodes.infrastructure.kanidm-client-application.secretFile}");
$config['oauth_auth_uri'] = 'https://login.kaareskovgaard.net/ui/oauth2';
$config['oauth_token_uri'] = 'https://login.kaareskovgaard.net/oauth2/token';
$config['oauth_identity_uri'] = 'https://login.kaareskovgaard.net/oauth2/openid/dovecot/userinfo';
$config['oauth_identity_fields'] = ['preferred_username'];
$config['oauth_scope'] = 'email openid profile';
$config['plugins'] = [
'managesieve',
];
'';
};
khscodes.services.nginx = {
enable = true;
virtualHosts."mail.kaareskovgaard.net" = { };

View file

@ -1,244 +0,0 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.khscodes."mx.kaareskovgaard.net";
passDbFile = "/run/secret/dovecot/passwd";
bogusPasswdFile = pkgs.writeTextFile {
name = "bogus-passwd";
text = "$6$1234";
};
accountPrimaryEmail =
name: account: if account.isLdapAccount then lib.lists.head account.aliases else name;
accountAlternativeEmails =
name: account:
if account.isLdapAccount then
lib.lists.ifilter0 (idx: _: idx > 0) account.aliases
else
account.aliases;
accountOption = lib.khscodes.mkSubmodule {
description = "mail account";
options = {
name = lib.mkOption {
type = lib.types.str;
example = "user1@example.com";
description = "Username";
};
aliases = lib.mkOption {
type = lib.types.listOf lib.types.str;
example = [
"abuse@example.com"
"postmaster@example.com"
];
default = [ ];
description = ''
A list of aliases of this login account.
Note: Use list entries like "@example.com" to create a catchAll
that allows sending from all email addresses in these domain.
'';
};
aliasesRegexp = lib.mkOption {
type = lib.types.listOf lib.types.str;
example = [ ''/^tom\..*@domain\.com$/'' ];
default = [ ];
description = ''
Same as {option}`mailserver.aliases` but using PCRE (Perl compatible regex).
'';
};
catchAll = lib.mkOption {
type = lib.types.listOf (lib.types.enum config.mailserver.domains);
example = [
"example.com"
"example2.com"
];
default = [ ];
description = ''
For which domains should this account act as a catch all?
Note: Does not allow sending from all addresses of these domains.
'';
};
quota = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "2G";
description = ''
Per user quota rules. Accepted sizes are `xx k/M/G/T` with the
obvious meaning. Leave blank for the standard quota `100G`.
'';
};
sieveScript = lib.mkOption {
type = lib.types.nullOr lib.types.lines;
default = null;
example = ''
require ["fileinto", "mailbox"];
if address :is "from" "gitlab@mg.gitlab.com" {
fileinto :create "GitLab";
stop;
}
# This must be the last rule, it will check if list-id is set, and
# file the message into the Lists folder for further investigation
elsif header :matches "list-id" "<?*>" {
fileinto :create "Lists";
stop;
}
'';
description = ''
Per-user sieve script.
'';
};
sendOnly = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Specifies if the account should be a send-only account.
Emails sent to send-only accounts will be rejected from
unauthorized senders with the `sendOnlyRejectMessage`
stating the reason.
'';
};
sendOnlyRejectMessage = lib.mkOption {
type = lib.types.str;
default = "This account cannot receive emails.";
description = ''
The message that will be returned to the sender when an email is
sent to a send-only account. Only used if the account is marked
as send-only.
'';
};
isLdapAccount = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Marks the account as an LDAP account. Non LDAP accounts gets passwords provisioned, such that other services can use them";
};
};
};
systemAccounts = lib.attrsets.foldlAttrs (
acc: name: value:
if value.isLdapAccount then acc else acc ++ [ name ]
) [ ] cfg.accounts;
systemAccountsPassDbTemplateContents =
lib.concatStringsSep "\n" (
lib.lists.map (account: ''
{{- with secret "mx.kaareskovgaard.net/data/users/${account}" -}}
"${account}:{{ .Data.data.hashed_password }}::::::"
{{- end -}}
'') systemAccounts
)
# Just make sure the file is not empty
+ "\n";
# I would like to not use emails as usernames, but this is not something postfix is particularly happy with,
# as far as I can see. I *think* I can do this by randomly selecting an address, ie. the first, as the "primary"
# address, and then map all other addresses to that one. Then create a virtual mailbox map that maps that back
# to the username before delivering it to lmtp. All of this just worked when using ldap, but that meant
# not using code configured accounts set up with opentofu as well, and would also make the email delivery
# take a hard dependency on ldap (and not just the login process).
# Create extra virtual aliases for all accounts (as they are not emails, that maps them to their primary email).
# Then we map those emails back into the account name in the mailbox maps.
extraVirtualAliases = lib.mapAttrs (name: account: accountPrimaryEmail name account) cfg.accounts;
in
{
options.khscodes."mx.kaareskovgaard.net".accounts = lib.mkOption {
type = lib.types.attrsOf accountOption;
default = { };
};
config = {
mailserver = {
inherit extraVirtualAliases;
loginAccounts = lib.attrsets.mapAttrs (name: value: {
inherit (value)
name
aliasesRegexp
catchAll
quota
sieveScript
sendOnly
sendOnlyRejectMessage
;
aliases = accountAlternativeEmails name value;
hashedPasswordFile = bogusPasswdFile;
}) cfg.accounts;
};
khscodes.infrastructure.vault-server-approle.policy = {
"mx.kaareskovgaard.net/data/users/*" = {
capabilities = [ "read" ];
};
};
khscodes.infrastructure.provisioning.pre.modules = [
{
terraform.required_providers.random = {
source = "hashicorp/random";
version = "3.7.2";
};
provider.random = { };
}
]
++ (lib.lists.map (
account:
let
tfName = lib.khscodes.sanitize-terraform-name account;
in
{
resource.random_password."${tfName}" = {
length = 48;
numeric = true;
lower = true;
upper = true;
special = false;
};
resource.vault_kv_secret_v2."${tfName}" = {
mount = config.khscodes.vault.output.mount."mx.kaareskovgaard.net".path;
name = "users/${account}";
data_json = ''
{
"hashed_password": ''${ jsonencode(resource.random_password.${tfName}.bcrypt_hash) },
"password": ''${ jsonencode(resource.random_password.${tfName}.result) }
}
'';
};
}
) systemAccounts);
khscodes.services.vault-agent.templates = [
{
contents = systemAccountsPassDbTemplateContents;
destination = passDbFile;
perms = "0600";
owner = "dovecot2";
group = "dovecot2";
restartUnits = [
"dovecot2.service"
];
}
];
systemd.services.dovecot2 = {
unitConfig.ConditionPathExists = [ passDbFile ];
serviceConfig.ReadOnlyPaths = [ passDbFile ];
# simple-nixos-mailserver creates its own passwd file,
# but the passwords in that file are all bogus, so replace them
# with our own.
preStart = lib.mkAfter ''
cp ${passDbFile} /run/dovecot2/passwd
'';
};
# This prevents local usernames without domain names to get rewritten.
# services.postfix.submissionOptions = submissionOptions;
# services.postfix.submissionsOptions = submissionOptions;
};
}

View file

@ -1,6 +0,0 @@
{ config, ... }:
{
config = {
khscodes.services.nginx.virtualHosts."${config.khscodes.networking.fqdn}" = { };
};
}

View file

@ -1,74 +0,0 @@
{
pkgs,
...
}:
let
ldapMount = "kanidm";
ldapPath = "ldap/dovecot";
secretFile = "/run/secret/dovecot/dovecot-ldap.conf.ext";
in
{
config = {
services.dovecot2.extraConfig = ''
passdb {
driver = ldap
args = ${secretFile}
}
'';
systemd.services = {
dovecot2 = {
unitConfig = {
ConditionPathExists = [ secretFile ];
};
serviceConfig.ReadOnlyPaths = [
secretFile
];
};
};
khscodes.services.vault-agent.templates = [
{
contents = ''
{{- with secret "${ldapMount}/data/${ldapPath}" -}}
ldap_version = 3
uris = ldaps://login.kaareskovgaard.net
tls_require_cert = hard
tls_ca_cert_file = ${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt
dn = dn=token
dnpass = {{ .Data.data.apiToken }}
auth_bind = yes
base = dc=login,dc=kaareskovgaard,dc=net
scope = subtree
pass_attrs = uid=user
pass_filter = (&(class=account)(memberOf=mail_user)(uid=%u)
{{- end -}}
'';
destination = secretFile;
owner = "root";
group = "root";
restartUnits = [
"dovecot2.service"
];
}
];
# TODO: Include a similiar rule for openstack
khscodes.infrastructure.hetzner-instance.extraFirewallRules = [
{
direction = "out";
protocol = "tcp";
port = 636;
destination_ips = [
"0.0.0.0/0"
"::/0"
];
description = "ldaps";
}
];
khscodes.infrastructure.vault-server-approle.policy = {
"${ldapMount}/data/${ldapPath}" = {
capabilities = [ "read" ];
};
};
};
}

View file

@ -1,10 +0,0 @@
{
config = {
services.dovecot2.protocols = [ "sieve" ];
services.roundcube.extraConfig = ''
$config['plugins'] = [
'managesieve',
];
'';
};
}

View file

@ -1,18 +0,0 @@
{ config, ... }:
{
services.roundcube = {
enable = true;
hostName = "mail.kaareskovgaard.net";
configureNginx = true;
extraConfig = ''
# starttls needed for authentication, so the fqdn required to match
# the certificate
$config['smtp_host'] = "ssl://${config.khscodes.networking.fqdn}";
$config['imap_host'] = "ssl://${config.khscodes.networking.fqdn}";
'';
};
khscodes.services.nginx = {
enable = true;
virtualHosts."mail.kaareskovgaard.net" = { };
};
}

View file

@ -0,0 +1,71 @@
{
khscodes.infrastructure.vault-server-approle.policy = {
"mail.kaareskovgaard.net/data/users/*" = {
capabilities = [ "read" ];
};
};
khscodes.services.vault-agent.templates = [
{
contents = ''
{{- with secret "mail.kaareskovgaard.net/data/users/test" -}}
{{ .Data.data.hashed_password }}
{{- end -}}
'';
destination = "/run/secret/mailserver/users/test.passwd.hash";
perms = "0600";
owner = "rspamd";
group = "rspamd";
restartUnits = [
"rspamd.service"
"postfix.service"
];
}
];
systemd.services.postfix = {
unitConfig = {
ConditionPathExists = [ "/run/secret/mailserver/users/test.passwd.hash" ];
};
};
systemd.services.rspamd = {
unitConfig = {
ConditionPathExists = [ "/run/secret/mailserver/users/test.passwd.hash" ];
};
};
khscodes.infrastructure.provisioning.pre.modules = [
(
{ config, ... }:
{
terraform.required_providers.random = {
source = "hashicorp/random";
version = "3.7.2";
};
provider.random = { };
resource.random_password.test = {
length = 48;
numeric = true;
lower = true;
upper = true;
special = false;
};
resource.vault_kv_secret_v2.test = {
mount = config.khscodes.vault.output.mount."mail.kaareskovgaard.net".path;
name = "users/test";
data_json = ''
{
"hashed_password": ''${ jsonencode(resource.random_password.test.bcrypt_hash) },
"password": ''${ jsonencode(resource.random_password.test.result) }
}
'';
};
}
)
];
mailserver.loginAccounts = {
"test@agerlin-skovgaard.dk" = {
hashedPasswordFile = "/run/secret/mailserver/users/test.passwd.hash";
};
};
}

View file

@ -1,11 +0,0 @@
{
"khs" = {
isLdapAccount = true;
name = "Kaare Agerlin Skovgaard";
aliases = [
"kaare@agerlin-skovgaard.dk"
"kaare@agerlinskovgaard.dk"
];
quota = "10G";
};
}