feat: stalwart mail-server
This commit is contained in:
parent
85feeb7b3f
commit
a565033341
16 changed files with 1192 additions and 442 deletions
|
|
@ -8,6 +8,7 @@ creation_rules:
|
|||
key_groups:
|
||||
- age:
|
||||
- *dn_server
|
||||
- *dn_pre7780
|
||||
- path_regex: system/dev/dn-pre7780/secret.yaml
|
||||
key_groups:
|
||||
- age:
|
||||
|
|
|
|||
113
flake.lock
generated
113
flake.lock
generated
|
|
@ -491,6 +491,24 @@
|
|||
}
|
||||
},
|
||||
"flake-parts_4": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": "nixpkgs-lib_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1733312601,
|
||||
"narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts_5": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
"nvf",
|
||||
|
|
@ -511,7 +529,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts_5": {
|
||||
"flake-parts_6": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
"stylix",
|
||||
|
|
@ -532,6 +550,21 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-root": {
|
||||
"locked": {
|
||||
"lastModified": 1723604017,
|
||||
"narHash": "sha256-rBtQ8gg+Dn4Sx/s+pvjdq3CB2wQNzx9XGFq/JVGCB6k=",
|
||||
"owner": "srid",
|
||||
"repo": "flake-root",
|
||||
"rev": "b759a56851e10cb13f6b8e5698af7b59c44be26e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "srid",
|
||||
"repo": "flake-root",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-schemas": {
|
||||
"locked": {
|
||||
"lastModified": 1721999734,
|
||||
|
|
@ -1405,6 +1438,22 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"marks-nvim": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1747179163,
|
||||
"narHash": "sha256-ho2b2Ulh+GTqY0QvW7zjFOSlF5g/kaxWyOjKWhTFq7c=",
|
||||
"owner": "chentoast",
|
||||
"repo": "marks.nvim",
|
||||
"rev": "f353e8c08c50f39e99a9ed474172df7eddd89b72",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "chentoast",
|
||||
"repo": "marks.nvim",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"microvm": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils_4",
|
||||
|
|
@ -1563,6 +1612,29 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixd": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts_4",
|
||||
"flake-root": "flake-root",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"treefmt-nix": "treefmt-nix_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1756563652,
|
||||
"narHash": "sha256-0MvTa6l071JAbePgP3qTkNXr1CbeGDmqyDyvVHxetqg=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixd",
|
||||
"rev": "15a3376f65de9e7984429b975777f3569430b8a6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nixd",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1755027561,
|
||||
|
|
@ -1594,6 +1666,18 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib_2": {
|
||||
"locked": {
|
||||
"lastModified": 1733096140,
|
||||
"narHash": "sha256-1qRH7uAUsyQI7R1Uwl4T+XvdNv778H0Nb5njNrqvylY=",
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/NixOS/nixpkgs/archive/5487e69da40cbd611ab2cadee0b4637225f7cfae.tar.gz"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/NixOS/nixpkgs/archive/5487e69da40cbd611ab2cadee0b4637225f7cfae.tar.gz"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1730741070,
|
||||
|
|
@ -1795,7 +1879,7 @@
|
|||
"nvf": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat_8",
|
||||
"flake-parts": "flake-parts_4",
|
||||
"flake-parts": "flake-parts_5",
|
||||
"mnw": "mnw",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
|
|
@ -1903,11 +1987,13 @@
|
|||
"hyprlock": "hyprlock",
|
||||
"hyprtasking": "hyprtasking",
|
||||
"lanzaboote": "lanzaboote",
|
||||
"marks-nvim": "marks-nvim",
|
||||
"microvm": "microvm",
|
||||
"neovim-nightly-overlay": "neovim-nightly-overlay",
|
||||
"nix-index-database": "nix-index-database",
|
||||
"nix-minecraft": "nix-minecraft",
|
||||
"nix-tmodloader": "nix-tmodloader",
|
||||
"nixd": "nixd",
|
||||
"nixpkgs": "nixpkgs_7",
|
||||
"nvf": "nvf",
|
||||
"sops-nix": "sops-nix",
|
||||
|
|
@ -2042,7 +2128,7 @@
|
|||
"base16-helix": "base16-helix",
|
||||
"base16-vim": "base16-vim",
|
||||
"firefox-gnome-theme": "firefox-gnome-theme",
|
||||
"flake-parts": "flake-parts_5",
|
||||
"flake-parts": "flake-parts_6",
|
||||
"gnome-shell": "gnome-shell",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
|
|
@ -2371,6 +2457,27 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"treefmt-nix_2": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixd",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1734704479,
|
||||
"narHash": "sha256-MMi74+WckoyEWBRcg/oaGRvXC9BVVxDZNRMpL+72wBI=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "65712f5af67234dad91a5a4baee986a8b62dbf8f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"xdph": {
|
||||
"inputs": {
|
||||
"hyprland-protocols": [
|
||||
|
|
|
|||
45
flake.nix
45
flake.nix
|
|
@ -117,16 +117,28 @@
|
|||
url = "github:NotAShelf/nvf";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
nixd = {
|
||||
url = "github:nix-community/nixd";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
outputs = {
|
||||
marks-nvim = {
|
||||
url = "github:chentoast/marks.nvim";
|
||||
flake = false;
|
||||
};
|
||||
};
|
||||
|
||||
outputs =
|
||||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
nix-index-database,
|
||||
lanzaboote,
|
||||
home-manager,
|
||||
...
|
||||
} @ inputs: let
|
||||
}@inputs:
|
||||
let
|
||||
system = "x86_64-linux";
|
||||
nix-version = "25.05";
|
||||
|
||||
|
|
@ -251,19 +263,17 @@
|
|||
];
|
||||
};
|
||||
};
|
||||
in {
|
||||
in
|
||||
{
|
||||
nixosConfigurations =
|
||||
(builtins.mapAttrs (
|
||||
dev: conf: let
|
||||
domain =
|
||||
if conf.domain != null
|
||||
then conf.domain
|
||||
else "local";
|
||||
dev: conf:
|
||||
let
|
||||
domain = if conf.domain != null then conf.domain else "local";
|
||||
inherit (conf) username hostname;
|
||||
in
|
||||
nixpkgs.lib.nixosSystem {
|
||||
modules =
|
||||
[
|
||||
modules = [
|
||||
{
|
||||
system.stateVersion = nix-version;
|
||||
home-manager = {
|
||||
|
|
@ -308,24 +318,24 @@
|
|||
]
|
||||
++ common-settings.modules
|
||||
++ conf.extra-modules;
|
||||
specialArgs =
|
||||
{
|
||||
specialArgs = {
|
||||
inherit username;
|
||||
}
|
||||
// common-settings.args;
|
||||
}
|
||||
)
|
||||
devices)
|
||||
) devices)
|
||||
//
|
||||
# VM For k8s
|
||||
(
|
||||
let
|
||||
vmList = let
|
||||
vmList =
|
||||
let
|
||||
kubeMasterIP = "192.168.0.6";
|
||||
kubeMasterHostname = "api.kube";
|
||||
kubeMasterAPIServerPort = 6443;
|
||||
kubeApi = "https://${kubeMasterHostname}:${toString kubeMasterAPIServerPort}";
|
||||
in {
|
||||
in
|
||||
{
|
||||
# master
|
||||
vm-1 = {
|
||||
ip = "192.168.0.6";
|
||||
|
|
@ -479,8 +489,7 @@
|
|||
];
|
||||
}
|
||||
)
|
||||
)
|
||||
vmList
|
||||
) vmList
|
||||
)
|
||||
// {
|
||||
vps = nixpkgs.lib.nixosSystem {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,11 @@
|
|||
pkgs,
|
||||
lib,
|
||||
osConfig,
|
||||
inputs,
|
||||
system,
|
||||
...
|
||||
}: let
|
||||
}:
|
||||
let
|
||||
inherit (lib.generators) mkLuaInline;
|
||||
|
||||
suda-nvim = pkgs.vimUtils.buildVimPlugin {
|
||||
|
|
@ -15,7 +18,13 @@
|
|||
hash = "sha256-46sy3rAdOCULVt1RkIoGdweoV3MqQaB33Et9MrxI6Lk=";
|
||||
};
|
||||
};
|
||||
in {
|
||||
|
||||
marks-nvim = pkgs.vimUtils.buildVimPlugin {
|
||||
name = "marks-nvim";
|
||||
src = inputs.marks-nvim;
|
||||
};
|
||||
in
|
||||
{
|
||||
programs.nvf = {
|
||||
enable = true;
|
||||
settings = {
|
||||
|
|
@ -69,6 +78,12 @@ in {
|
|||
suda = {
|
||||
package = suda-nvim;
|
||||
};
|
||||
marks = {
|
||||
package = marks-nvim;
|
||||
setup = ''
|
||||
require("marks").setup {}
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
keymaps = [
|
||||
|
|
@ -81,7 +96,7 @@ in {
|
|||
silent = true;
|
||||
desc = "Toggle file explorer";
|
||||
}
|
||||
# Fzf lua
|
||||
# === Fzf lua === #
|
||||
{
|
||||
key = "<Leader><Space>";
|
||||
silent = true;
|
||||
|
|
@ -119,6 +134,20 @@ in {
|
|||
nowait = true;
|
||||
desc = "Find symbols (workspace)";
|
||||
}
|
||||
# Registers
|
||||
{
|
||||
key = ''""'';
|
||||
mode = [ "n" ];
|
||||
action = ":FzfLua registers<CR>";
|
||||
desc = "Registers";
|
||||
}
|
||||
# Marks
|
||||
{
|
||||
key = "''";
|
||||
mode = [ "n" ];
|
||||
action = ":FzfLua marks<CR>";
|
||||
desc = "Marks";
|
||||
}
|
||||
|
||||
# === Buffer === #
|
||||
{
|
||||
|
|
@ -427,17 +456,21 @@ in {
|
|||
};
|
||||
nix = {
|
||||
enable = true;
|
||||
extraDiagnostics.enable = false;
|
||||
format.type = "nixfmt";
|
||||
lsp = {
|
||||
server = "nixd";
|
||||
server = "nil";
|
||||
options = {
|
||||
nixos.expr =
|
||||
# nix
|
||||
''(builtins.getFlake (builtins.toString ./.)).nixosConfigurations.${osConfig.networking.hostName}.options'';
|
||||
home_manager.expr =
|
||||
# nix
|
||||
''(builtins.getFlake (builtins.toString ./.)).nixosConfigurations.${osConfig.networking.hostName}.options.home-manager.users.type.getSubOptions []'';
|
||||
nix.flake.autoArchive = true;
|
||||
};
|
||||
# options = {
|
||||
# nixos.expr =
|
||||
# # nix
|
||||
# ''(builtins.getFlake (builtins.toString ./.)).nixosConfigurations.${osConfig.networking.hostName}.options'';
|
||||
# home_manager.expr =
|
||||
# # nix
|
||||
# ''(builtins.getFlake (builtins.toString ./.)).nixosConfigurations.${osConfig.networking.hostName}.options.home-manager.users.type.getSubOptions []'';
|
||||
# };
|
||||
};
|
||||
};
|
||||
sql.enable = true;
|
||||
|
|
@ -450,7 +483,12 @@ in {
|
|||
};
|
||||
};
|
||||
python.enable = true;
|
||||
markdown.enable = true;
|
||||
markdown = {
|
||||
enable = true;
|
||||
extensions = {
|
||||
render-markdown-nvim.enable = true;
|
||||
};
|
||||
};
|
||||
html.enable = true;
|
||||
lua.enable = true;
|
||||
};
|
||||
|
|
@ -716,7 +754,85 @@ in {
|
|||
whichKey.enable = true;
|
||||
};
|
||||
|
||||
fzf-lua.enable = true;
|
||||
fzf-lua = {
|
||||
enable = true;
|
||||
setupOpts = {
|
||||
previewers = {
|
||||
builtin = {
|
||||
extensions = {
|
||||
"jpg" = {
|
||||
"kitty" = "";
|
||||
};
|
||||
};
|
||||
snacks_image = {
|
||||
enabled = false;
|
||||
render_inline = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
winopts = {
|
||||
preview = {
|
||||
hidden = "hidden";
|
||||
};
|
||||
border = "rounded";
|
||||
};
|
||||
fzf_opts = {
|
||||
"--no-header" = "";
|
||||
"--no-scrollbar" = "";
|
||||
};
|
||||
files = {
|
||||
formatter = "path.filename_first";
|
||||
prompt = ":";
|
||||
no_header = true;
|
||||
cwd_header = false;
|
||||
cwd_prompt = false;
|
||||
winopts = {
|
||||
title = " files 📑 ";
|
||||
title_pos = "center";
|
||||
title_flags = false;
|
||||
};
|
||||
};
|
||||
buffers = {
|
||||
formatter = "path.filename_first";
|
||||
prompt = ":";
|
||||
no_header = true;
|
||||
fzf_opts = {
|
||||
"--delimiter" = " ";
|
||||
"--with-nth" = "-1..";
|
||||
};
|
||||
winopts = {
|
||||
title = " buffers 📝 ";
|
||||
title_pos = "center";
|
||||
};
|
||||
};
|
||||
lsp = {
|
||||
symbols = {
|
||||
cwd_only = true;
|
||||
no_header = true;
|
||||
prompt = ":";
|
||||
winopts = {
|
||||
title = " symbols ✨ ";
|
||||
title_pos = "center";
|
||||
height = 0.6;
|
||||
preview = {
|
||||
hidden = "nohidden";
|
||||
horizontal = "down:40%";
|
||||
wrap = "wrap";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
registers = {
|
||||
prompt = "registers:";
|
||||
filter = "%a";
|
||||
winopts = {
|
||||
title = " registers 🏷️ ";
|
||||
title_pos = "center";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
dashboard = {
|
||||
alpha.enable = true;
|
||||
|
|
@ -745,7 +861,12 @@ in {
|
|||
};
|
||||
|
||||
images = {
|
||||
img-clip.enable = true;
|
||||
image-nvim = {
|
||||
enable = true;
|
||||
setupOpts = {
|
||||
backend = "kitty";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
[
|
||||
(import ./vesktop.nix)
|
||||
(import ./powerdns-admin.nix)
|
||||
(import ./stalwart-mail)
|
||||
]
|
||||
|
|
|
|||
7
pkgs/overlays/stalwart-mail/default.nix
Normal file
7
pkgs/overlays/stalwart-mail/default.nix
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
final: prev: {
|
||||
stalwart-mail = prev.stalwart-mail.overrideAttrs (oldAttrs: {
|
||||
patches = [
|
||||
./enable_root_ca.patch
|
||||
];
|
||||
});
|
||||
}
|
||||
291
pkgs/overlays/stalwart-mail/enable_root_ca.patch
Normal file
291
pkgs/overlays/stalwart-mail/enable_root_ca.patch
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
diff --git a/crates/common/src/config/server/tls.rs b/crates/common/src/config/server/tls.rs
|
||||
index 603cf889..58e38c84 100644
|
||||
--- a/crates/common/src/config/server/tls.rs
|
||||
+++ b/crates/common/src/config/server/tls.rs
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
use std::{
|
||||
- io::Cursor,
|
||||
+ io::{Cursor, Read},
|
||||
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
@@ -35,7 +35,8 @@ use x509_parser::{
|
||||
|
||||
use crate::listener::{
|
||||
acme::{
|
||||
- AcmeProvider, ChallengeSettings, EabSettings, directory::LETS_ENCRYPT_PRODUCTION_DIRECTORY,
|
||||
+ AcmeProvider, ChallengeSettings, EabSettings,
|
||||
+ directory::{CABundle, LETS_ENCRYPT_PRODUCTION_DIRECTORY},
|
||||
},
|
||||
tls::AcmeProviders,
|
||||
};
|
||||
@@ -66,6 +67,31 @@ impl AcmeProviders {
|
||||
.property_or_default(("acme", acme_id, "renew-before"), "30d")
|
||||
.unwrap_or_else(|| Duration::from_secs(30 * 24 * 60 * 60));
|
||||
|
||||
+ let ca_bundle: CABundle = config
|
||||
+ .value(("acme", acme_id, "ca_bundle"))
|
||||
+ .map(|s| s.to_string())
|
||||
+ .and_then(|path| {
|
||||
+ let buf = std::fs::read(&path)
|
||||
+ .map_err(|err| {
|
||||
+ config.new_parse_error(
|
||||
+ format!("acme.{acme_id}.ca_bundle"),
|
||||
+ err.to_string(),
|
||||
+ );
|
||||
+ })
|
||||
+ .ok()?;
|
||||
+
|
||||
+ reqwest::Certificate::from_pem_bundle(&buf)
|
||||
+ .map_err(|err| {
|
||||
+ config.new_parse_error(
|
||||
+ format!("acme.{acme_id}.ca_bundle"),
|
||||
+ err.to_string(),
|
||||
+ );
|
||||
+
|
||||
+ err
|
||||
+ })
|
||||
+ .ok()
|
||||
+ });
|
||||
+
|
||||
if directory.is_empty() {
|
||||
config.new_parse_error(format!("acme.{acme_id}.directory"), "Missing property");
|
||||
continue;
|
||||
@@ -167,6 +193,7 @@ impl AcmeProviders {
|
||||
contact,
|
||||
challenge,
|
||||
eab,
|
||||
+ ca_bundle,
|
||||
renew_before,
|
||||
default,
|
||||
) {
|
||||
@@ -304,7 +331,7 @@ fn build_dns_updater(config: &mut Config, acme_id: &str) -> Option<DnsUpdater> {
|
||||
.map_err(|err| {
|
||||
config.new_build_error(
|
||||
("acme", acme_id, "provider"),
|
||||
- format!("Failed to create Desec DNS updater: {err}"),
|
||||
+ format!("Failed to create OVH DNS updater: {err}"),
|
||||
)
|
||||
})
|
||||
.ok(),
|
||||
diff --git a/crates/common/src/listener/acme/directory.rs b/crates/common/src/listener/acme/directory.rs
|
||||
index 21640cd6..bbefd7aa 100644
|
||||
--- a/crates/common/src/listener/acme/directory.rs
|
||||
+++ b/crates/common/src/listener/acme/directory.rs
|
||||
@@ -20,6 +20,8 @@ use store::write::Archiver;
|
||||
use trc::AddContext;
|
||||
use trc::event::conv::AssertSuccess;
|
||||
|
||||
+pub type CABundle = Option<Vec<reqwest::Certificate>>;
|
||||
+
|
||||
pub const LETS_ENCRYPT_STAGING_DIRECTORY: &str =
|
||||
"https://acme-staging-v02.api.letsencrypt.org/directory";
|
||||
pub const LETS_ENCRYPT_PRODUCTION_DIRECTORY: &str =
|
||||
@@ -31,6 +33,7 @@ pub struct Account {
|
||||
pub key_pair: EcdsaKeyPair,
|
||||
pub directory: Directory,
|
||||
pub kid: String,
|
||||
+ pub ca_bundle: CABundle,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
@@ -89,16 +92,23 @@ impl Account {
|
||||
let body = sign(
|
||||
&key_pair,
|
||||
None,
|
||||
- directory.nonce().await?,
|
||||
+ directory.nonce(&provider.ca_bundle).await?,
|
||||
&directory.new_account,
|
||||
&payload,
|
||||
)?;
|
||||
- let response = https(&directory.new_account, Method::POST, Some(body)).await?;
|
||||
+ let response = https(
|
||||
+ &directory.new_account,
|
||||
+ Method::POST,
|
||||
+ Some(body),
|
||||
+ &provider.ca_bundle,
|
||||
+ )
|
||||
+ .await?;
|
||||
let kid = get_header(&response, "Location")?;
|
||||
Ok(Account {
|
||||
key_pair,
|
||||
kid,
|
||||
directory,
|
||||
+ ca_bundle: provider.ca_bundle.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -106,15 +116,16 @@ impl Account {
|
||||
&self,
|
||||
url: impl AsRef<str>,
|
||||
payload: &str,
|
||||
+ ca_bundle: &CABundle,
|
||||
) -> trc::Result<(Option<String>, String)> {
|
||||
let body = sign(
|
||||
&self.key_pair,
|
||||
Some(&self.kid),
|
||||
- self.directory.nonce().await?,
|
||||
+ self.directory.nonce(ca_bundle).await?,
|
||||
url.as_ref(),
|
||||
payload,
|
||||
)?;
|
||||
- let response = https(url.as_ref(), Method::POST, Some(body)).await?;
|
||||
+ let response = https(url.as_ref(), Method::POST, Some(body), ca_bundle).await?;
|
||||
let location = get_header(&response, "Location").ok();
|
||||
let body = response
|
||||
.text()
|
||||
@@ -130,7 +141,9 @@ impl Account {
|
||||
serde_json::to_string(&domains)
|
||||
.map_err(|err| trc::EventType::Acme(trc::AcmeEvent::Error).from_json_error(err))?
|
||||
);
|
||||
- let response = self.request(&self.directory.new_order, &payload).await?;
|
||||
+ let response = self
|
||||
+ .request(&self.directory.new_order, &payload, &self.ca_bundle)
|
||||
+ .await?;
|
||||
let url = response.0.ok_or(
|
||||
trc::EventType::Acme(trc::AcmeEvent::Error)
|
||||
.caused_by(trc::location!())
|
||||
@@ -143,30 +156,30 @@ impl Account {
|
||||
}
|
||||
|
||||
pub async fn auth(&self, url: impl AsRef<str>) -> trc::Result<Auth> {
|
||||
- let response = self.request(url, "").await?;
|
||||
+ let response = self.request(url, "", &self.ca_bundle).await?;
|
||||
serde_json::from_str(&response.1)
|
||||
.map_err(|err| trc::EventType::Acme(trc::AcmeEvent::Error).from_json_error(err))
|
||||
}
|
||||
|
||||
pub async fn challenge(&self, url: impl AsRef<str>) -> trc::Result<()> {
|
||||
- self.request(&url, "{}").await.map(|_| ())
|
||||
+ self.request(&url, "{}", &self.ca_bundle).await.map(|_| ())
|
||||
}
|
||||
|
||||
pub async fn order(&self, url: impl AsRef<str>) -> trc::Result<Order> {
|
||||
- let response = self.request(&url, "").await?;
|
||||
+ let response = self.request(&url, "", &self.ca_bundle).await?;
|
||||
serde_json::from_str(&response.1)
|
||||
.map_err(|err| trc::EventType::Acme(trc::AcmeEvent::Error).from_json_error(err))
|
||||
}
|
||||
|
||||
pub async fn finalize(&self, url: impl AsRef<str>, csr: Vec<u8>) -> trc::Result<Order> {
|
||||
let payload = format!("{{\"csr\":\"{}\"}}", URL_SAFE_NO_PAD.encode(csr));
|
||||
- let response = self.request(&url, &payload).await?;
|
||||
+ let response = self.request(&url, &payload, &self.ca_bundle).await?;
|
||||
serde_json::from_str(&response.1)
|
||||
.map_err(|err| trc::EventType::Acme(trc::AcmeEvent::Error).from_json_error(err))
|
||||
}
|
||||
|
||||
pub async fn certificate(&self, url: impl AsRef<str>) -> trc::Result<String> {
|
||||
- Ok(self.request(&url, "").await?.1)
|
||||
+ Ok(self.request(&url, "", &self.ca_bundle).await?.1)
|
||||
}
|
||||
|
||||
pub fn http_proof(&self, challenge: &Challenge) -> trc::Result<Vec<u8>> {
|
||||
@@ -218,9 +231,9 @@ pub struct Directory {
|
||||
}
|
||||
|
||||
impl Directory {
|
||||
- pub async fn discover(url: impl AsRef<str>) -> trc::Result<Self> {
|
||||
+ pub async fn discover(url: impl AsRef<str>, ca_bundle: &CABundle) -> trc::Result<Self> {
|
||||
serde_json::from_str(
|
||||
- &https(url, Method::GET, None)
|
||||
+ &https(url, Method::GET, None, ca_bundle)
|
||||
.await?
|
||||
.text()
|
||||
.await
|
||||
@@ -228,9 +241,9 @@ impl Directory {
|
||||
)
|
||||
.map_err(|err| trc::EventType::Acme(trc::AcmeEvent::Error).from_json_error(err))
|
||||
}
|
||||
- pub async fn nonce(&self) -> trc::Result<String> {
|
||||
+ pub async fn nonce(&self, ca_bundle: &CABundle) -> trc::Result<String> {
|
||||
get_header(
|
||||
- &https(&self.new_nonce.as_str(), Method::HEAD, None).await?,
|
||||
+ &https(&self.new_nonce.as_str(), Method::HEAD, None, ca_bundle).await?,
|
||||
"replay-nonce",
|
||||
)
|
||||
}
|
||||
@@ -316,6 +329,7 @@ async fn https(
|
||||
url: impl AsRef<str>,
|
||||
method: Method,
|
||||
body: Option<String>,
|
||||
+ ca_bundle: &CABundle,
|
||||
) -> trc::Result<Response> {
|
||||
let url = url.as_ref();
|
||||
let mut builder = reqwest::Client::builder()
|
||||
@@ -329,6 +343,15 @@ async fn https(
|
||||
);
|
||||
}
|
||||
|
||||
+ match ca_bundle {
|
||||
+ Some(certs) => {
|
||||
+ for cert in certs {
|
||||
+ builder = builder.add_root_certificate(cert.clone());
|
||||
+ }
|
||||
+ }
|
||||
+ None => {}
|
||||
+ };
|
||||
+
|
||||
let mut request = builder
|
||||
.build()
|
||||
.map_err(|err| trc::EventType::Acme(trc::AcmeEvent::Error).from_http_error(err))?
|
||||
diff --git a/crates/common/src/listener/acme/mod.rs b/crates/common/src/listener/acme/mod.rs
|
||||
index a6d9b978..2674dd3a 100644
|
||||
--- a/crates/common/src/listener/acme/mod.rs
|
||||
+++ b/crates/common/src/listener/acme/mod.rs
|
||||
@@ -16,7 +16,7 @@ use arc_swap::ArcSwap;
|
||||
use dns_update::DnsUpdater;
|
||||
use rustls::sign::CertifiedKey;
|
||||
|
||||
-use crate::Server;
|
||||
+use crate::{Server, listener::acme::directory::CABundle};
|
||||
|
||||
use self::directory::{Account, ChallengeType};
|
||||
|
||||
@@ -27,6 +27,7 @@ pub struct AcmeProvider {
|
||||
pub contact: Vec<String>,
|
||||
pub challenge: ChallengeSettings,
|
||||
pub eab: Option<EabSettings>,
|
||||
+ pub ca_bundle: CABundle,
|
||||
renew_before: chrono::Duration,
|
||||
account_key: ArcSwap<Vec<u8>>,
|
||||
default: bool,
|
||||
@@ -64,6 +65,7 @@ impl AcmeProvider {
|
||||
contact: Vec<String>,
|
||||
challenge: ChallengeSettings,
|
||||
eab: Option<EabSettings>,
|
||||
+ ca_bundle: CABundle,
|
||||
renew_before: Duration,
|
||||
default: bool,
|
||||
) -> trc::Result<Self> {
|
||||
@@ -85,6 +87,7 @@ impl AcmeProvider {
|
||||
account_key: Default::default(),
|
||||
challenge,
|
||||
eab,
|
||||
+ ca_bundle,
|
||||
default,
|
||||
})
|
||||
}
|
||||
@@ -153,6 +156,7 @@ impl Clone for AcmeProvider {
|
||||
renew_before: self.renew_before,
|
||||
account_key: ArcSwap::from_pointee(self.account_key.load().as_ref().clone()),
|
||||
eab: self.eab.clone(),
|
||||
+ ca_bundle: self.ca_bundle.clone(),
|
||||
default: self.default,
|
||||
}
|
||||
}
|
||||
diff --git a/crates/common/src/listener/acme/order.rs b/crates/common/src/listener/acme/order.rs
|
||||
index 6def86a4..8ba3b185 100644
|
||||
--- a/crates/common/src/listener/acme/order.rs
|
||||
+++ b/crates/common/src/listener/acme/order.rs
|
||||
@@ -85,7 +85,7 @@ impl Server {
|
||||
}
|
||||
|
||||
async fn order(&self, provider: &AcmeProvider) -> trc::Result<Vec<u8>> {
|
||||
- let directory = Directory::discover(&provider.directory_url).await?;
|
||||
+ let directory = Directory::discover(&provider.directory_url, &provider.ca_bundle).await?;
|
||||
let account = Account::create_with_keypair(directory, provider).await?;
|
||||
|
||||
let mut params = CertificateParams::new(provider.domains.clone());
|
||||
|
|
@ -59,6 +59,33 @@ in
|
|||
];
|
||||
})
|
||||
|
||||
(import ../../modules/stalwart.nix {
|
||||
enableNginx = true;
|
||||
domain = "pre7780.dn";
|
||||
adminPassFile = config.sops.secrets."stalwart/adminPassword".path;
|
||||
dbPassFile = config.sops.secrets."stalwart/db".path;
|
||||
acmeConf = {
|
||||
directory = "https://ca.net.dn/acme/acme/directory";
|
||||
ca_bundle = "${"" + ../../extra/ca.crt}";
|
||||
challenge = "dns-01";
|
||||
origin = "pre7780.dn";
|
||||
contact = "admin@pre7780.dn";
|
||||
domains = [
|
||||
"pre7780.dn"
|
||||
"mx1.pre7780.dn"
|
||||
];
|
||||
default = true;
|
||||
provider = "rfc2136-tsig";
|
||||
host = "10.0.0.1";
|
||||
renew-before = "1d";
|
||||
port = 5359;
|
||||
cache = "${config.services.stalwart-mail.dataDir}/acme";
|
||||
key = "stalwart";
|
||||
tsig-algorithm = "hmac-sha512";
|
||||
secret = "%{file:${config.sops.secrets."stalwart/tsig".path}}%";
|
||||
};
|
||||
})
|
||||
|
||||
../../modules/davinci-resolve.nix
|
||||
../../modules/webcam.nix
|
||||
../../modules/postgresql.nix
|
||||
|
|
|
|||
|
|
@ -1,13 +1,20 @@
|
|||
{ ... }:
|
||||
{ config, ... }:
|
||||
{
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
443
|
||||
80
|
||||
];
|
||||
|
||||
security.acme = {
|
||||
acceptTerms = true;
|
||||
defaults = {
|
||||
validMinDays = 2;
|
||||
server = "https://10.0.0.1:${toString 8443}/acme/acme/directory";
|
||||
server = "https://ca.net.dn/acme/acme/directory";
|
||||
renewInterval = "daily";
|
||||
email = "danny@net.dn";
|
||||
webroot = "/var/lib/acme/acme-challenge";
|
||||
dnsProvider = "pdns";
|
||||
dnsPropagationCheck = false;
|
||||
environmentFile = config.sops.secrets."acme/pdns".path;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,12 @@ openldap:
|
|||
adminPassword: ENC[AES256_GCM,data:jEGuzgs5QTWfdyJenC3t3g==,iv:StfFOcvbDapnma6eAlpaGiBWnqiD3I/wfQsMBzufol0=,tag:892q7N4KrsSQoZYGy6CQrA==,type:str]
|
||||
lam:
|
||||
env: ENC[AES256_GCM,data:f1LlC/VvilH8o2Ra7MrSHsMEGlGw3LOV2O9JJf9f,iv:u7cXM8n3jJeLBfxXtA0QMyijBqTcC+yJeW/OO9JuZMI=,tag:QL5FkcCPI5Gxudi0NmCZWg==,type:str]
|
||||
stalwart:
|
||||
adminPassword: ENC[AES256_GCM,data:6tUL7b2s3gLtF4Ors9CgYQ==,iv:9UQowgXKr9HR/poELP6SZijp3c2HVTHzEfwf1tZI/3w=,tag:KIOiYEwLsZLH31E2Xb478A==,type:str]
|
||||
tsig: ENC[AES256_GCM,data:wxsM/dbkW2fNf86b6TsLRNAce19h7mBEuSzFT84aIlaVZA/S29g1U4/CAwD4b+h/XfBgpZQCJf/9yT3yo6dbGAIAk5UgjV2cNY9pO1/uF1T6xoKDgfRZxA==,iv:9BvP8vQkTTEaNgYUPfQcfEMcWqDyD045EPBr7NyHmO4=,tag:coBBAe62kpe/L0S6V8NhXg==,type:str]
|
||||
db: ENC[AES256_GCM,data:ZRZ2ZzUotYMe2GfkMS7o7dz0aGg=,iv:ys6ogueueESp0y6A+hUG9zTnqmCVobuIzyqA4WVtewo=,tag:p74G+8XhMcpgDnIfh1aXTg==,type:str]
|
||||
acme:
|
||||
pdns: ENC[AES256_GCM,data:+InGSnaGIFVtDRlVltzWbZfquzodHUQrPeMRBnVNB9mrajlKr5dFK6DD8dXAvN7UjZFBfrgZefOPkmLR2ncLXGOV2Kl7jorVw50Y0f0iKl7mqwHaZKaQdk7cpGDkCrt/LvfbP9x7gVrs6pQpsU+c/P5rbBLRyejchh/WtiyzgowYIJxYohggeG09+l7YI3FR6U5wiymIRISpNBGEhwG0q17qdAhdtc49qP/K,iv:JcSlxAwHwU528S7iSpAnSbUZw7iO+LMjR3qGwRHp+Zk=,tag:twf2WOQb/yZ3GtN/hlikMA==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- recipient: age1uvsvf5ljaezh5wze32p685kfentyle0l2mvysc67yvgct2h4850qqph9lv
|
||||
|
|
@ -19,7 +25,7 @@ sops:
|
|||
MEdmWkFwNXZoR1ZVRnQ0aWlkYzZwSmsK0EFecUIdqlDKX08oRCoDQQ3QCX1wzb8w
|
||||
lghDJhWlfuKr+X24GoE4UK04aJVLqVMRRI4BJW+LQXeHS+dWKu3mQA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2025-06-15T08:18:50Z"
|
||||
mac: ENC[AES256_GCM,data:sq+/fpOeNO5wn9S1kFqzRy6xCOVkSBcAkral7MTn4UxRebBDa78KF76Nsba0+o5bzwCchoGl/TC6vySIzGq8FUYwd1tQ9nH5DlqYBVVRgRlKLRyhxXf14BTyYgzHzFuRWdFyY8i4j0flZtlDHk4dVQrE4OhHvhLQ2Zvet5HQ20I=,iv:qoPZ+8tAHJxcR53M2PNwukYgdguSRrAVB+FtKYbf+aM=,tag:FYaPzh6o0ZI27Ul5jEhgVg==,type:str]
|
||||
lastmodified: "2025-09-16T04:39:12Z"
|
||||
mac: ENC[AES256_GCM,data:yRVAJz73AqlBm6fxeTehfSqlTLyRYIsPjC/5igpnGC8URUiK66SUtHJSE3196AaPV+CWJrxrXfNWoCmZsP85Rr5V9nw31ZF1boaAc0YzRQBxVmBBlAK7+9Z5KADShAetYNwk9qtCrXd6S8mCwmZjNJaN/Rthy3hchxzAB0/79R4=,iv:QeNUZfmnCx4QF/0wjU/JJRu6umNFC/weW2BJx+7OaPo=,tag:KsityLnPYhugFL4c6wrs6Q==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.10.2
|
||||
|
|
|
|||
|
|
@ -15,6 +15,36 @@
|
|||
group = config.services.dovecot2.group;
|
||||
mode = "0660";
|
||||
};
|
||||
|
||||
"stalwart/adminPassword" =
|
||||
let
|
||||
inherit (config.users.users.stalwart-mail) name group;
|
||||
in
|
||||
lib.mkIf config.services.stalwart-mail.enable {
|
||||
inherit group;
|
||||
owner = name;
|
||||
};
|
||||
"stalwart/tsig" =
|
||||
let
|
||||
inherit (config.users.users.stalwart-mail) name group;
|
||||
in
|
||||
lib.mkIf config.services.stalwart-mail.enable {
|
||||
inherit group;
|
||||
owner = name;
|
||||
};
|
||||
"stalwart/db" =
|
||||
let
|
||||
inherit (config.users.users.stalwart-mail) name group;
|
||||
in
|
||||
lib.mkIf config.services.stalwart-mail.enable {
|
||||
inherit group;
|
||||
owner = name;
|
||||
};
|
||||
"acme/pdns" = {
|
||||
mode = "0660";
|
||||
owner = "acme";
|
||||
group = "acme";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,14 @@
|
|||
|
||||
locations."/".proxyPass = "http://10.0.0.130:8001/phone.html";
|
||||
};
|
||||
|
||||
"ca.net.dn" = {
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
locations."/" = {
|
||||
proxyPass = "https://10.0.0.1:8443/";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,11 +33,20 @@ sops:
|
|||
- recipient: age1z6f643a6vqm7cqh6fna5dhmxfkgwxgqy8kg9s0vf9uxhaswtngtspmqsjw
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBuKzJObXlPVUJzUkEyZXlV
|
||||
Q0tEbzBPTy9kUXIwVmJkckUyWklUMzhCcTE0Ckh3bXIwRkpESTJYeTBPMGhQYk9y
|
||||
L2NQTWFuMWVqYzJHZGhTaHpDRE5CRGMKLS0tIEsybHdPMk9JeEM2cXFwdlpOeXRj
|
||||
Qm0wbmNGZDZwZlNTOVl0WVh5RXNxK2cK1Fwbgl5kKAFyrIIhBP+X4ZKFS4Xl39QY
|
||||
11qkglNgro/JBFJ/W7Hj5wtEd8QToiJM1RW0lQaI25sneQ2v6L5pDA==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBuYWpiZ0h3VURrcW0rV0Vj
|
||||
SFJwMlRUMHUyS1FGTEo3cHZJc1Z6a3FWbmtRCkdoZXhwOGJQNlV2dU8wRFRMUHVv
|
||||
QzhxU3RiVHl5UVpUNk10S2VRVy95OHMKLS0tIE9zbUNUU3ZINU1JNGtmd2trS2tI
|
||||
d3YxREtHcTBJYU1sNU9vMGZTUGh6NXMKtGKMnnamCAeftkQ0+Ygb/yg1NdyKDz1W
|
||||
UjYvW2PYKzkx8IWmIgzdAI3fWDOiE7tmBTMlX9C3/2PKR6dCc/a+SQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1uvsvf5ljaezh5wze32p685kfentyle0l2mvysc67yvgct2h4850qqph9lv
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxc3pna3R3aG85bmt2WERa
|
||||
aG9TaDBKTlNMTUVwaFlIdkV0UmFJQStYSHdvCmNuYWJpN2M3QjRkV2s0MHJ4TzZP
|
||||
ZkhKc0xPUFBrblVFR1U4SUdjYzQ2cm8KLS0tIDVuNW9tRGoxanVKOUJYa2QwNFNz
|
||||
OTRiU0cxeXp5K1FjaWRGTnBHcnpUYmcKVVlueEj/DELe9Xi9iaBddpPPRmoUmD48
|
||||
wyjtlvKzS20zishE/D7GkHZ2ZdNsLD3AOnYZ6r6ATAndssC2YT/SXA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2025-08-29T07:40:00Z"
|
||||
mac: ENC[AES256_GCM,data:QeQ5NOrcq3uNmt+MiVF+Jr3JWWBNGPw5A8pSdd1WR426WWqHTRP7NHAaVbS3st9VSmoYY5NI6JKeizuAq/NCvzOZL3Idy9mP+3HD9VZwn1GNSEGfhn+KZT02AY0JHq29KxcZlAYiWZOL4p+blG2aWfGm9+zy1GHoEXoo3OVhaEg=,iv:8uIoOE0ZJZYGZoQaskCXQKr7vl6wjsmJ4iudhvtgqtY=,tag:fRLGalP92dDyF8q+zT97BQ==,type:str]
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
username,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
inherit username;
|
||||
|
||||
|
|
@ -13,10 +12,10 @@ let
|
|||
sshPortsString = builtins.concatStringsSep ", " (builtins.map (p: builtins.toString p) sshPorts);
|
||||
|
||||
personal = {
|
||||
inherit (config.networking) domain;
|
||||
ip = "10.0.0.1/24";
|
||||
interface = "wg0";
|
||||
port = 51820;
|
||||
domain = config.networking.domain;
|
||||
range = "10.0.0.0/24";
|
||||
full = "10.0.0.1/25";
|
||||
restrict = "10.0.0.128/25";
|
||||
|
|
@ -160,11 +159,13 @@ in
|
|||
kube.port
|
||||
25565
|
||||
kube.masterAPIServerPort
|
||||
5359
|
||||
];
|
||||
allowedTCPPorts = sshPorts ++ [
|
||||
53
|
||||
25565
|
||||
kube.masterAPIServerPort
|
||||
5359
|
||||
];
|
||||
};
|
||||
|
||||
|
|
@ -237,8 +238,7 @@ in
|
|||
listenPort = personal.port;
|
||||
privateKeyFile = config.sops.secrets."wireguard/privateKey".path;
|
||||
peers = builtins.map (r: {
|
||||
publicKey = r.publicKey;
|
||||
allowedIPs = r.allowedIPs;
|
||||
inherit (r) publicKey allowedIPs;
|
||||
}) (fullRoute ++ meshRoute);
|
||||
};
|
||||
|
||||
|
|
@ -254,7 +254,11 @@ in
|
|||
extraHosts = "${kube.masterIP} ${kube.masterHostname}";
|
||||
};
|
||||
|
||||
services.postgresql = {
|
||||
services = {
|
||||
dbus.enable = true;
|
||||
blueman.enable = true;
|
||||
|
||||
postgresql = {
|
||||
enable = lib.mkDefault true;
|
||||
authentication = ''
|
||||
host powerdnsadmin powerdnsadmin 127.0.0.1/32 trust
|
||||
|
|
@ -275,10 +279,6 @@ in
|
|||
];
|
||||
};
|
||||
|
||||
services = {
|
||||
dbus.enable = true;
|
||||
blueman.enable = true;
|
||||
|
||||
openssh = {
|
||||
enable = true;
|
||||
ports = sshPorts;
|
||||
|
|
@ -293,6 +293,7 @@ in
|
|||
enable = true;
|
||||
extraConfig = ''
|
||||
launch=gpgsql
|
||||
loglevel=6
|
||||
webserver-password=$WEB_PASSWORD
|
||||
api=yes
|
||||
api-key=$WEB_PASSWORD
|
||||
|
|
@ -302,6 +303,8 @@ in
|
|||
webserver=yes
|
||||
webserver-port=8081
|
||||
local-port=5359
|
||||
dnsupdate=yes
|
||||
allow-dnsupdate-from=10.0.0.0/24
|
||||
'';
|
||||
secretFile = config.sops.secrets.powerdns.path;
|
||||
};
|
||||
|
|
@ -310,6 +313,7 @@ in
|
|||
enable = true;
|
||||
forwardZones = {
|
||||
"${config.networking.domain}." = "127.0.0.1:5359";
|
||||
"pre7780.dn." = "127.0.0.1:5359";
|
||||
};
|
||||
forwardZonesRecurse = {
|
||||
"." = "8.8.8.8";
|
||||
|
|
@ -380,11 +384,16 @@ in
|
|||
};
|
||||
};
|
||||
|
||||
systemd.services.raspamd-trainer = {
|
||||
after = [ "pdns-recursor.service" ];
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts = {
|
||||
"powerdns.${config.networking.domain}" = {
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
locations."/".proxyPass = "http://localhost:8000";
|
||||
locations."/api".proxyPass = "http://127.0.0.1:8081";
|
||||
locations."/".proxyPass = "http://127.0.0.1:8000";
|
||||
};
|
||||
|
||||
"uptime.${config.networking.domain}" = {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ in
|
|||
postRun = ''
|
||||
systemctl restart postfix.service
|
||||
systemctl restart dovecot.service
|
||||
systemctl restart rspamd-trainer.service
|
||||
'';
|
||||
};
|
||||
"${cfg.domain}" = {
|
||||
|
|
|
|||
118
system/modules/stalwart.nix
Normal file
118
system/modules/stalwart.nix
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
{
|
||||
adminPassFile,
|
||||
dbPassFile,
|
||||
domain ? null,
|
||||
acmeConf ? null,
|
||||
enableNginx ? true,
|
||||
}:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib) mkIf;
|
||||
in
|
||||
{
|
||||
services.postgresql = {
|
||||
enable = true;
|
||||
ensureDatabases = [
|
||||
"stalwart"
|
||||
];
|
||||
ensureUsers = [
|
||||
{
|
||||
name = "stalwart";
|
||||
ensureDBOwnership = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
services.stalwart-mail = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
settings = {
|
||||
server = {
|
||||
hostname = if (domain != null) then "mx1.${domain}" else config.networking.fqdn;
|
||||
auto-ban.scan.rate = "1000/1d";
|
||||
tls = {
|
||||
enable = true;
|
||||
implicit = true;
|
||||
};
|
||||
listener = {
|
||||
smtp = {
|
||||
protocol = "smtp";
|
||||
bind = "[::]:25";
|
||||
};
|
||||
submissions = {
|
||||
protocol = "smtp";
|
||||
bind = "[::]:465";
|
||||
tls.implicit = true;
|
||||
};
|
||||
imaps = {
|
||||
protocol = "imap";
|
||||
bind = "[::]:993";
|
||||
tls.implicit = true;
|
||||
};
|
||||
management = {
|
||||
protocol = "http";
|
||||
bind = [ "127.0.0.1:8080" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
lookup.default = {
|
||||
hostname = "mx1.${domain}";
|
||||
domain = "${domain}";
|
||||
};
|
||||
acme."step-ca" = mkIf (acmeConf != null) acmeConf;
|
||||
session.auth = {
|
||||
mechanisms = "[plain]";
|
||||
directory = "'in-memory'";
|
||||
require = true;
|
||||
allow-plain-text = true;
|
||||
};
|
||||
storage.data = "db";
|
||||
store."db" = {
|
||||
type = "postgresql";
|
||||
host = "localhost";
|
||||
port = 5432;
|
||||
database = "stalwart";
|
||||
user = "stalwart";
|
||||
password = "%{file:${dbPassFile}}%";
|
||||
};
|
||||
directory = {
|
||||
"imap".lookup.domains = [ domain ];
|
||||
"in-memory" = {
|
||||
type = "memory";
|
||||
principals = [
|
||||
{
|
||||
name = "admin";
|
||||
class = "admin";
|
||||
secret = "%{file:${adminPassFile}}%";
|
||||
email = [ "admin@${domain}" ];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
authentication.fallback-admin = {
|
||||
user = "admin";
|
||||
secret = "%{file:${adminPassFile}}%";
|
||||
};
|
||||
tracer."stdout" = {
|
||||
enable = true;
|
||||
type = "console";
|
||||
level = "debug";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.nginx = mkIf enableNginx {
|
||||
enable = true;
|
||||
virtualHosts = {
|
||||
"mail.${domain}" = {
|
||||
locations."/".proxyPass = "http://127.0.0.1:8080";
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue