nixos/postfix-tlspol: init (#415482)
* pkgs.formats.yaml_1_2: init Same as YAML 1.1 but relies on the unpinned remarshal version which emits YAML 1.2. * nixos/postfix-tlspol: init MTA-STS and DANE/TLSA resolver and TLS policy socketmap server for Postfix. * nixos/tests/postfix-tlspol: init Simple test if the service comes up and the CLI can interact with it and gives reasonable results.
This commit is contained in:
commit
1b59fd6732
@ -28,6 +28,8 @@
|
||||
|
||||
- [Draupnir](https://github.com/the-draupnir-project/draupnir), a Matrix moderation bot. Available as [services.draupnir](#opt-services.draupnir.enable).
|
||||
|
||||
- [postfix-tlspol](https://github.com/Zuplu/postfix-tlspol), MTA-STS and DANE resolver and TLS policy server for Postfix. Available as [services.postfix-tlspol](#opt-services.postfix-tlspol.enable).
|
||||
|
||||
- [SuiteNumérique Docs](https://github.com/suitenumerique/docs), a collaborative note taking, wiki and documentation web platform and alternative to Notion or Outline. Available as [services.lasuite-docs](#opt-services.lasuite-docs.enable).
|
||||
|
||||
[dwl](https://codeberg.org/dwl/dwl), a compact, hackable compositor for Wayland based on wlroots. Available as [programs.dwl](#opt-programs.dwl.enable).
|
||||
|
@ -739,6 +739,7 @@
|
||||
./services/mail/opendkim.nix
|
||||
./services/mail/opensmtpd.nix
|
||||
./services/mail/pfix-srsd.nix
|
||||
./services/mail/postfix-tlspol.nix
|
||||
./services/mail/postfix.nix
|
||||
./services/mail/postfixadmin.nix
|
||||
./services/mail/postgrey.nix
|
||||
|
220
nixos/modules/services/mail/postfix-tlspol.nix
Normal file
220
nixos/modules/services/mail/postfix-tlspol.nix
Normal file
@ -0,0 +1,220 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (lib)
|
||||
hasPrefix
|
||||
mkEnableOption
|
||||
mkIf
|
||||
mkOption
|
||||
mkPackageOption
|
||||
types
|
||||
;
|
||||
|
||||
cfg = config.services.postfix-tlspol;
|
||||
|
||||
format = pkgs.formats.yaml_1_2 { };
|
||||
in
|
||||
|
||||
{
|
||||
options.services.postfix-tlspol = {
|
||||
enable = mkEnableOption "postfix-tlspol";
|
||||
|
||||
package = mkPackageOption pkgs "postfix-tlspol" { };
|
||||
|
||||
settings = mkOption {
|
||||
type = types.submodule {
|
||||
freeformType = format.type;
|
||||
options = {
|
||||
server = {
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
default = "unix:/run/postfix-tlspol/tlspol.sock";
|
||||
example = "127.0.0.1:8642";
|
||||
description = ''
|
||||
Path or address/port where postfix-tlspol binds its socket to.
|
||||
'';
|
||||
};
|
||||
|
||||
socket-permissions = mkOption {
|
||||
type = types.str;
|
||||
default = "0660";
|
||||
readOnly = true;
|
||||
description = ''
|
||||
Permissions to the UNIX socket, if configured.
|
||||
|
||||
::: {.note}
|
||||
Due to hardening on the systemd unit the socket can never be created world readable/writable.
|
||||
:::
|
||||
'';
|
||||
apply = value: (builtins.fromTOML "v=0o${value}").v;
|
||||
};
|
||||
|
||||
log-level = mkOption {
|
||||
type = types.enum [
|
||||
"debug"
|
||||
"info"
|
||||
"warn"
|
||||
"error"
|
||||
];
|
||||
default = "info";
|
||||
example = "warn";
|
||||
description = ''
|
||||
Log level
|
||||
'';
|
||||
};
|
||||
|
||||
prefetch = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
example = false;
|
||||
description = ''
|
||||
Whether to prefetch DNS records when the TTL of a cached record is about to expire.
|
||||
'';
|
||||
};
|
||||
|
||||
cache-file = mkOption {
|
||||
type = types.path;
|
||||
default = "/var/cache/postfix-tlspol/cache.db";
|
||||
readOnly = true;
|
||||
description = ''
|
||||
Path to the cache file.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
dns = {
|
||||
server = mkOption {
|
||||
type = types.str;
|
||||
default = "127.0.0.1:53";
|
||||
description = ''
|
||||
IP and port to your DNS resolver
|
||||
|
||||
::: {.note}
|
||||
The configured DNS resolver must validate DNSSEC signatures.
|
||||
:::
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
default = { };
|
||||
description = ''
|
||||
The postfix-tlspol configuration file as a Nix attribute set.
|
||||
|
||||
See the reference documentation for possible options.
|
||||
<https://github.com/Zuplu/postfix-tlspol/blob/main/configs/config.default.yaml>
|
||||
'';
|
||||
};
|
||||
|
||||
configurePostfix = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to configure the required settings to use postfix-tlspol in the local Postfix instance.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
environment.etc."postfix-tlspol/config.yaml".source =
|
||||
format.generate "postfix-tlspol.yaml" cfg.settings;
|
||||
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
|
||||
# https://github.com/Zuplu/postfix-tlspol#postfix-configuration
|
||||
services.postfix.config = mkIf (config.services.postfix.enable && cfg.configurePostfix) {
|
||||
smtp_dns_support_level = "dnssec";
|
||||
smtp_tls_security_level = "dane";
|
||||
smtp_tls_policy_maps =
|
||||
let
|
||||
address =
|
||||
if (hasPrefix "unix:" cfg.settings.server.address) then
|
||||
cfg.settings.server.address
|
||||
else
|
||||
"inet:${cfg.settings.server.address}";
|
||||
in
|
||||
[ "socketmap:${address}:QUERYwithTLSRPT" ];
|
||||
};
|
||||
|
||||
systemd.services.postfix-tlspol = {
|
||||
after = [
|
||||
"nss-lookup.target"
|
||||
"network-online.target"
|
||||
];
|
||||
wants = [
|
||||
"nss-lookup.target"
|
||||
"network-online.target"
|
||||
];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
description = "Postfix DANE/MTA-STS TLS policy socketmap service";
|
||||
documentation = [ "https://github.com/Zuplu/postfix-tlspol" ];
|
||||
|
||||
# https://github.com/Zuplu/postfix-tlspol/blob/main/init/postfix-tlspol.service
|
||||
serviceConfig = {
|
||||
ExecStart = toString [
|
||||
(lib.getExe cfg.package)
|
||||
"-config"
|
||||
"/etc/postfix-tlspol/config.yaml"
|
||||
];
|
||||
ExecReload = "${lib.getExe' pkgs.util-linux "kill"} -HUP $MAINPID";
|
||||
Restart = "always";
|
||||
RestartSec = 5;
|
||||
|
||||
DynamicUser = true;
|
||||
|
||||
CacheDirectory = "postfix-tlspol";
|
||||
CapabilityBoundingSet = [ "" ];
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
NoNewPrivileges = true;
|
||||
PrivateDevices = true;
|
||||
PrivateTmp = true;
|
||||
PrivateUsers = true;
|
||||
ProcSubset = "pid";
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHome = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectProc = "invisible";
|
||||
ProtectSystem = "strict";
|
||||
ReadOnlyPaths = [ "/etc/postfix-tlspol/config.yaml" ];
|
||||
RemoveIPC = true;
|
||||
RestrictAddressFamilies =
|
||||
[
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
]
|
||||
++ lib.optionals (lib.hasPrefix "unix:" cfg.settings.server.address) [
|
||||
"AF_UNIX"
|
||||
];
|
||||
RestrictNamespace = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallFilter = [
|
||||
"@system-service"
|
||||
"~@privileged @resources"
|
||||
];
|
||||
SystemCallErrorNumber = "EPERM";
|
||||
SecureBits = [
|
||||
"noroot"
|
||||
"noroot-locked"
|
||||
];
|
||||
RuntimeDirectory = "postfix-tlspol";
|
||||
RuntimeDirectoryMode = "1750";
|
||||
WorkingDirectory = "/var/cache/postfix-tlspol";
|
||||
UMask = "0117";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
@ -1103,6 +1103,7 @@ in
|
||||
postfix-raise-smtpd-tls-security-level =
|
||||
handleTest ./postfix-raise-smtpd-tls-security-level.nix
|
||||
{ };
|
||||
postfix-tlspol = runTest ./postfix-tlspol.nix;
|
||||
postfixadmin = runTest ./postfixadmin.nix;
|
||||
postgres-websockets = runTest ./postgres-websockets.nix;
|
||||
postgresql = handleTest ./postgresql { };
|
||||
|
29
nixos/tests/postfix-tlspol.nix
Normal file
29
nixos/tests/postfix-tlspol.nix
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
name = "postfix-tlspol";
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ hexa ];
|
||||
|
||||
nodes.machine = {
|
||||
services.postfix-tlspol.enable = true;
|
||||
};
|
||||
|
||||
enableOCR = true;
|
||||
|
||||
testScript = ''
|
||||
import json
|
||||
|
||||
machine.wait_for_unit("postfix-tlspol.service")
|
||||
|
||||
with subtest("Interact with the service"):
|
||||
machine.succeed("postfix-tlspol -purge")
|
||||
|
||||
response = json.loads((machine.succeed("postfix-tlspol -query localhost")))
|
||||
machine.log(json.dumps(response, indent=2))
|
||||
|
||||
'';
|
||||
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
lib,
|
||||
buildGoModule,
|
||||
fetchFromGitHub,
|
||||
nixosTests,
|
||||
}:
|
||||
|
||||
buildGoModule rec {
|
||||
@ -22,6 +23,10 @@ buildGoModule rec {
|
||||
|
||||
ldflags = [ "-X main.Version=${version}" ];
|
||||
|
||||
passthru.tests = {
|
||||
inherit (nixosTests) postfix-tlspol;
|
||||
};
|
||||
|
||||
meta = {
|
||||
description = "Lightweight MTA-STS + DANE/TLSA resolver and TLS policy server for Postfix, prioritizing DANE.";
|
||||
homepage = "https://github.com/Zuplu/postfix-tlspol";
|
||||
|
@ -192,7 +192,46 @@ optionalAttrs allowAliases aliases
|
||||
(listOf valueType)
|
||||
])
|
||||
// {
|
||||
description = "YAML value";
|
||||
description = "YAML 1.1 value";
|
||||
};
|
||||
in
|
||||
valueType;
|
||||
|
||||
};
|
||||
|
||||
yaml_1_2 =
|
||||
{ }:
|
||||
{
|
||||
generate =
|
||||
name: value:
|
||||
pkgs.callPackage (
|
||||
{ runCommand, remarshal }:
|
||||
runCommand name
|
||||
{
|
||||
nativeBuildInputs = [ remarshal ];
|
||||
value = builtins.toJSON value;
|
||||
passAsFile = [ "value" ];
|
||||
preferLocalBuild = true;
|
||||
}
|
||||
''
|
||||
json2yaml "$valuePath" "$out"
|
||||
''
|
||||
) { };
|
||||
|
||||
type =
|
||||
let
|
||||
valueType =
|
||||
nullOr (oneOf [
|
||||
bool
|
||||
int
|
||||
float
|
||||
str
|
||||
path
|
||||
(attrsOf valueType)
|
||||
(listOf valueType)
|
||||
])
|
||||
// {
|
||||
description = "YAML 1.2 value";
|
||||
};
|
||||
in
|
||||
valueType;
|
||||
|
@ -143,7 +143,7 @@ runBuildTests {
|
||||
};
|
||||
|
||||
yaml_1_1Atoms = shouldPass {
|
||||
format = formats.yaml { };
|
||||
format = formats.yaml_1_1 { };
|
||||
input = {
|
||||
null = null;
|
||||
false = false;
|
||||
@ -176,6 +176,40 @@ runBuildTests {
|
||||
'';
|
||||
};
|
||||
|
||||
yaml_1_2Atoms = shouldPass {
|
||||
format = formats.yaml_1_2 { };
|
||||
input = {
|
||||
null = null;
|
||||
false = false;
|
||||
true = true;
|
||||
float = 3.141;
|
||||
str = "foo";
|
||||
attrs.foo = null;
|
||||
list = [
|
||||
null
|
||||
null
|
||||
];
|
||||
path = ./testfile;
|
||||
no = "no";
|
||||
time = "22:30:00";
|
||||
};
|
||||
expected = ''
|
||||
attrs:
|
||||
foo: null
|
||||
'false': false
|
||||
float: 3.141
|
||||
list:
|
||||
- null
|
||||
- null
|
||||
no: no
|
||||
'null': null
|
||||
path: ${./testfile}
|
||||
str: foo
|
||||
time: 22:30:00
|
||||
'true': true
|
||||
'';
|
||||
};
|
||||
|
||||
iniAtoms = shouldPass {
|
||||
format = formats.ini { };
|
||||
input = {
|
||||
|
Loading…
x
Reference in New Issue
Block a user