gerd.starwart: fixed bug in fallback password + added custom patch
This commit is contained in:
parent
0a8f393f9d
commit
2f46a5197f
5 changed files with 108 additions and 12 deletions
5
machines/gerd/services/stalwart/default.nix
Normal file
5
machines/gerd/services/stalwart/default.nix
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
imports = [
|
||||
./stalwart.nix
|
||||
];
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
diff --git a/crates/cli/src/modules/cli.rs b/crates/cli/src/modules/cli.rs
|
||||
index 865b7c8e..f30ee8a8 100644
|
||||
--- a/crates/cli/src/modules/cli.rs
|
||||
+++ b/crates/cli/src/modules/cli.rs
|
||||
@@ -330,6 +330,12 @@ pub enum DomainCommands {
|
||||
name: String,
|
||||
},
|
||||
|
||||
+ /// List DNS records for domain
|
||||
+ DNSRecords {
|
||||
+ /// Domain name to list DNS records for
|
||||
+ name: String,
|
||||
+ },
|
||||
+
|
||||
/// List all domains
|
||||
List {
|
||||
/// Starting point for listing domains
|
||||
diff --git a/crates/cli/src/modules/domain.rs b/crates/cli/src/modules/domain.rs
|
||||
index bc0dd898..462e0251 100644
|
||||
--- a/crates/cli/src/modules/domain.rs
|
||||
+++ b/crates/cli/src/modules/domain.rs
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
-use prettytable::{Attr, Cell, Row, Table};
|
||||
+use prettytable::{Attr, Cell, Row, Table, format};
|
||||
use reqwest::Method;
|
||||
use serde_json::Value;
|
||||
|
||||
@@ -14,6 +14,15 @@ use crate::modules::List;
|
||||
|
||||
use super::cli::{Client, DomainCommands};
|
||||
|
||||
+use serde::{Deserialize, Serialize};
|
||||
+#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
+struct DnsRecord {
|
||||
+ #[serde(rename = "type")]
|
||||
+ typ: String,
|
||||
+ name: String,
|
||||
+ content: String,
|
||||
+}
|
||||
+
|
||||
impl DomainCommands {
|
||||
pub async fn exec(self, client: Client) {
|
||||
match self {
|
||||
@@ -37,6 +46,39 @@ impl DomainCommands {
|
||||
.await;
|
||||
eprintln!("Successfully deleted domain {name:?}");
|
||||
}
|
||||
+ DomainCommands::DNSRecords { name } => {
|
||||
+ let records = client
|
||||
+ .http_request::<Vec<DnsRecord>, String>(
|
||||
+ Method::GET,
|
||||
+ &format!("/api/domain/{name}"),
|
||||
+ None,
|
||||
+ )
|
||||
+ .await;
|
||||
+
|
||||
+ if !records.is_empty() {
|
||||
+ let mut table = Table::new();
|
||||
+ // no borderline separator, as long values will mess it up
|
||||
+ table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
|
||||
+
|
||||
+ table.add_row(Row::new(vec![
|
||||
+ Cell::new("Type").with_style(Attr::Bold),
|
||||
+ Cell::new("Name").with_style(Attr::Bold),
|
||||
+ Cell::new("Contents").with_style(Attr::Bold),
|
||||
+ ]));
|
||||
+
|
||||
+ for record in &records {
|
||||
+ table.add_row(Row::new(vec![
|
||||
+ Cell::new(&record.typ),
|
||||
+ Cell::new(&record.name),
|
||||
+ Cell::new(&record.content),
|
||||
+ ]));
|
||||
+ }
|
||||
+
|
||||
+ eprintln!();
|
||||
+ table.printstd();
|
||||
+ eprintln!();
|
||||
+ }
|
||||
+ }
|
||||
DomainCommands::List { from, limit } => {
|
||||
let query = if from.is_none() && limit.is_none() {
|
||||
Cow::Borrowed("/api/domain")
|
147
machines/gerd/services/stalwart/stalwart.nix
Normal file
147
machines/gerd/services/stalwart/stalwart.nix
Normal file
|
@ -0,0 +1,147 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
svc_domain = "mail.${config.mine.shared.settings.domain}";
|
||||
svc_domain_smtp = "smtp.${config.mine.shared.settings.domain}";
|
||||
svc_domain_imap = "imap.${config.mine.shared.settings.domain}";
|
||||
|
||||
ports = {
|
||||
smtp = 25;
|
||||
submissions = 465;
|
||||
imaptls = 993;
|
||||
|
||||
http_management = 7377;
|
||||
};
|
||||
|
||||
# place data into own zfs dataset
|
||||
stateDir = config.mine.zfsMounts."rpool/safe/svcs/stalwart";
|
||||
|
||||
# user/group
|
||||
stalwart_user = config.users.users.stalwart-mail.name;
|
||||
stalwart_group = config.users.groups.stalwart-mail.name;
|
||||
|
||||
certLocation = config.security.acme.certs."${svc_domain}".directory;
|
||||
in {
|
||||
services.stalwart-mail = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
|
||||
package = pkgs.stalwart-mail.overrideAttrs (old: {
|
||||
patches = old.patches ++ [
|
||||
./patches/stalwart-cli-dns-records.patch
|
||||
];
|
||||
});
|
||||
|
||||
settings = {
|
||||
lookup.default.hostname = svc_domain;
|
||||
|
||||
store.db.path = "${stateDir}/db";
|
||||
|
||||
directory.ldap = {
|
||||
type = "ldap";
|
||||
url = config.mine.shared.settings.ldap.url;
|
||||
base-dn = config.mine.shared.settings.ldap.dc;
|
||||
tls.enable = false;
|
||||
|
||||
bind = {
|
||||
dn = config.mine.shared.settings.ldap.bind_dn;
|
||||
secret = "%{file:${config.age.secrets.lldap-bind-user-pass.path}}%";
|
||||
|
||||
auth = {
|
||||
enable = true;
|
||||
dn = "cn=?,${config.mine.shared.settings.ldap.search_base}";
|
||||
};
|
||||
};
|
||||
|
||||
filter = let
|
||||
_mkFilter = attrs: ph: config.mine.shared.lib.ldap.mkFilter (lconfig: llib:
|
||||
llib.mkAnd [
|
||||
(llib.mkGroup lconfig.groups.member)
|
||||
(llib.mkOr (lib.forEach attrs (v: llib.mkSearch v ph)))
|
||||
]
|
||||
);
|
||||
|
||||
attrs = config.mine.shared.settings.ldap.attr // { emailAlias = "mailAlias"; emailList = "mailList"; };
|
||||
in {
|
||||
name = _mkFilter [ attrs.uid ] "?";
|
||||
email = _mkFilter [ attrs.email attrs.emailAlias attrs.emailList ] "?";
|
||||
verify = _mkFilter [ attrs.email attrs.emailAlias ] "*?*";
|
||||
expand = _mkFilter [ attrs.emailList ] "?";
|
||||
domains = _mkFilter [ attrs.email attrs.emailAlias ] "*@?";
|
||||
};
|
||||
|
||||
attributes = {
|
||||
name = "uid";
|
||||
class = "objectClass";
|
||||
description = "givenName";
|
||||
secret = "uid";
|
||||
groups = "memberOf";
|
||||
email = "mail";
|
||||
# email-alias = "mailAlias";
|
||||
# quota = "diskQuota";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
storage.directory = "ldap";
|
||||
|
||||
# listeners
|
||||
server.listener = {
|
||||
smtp = { bind = [ "[::]:${builtins.toString ports.smtp}"]; protocol = "smtp"; };
|
||||
submissions = { bind = [ "[::]:${builtins.toString ports.submissions}"]; protocol = "smtp"; tls.implicit = true; };
|
||||
imaptls = { bind = [ "[::]:${builtins.toString ports.imaptls}"]; protocol = "imap"; tls.implicit = true; };
|
||||
|
||||
management = { bind = [ "127.0.0.1:${builtins.toString ports.http_management}" ]; protocol = "http"; };
|
||||
};
|
||||
|
||||
certificate.domain = {
|
||||
cert = "%{file:${certLocation + "/cert.pem"}}%";
|
||||
private-key = "%{file:${certLocation + "/key.pem"}}%";
|
||||
};
|
||||
|
||||
server.tls = {
|
||||
certificate = "domain";
|
||||
enable = true;
|
||||
implicit = true;
|
||||
};
|
||||
|
||||
# authentication
|
||||
authentication.fallback-admin = {
|
||||
user = "admin";
|
||||
secret = "%{file:${config.age.secrets.stalwart-admin-fallback-password.path}}%";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# setup so that stalwart can access and write to the directory
|
||||
systemd.services.stalwart-mail.serviceConfig.ReadWritePaths = [ stateDir ];
|
||||
systemd.tmpfiles.rules = [
|
||||
"Z ${stateDir} 0700 ${stalwart_user} ${stalwart_group} -"
|
||||
];
|
||||
|
||||
# setup certs
|
||||
services.nginx.virtualHosts."${svc_domain}" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
serverAliases = [ svc_domain_smtp svc_domain_imap ];
|
||||
root = pkgs.writeTextDir "index.html" "Nothing.";
|
||||
};
|
||||
|
||||
# need to change group to stalwart-mail for cert + add nginx to stalwart-mail group to do HTTP ACME
|
||||
users.users.nginx.extraGroups = [ stalwart_group ];
|
||||
security.acme.certs."${svc_domain}".group = stalwart_group;
|
||||
|
||||
|
||||
# setup secrets for stalwart
|
||||
# setup access to ldap bind user credential
|
||||
users.groups.secrets-lldap-bind-user-pass.members = [ stalwart_user ];
|
||||
age.secrets.stalwart-admin-fallback-password.owner = stalwart_user;
|
||||
|
||||
mine.shared.settings.mail = {
|
||||
domain = svc_domain;
|
||||
domain_smtp = svc_domain_smtp;
|
||||
domain_imap = svc_domain_imap;
|
||||
|
||||
ports = ports;
|
||||
};
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue