PoC working SP module system

+ simple-nixos-mailserver as an SP module
pull/55/head
Alexander Tomokhov 2023-11-10 07:10:06 +04:00
parent d9e2311f3c
commit 80ba7d0bda
25 changed files with 356 additions and 181 deletions

View File

@ -1,4 +1,4 @@
{ config, lib, pkgs, selfprivacy-graphql-api, ... }:
selfprivacy-graphql-api: { config, lib, pkgs, ... }:
with lib;

View File

@ -2,8 +2,8 @@
{
services.selfprivacy-api = {
enable = true;
enableSwagger = config.services.userdata.api.enableSwagger;
b2Bucket = config.services.userdata.backup.bucket;
enableSwagger = config.selfprivacy.userdata.api.enableSwagger;
b2Bucket = config.selfprivacy.userdata.backup.bucket;
};
users.users."selfprivacy-api" = {

View File

@ -1,6 +1,6 @@
{ config, pkgs, ... }:
{ config, ... }:
let
cfg = config.services.userdata;
cfg = config.selfprivacy.userdata;
in
{
services.restic.backups = {

View File

@ -1,15 +1,12 @@
{ userdata, config, pkgs, lib, ... }:
{ config, pkgs, lib, ... }:
{
imports = [
./variables-module.nix
./variables.nix
./files.nix
./volumes.nix
./users.nix
./mailserver/system/mailserver.nix
./vpn/ocserv.nix
./api/api.nix
./api/api-module.nix
./social/pleroma.nix
./letsencrypt/acme.nix
./letsencrypt/resolve.nix
@ -41,11 +38,11 @@
};
};
services.do-agent.enable = if config.services.userdata.server.provider == "DIGITALOCEAN" then true else false;
services.do-agent.enable = if config.selfprivacy.userdata.server.provider == "DIGITALOCEAN" then true else false;
boot.cleanTmpDir = true;
networking = {
hostName = config.services.userdata.hostname;
hostName = config.selfprivacy.userdata.hostname;
usePredictableInterfaceNames = false;
firewall = {
allowedTCPPorts = lib.mkForce [ 22 25 80 143 443 465 587 993 4443 8443 ];
@ -57,12 +54,12 @@
};
nameservers = [ "1.1.1.1" "1.0.0.1" ];
};
time.timeZone = config.services.userdata.timezone;
time.timeZone = config.selfprivacy.userdata.timezone;
i18n.defaultLocale = "en_GB.UTF-8";
users.users.root.openssh.authorizedKeys.keys = config.services.userdata.ssh.rootKeys;
users.users.root.openssh.authorizedKeys.keys = config.selfprivacy.userdata.ssh.rootKeys;
services.openssh = {
enable = config.services.userdata.ssh.enable;
passwordAuthentication = config.services.userdata.ssh.passwordAuthentication;
enable = config.selfprivacy.userdata.ssh.enable;
passwordAuthentication = config.selfprivacy.userdata.ssh.passwordAuthentication;
permitRootLogin = "yes";
openFirewall = false;
};
@ -75,14 +72,14 @@
jq
];
environment.variables = {
DOMAIN = config.services.userdata.domain;
DOMAIN = config.selfprivacy.userdata.domain;
};
system.autoUpgrade = {
enable = config.services.userdata.autoUpgrade.enable;
allowReboot = config.services.userdata.autoUpgrade.allowReboot;
enable = config.selfprivacy.userdata.autoUpgrade.enable;
allowReboot = config.selfprivacy.userdata.autoUpgrade.allowReboot;
channel = "https://channel.selfprivacy.org/nixos-selfpricacy";
};
system.stateVersion = config.services.userdata.stateVersion;
system.stateVersion = config.selfprivacy.userdata.stateVersion;
nix = {
optimise.automatic = true;
gc = {

View File

@ -1,6 +1,6 @@
{ config, pkgs, ... }:
let
cfg = config.services.userdata;
cfg = config.selfprivacy.userdata;
dnsCredentialsTemplates = {
DIGITALOCEAN = "DO_AUTH_TOKEN=REPLACEME";
CLOUDFLARE = ''

View File

@ -9,40 +9,54 @@
selfprivacy-graphql-api.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { self, nixpkgs, selfprivacy-graphql-api }:
let
system = "x86_64-linux";
in
{
nixosConfigurations-fun =
{ hardware-configuration
, userdata
, top-level-flake
}: {
just-nixos = nixpkgs.lib.nixosSystem {
specialArgs = {
inherit
system
hardware-configuration
userdata;
selfprivacy-graphql-api =
selfprivacy-graphql-api.packages.${system}.default;
};
modules = [
hardware-configuration
./configuration.nix
{
# embed top-level flake source folder into the build
environment.etc."selfprivacy-config-source" =
top-level-flake.outPath;
# for running "nix search nixpkgs", etc
nix.registry.nixpkgs.flake = nixpkgs;
# embed commit sha1; dirty builds are intentionally forbidden
system.configurationRevision = self.rev;
}
];
inherit system;
};
outputs = { self, nixpkgs, selfprivacy-graphql-api }: {
nixosConfigurations-fun =
{ system
, hardware-configuration
, userdata
, top-level-flake
, sp-modules
}:
let
lib = nixpkgs.legacyPackages.${system}.lib;
in
{
inherit system;
just-nixos = nixpkgs.lib.nixosSystem {
specialArgs = { inherit system; };
modules = [
hardware-configuration
./configuration.nix
(import ./userdata-variables.nix userdata)
(import ./api/api-module.nix
selfprivacy-graphql-api.packages.${system}.default)
{
# embed top-level flake source folder into the build
environment.etc."selfprivacy-config-source".source =
top-level-flake.outPath;
# for running "nix search nixpkgs", etc
nix.registry.nixpkgs.flake = nixpkgs;
# embed commit sha1; FIXME dirty builds must be intentionally forbidden
system.configurationRevision = self.rev or ("#" + self.lastModifiedDate + "-" + toString self.lastModified);
}
]
++
# add SP modules, but filter available config attributes for each
map
(sp-module: args@{ pkgs, ... }: (sp-module.nixosModules.default
(args // {
config =
# TODO use lib.attrsets.mergeAttrsList from nixpkgs 23.05
(builtins.foldl' lib.trivial.mergeAttrs { }
(map
(p: lib.attrsets.setAttrByPath p
(lib.attrsets.getAttrFromPath p args.config))
sp-module.configPathsNeeded));
}))
)
# (sp-module: sp-module.nixosModules.default)
(lib.attrsets.attrValues sp-modules);
};
};
};
};
}

View File

@ -1,6 +1,6 @@
{ config, lib, pkgs, ... }:
let
cfg = config.services.userdata;
cfg = config.selfprivacy.userdata;
in
{
fileSystems = lib.mkIf cfg.useBinds {

View File

@ -1,6 +1,6 @@
{ config, pkgs, lib, ... }:
let
cfg = config.services.userdata;
cfg = config.selfprivacy.userdata;
in
{
users.groups.acmerecievers = {

View File

@ -1,6 +1,6 @@
{ config, pkgs, ... }:
let
domain = config.services.userdata.domain;
domain = config.selfprivacy.userdata.domain;
in
{
systemd = {

View File

@ -1,89 +0,0 @@
{ config, pkgs, lib, ... }:
let
cfg = config.services.userdata;
in
{
imports = [
(builtins.fetchTarball {
# Pick a commit from the branch you are interested in
url = "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/6d0d9fb9/nixos-mailserver-6d0d9fb9.tar.gz";
# And set its hash
sha256 = "sha256:0h35al73p15z9v8zb6hi5nq987sfl5wp4rm5c8947nlzlnsjl61x";
})
];
fileSystems = lib.mkIf cfg.useBinds {
"/var/vmail" = {
device = "/volumes/${cfg.email.location}/vmail";
options = [ "bind" ];
};
"/var/sieve" = {
device = "/volumes/${cfg.email.location}/sieve";
options = [ "bind" ];
};
};
users.users = {
virtualMail = {
isNormalUser = false;
};
};
mailserver = {
enable = true;
fqdn = cfg.domain;
domains = [ cfg.domain ];
# A list of all login accounts. To create the password hashes, use
# mkpasswd -m sha-512 "super secret password"
loginAccounts = {
"${cfg.username}@${cfg.domain}" = {
hashedPassword = cfg.hashedMasterPassword;
sieveScript = ''
require ["fileinto", "mailbox"];
if header :contains "Chat-Version" "1.0"
{
fileinto :create "DeltaChat";
stop;
}
'';
};
} // builtins.listToAttrs (builtins.map
(user: {
name = "${user.username}@${cfg.domain}";
value = {
hashedPassword = user.hashedPassword;
sieveScript = ''
require ["fileinto", "mailbox"];
if header :contains "Chat-Version" "1.0"
{
fileinto :create "DeltaChat";
stop;
}
'';
};
})
cfg.users);
extraVirtualAliases = {
"admin@${cfg.domain}" = "${cfg.username}@${cfg.domain}";
};
certificateScheme = 1;
certificateFile = "/var/lib/acme/${cfg.domain}/fullchain.pem";
keyFile = "/var/lib/acme/${cfg.domain}/key.pem";
# Enable IMAP and POP3
enableImap = true;
enableImapSsl = true;
enablePop3 = false;
enablePop3Ssl = false;
dkimSelector = "selector";
# Enable the ManageSieve protocol
enableManageSieve = true;
virusScanning = false;
};
}

View File

@ -1,6 +1,6 @@
{ pkgs, lib, config, ... }:
let
cfg = config.services.userdata;
cfg = config.selfprivacy.userdata;
in
{
fileSystems = lib.mkIf cfg.useBinds {

View File

@ -1,6 +1,6 @@
{ pkgs, lib, config, ... }:
let
cfg = config.services.userdata;
cfg = config.selfprivacy.userdata;
in
{
fileSystems = lib.mkIf cfg.useBinds {

View File

@ -1,6 +1,6 @@
{ pkgs, lib, config, ... }:
let
cfg = config.services.userdata;
cfg = config.selfprivacy.userdata;
in
{
fileSystems = lib.mkIf cfg.useBinds {

View File

@ -0,0 +1,24 @@
[
[
"services",
"redis"
],
[
"mailserver"
],
[
"selfprivacy",
"userdata",
"email"
],
[
"selfprivacy",
"userdata",
"domain"
],
[
"selfprivacy",
"userdata",
"username"
]
]

View File

@ -0,0 +1,81 @@
{ config, lib, ... }:
let
cfg = config.selfprivacy.userdata;
in
{
fileSystems = lib.mkIf
(cfg.simple-nixos-mailserver.enable && cfg.useBinds)
{
"/var/vmail" = {
device = "/volumes/${cfg.email.location}/vmail";
options = [ "bind" ];
};
"/var/sieve" = {
device = "/volumes/${cfg.email.location}/sieve";
options = [ "bind" ];
};
};
users.users = lib.mkIf cfg.simple-nixos-mailserver.enable {
virtualMail = {
isNormalUser = false;
};
};
selfprivacy.userdata.simple-nixos-mailserver =
lib.mkIf cfg.simple-nixos-mailserver.enable {
fqdn = cfg.domain;
domains = [ cfg.domain ];
# A list of all login accounts. To create the password hashes, use
# mkpasswd -m sha-512 "super secret password"
loginAccounts = {
"${cfg.username}@${cfg.domain}" = {
hashedPassword = cfg.hashedMasterPassword;
sieveScript = ''
require ["fileinto", "mailbox"];
if header :contains "Chat-Version" "1.0"
{
fileinto :create "DeltaChat";
stop;
}
'';
};
} // builtins.listToAttrs (builtins.map
(user: {
name = "${user.username}@${cfg.domain}";
value = {
hashedPassword = user.hashedPassword;
sieveScript = ''
require ["fileinto", "mailbox"];
if header :contains "Chat-Version" "1.0"
{
fileinto :create "DeltaChat";
stop;
}
'';
};
})
cfg.users);
extraVirtualAliases = {
"admin@${cfg.domain}" = "${cfg.username}@${cfg.domain}";
};
certificateScheme = "manual";
certificateFile = "/var/lib/acme/${cfg.domain}/fullchain.pem";
keyFile = "/var/lib/acme/${cfg.domain}/key.pem";
# Enable IMAP and POP3
enableImap = true;
enableImapSsl = true;
enablePop3 = false;
enablePop3Ssl = false;
dkimSelector = "selector";
# Enable the ManageSieve protocol
enableManageSieve = true;
virusScanning = false;
};
}

View File

@ -0,0 +1,126 @@
{
"nodes": {
"blobs": {
"flake": false,
"locked": {
"lastModified": 1604995301,
"narHash": "sha256-wcLzgLec6SGJA8fx1OEN1yV/Py5b+U5iyYpksUY/yLw=",
"owner": "simple-nixos-mailserver",
"repo": "blobs",
"rev": "2cccdf1ca48316f2cfd1c9a0017e8de5a7156265",
"type": "gitlab"
},
"original": {
"owner": "simple-nixos-mailserver",
"repo": "blobs",
"type": "gitlab"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1668681692,
"narHash": "sha256-Ht91NGdewz8IQLtWZ9LCeNXMSXHUss+9COoqu6JLmXU=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "009399224d5e398d03b22badca40a37ac85412a1",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1670751203,
"narHash": "sha256-XdoH1v3shKDGlrwjgrNX/EN8s3c+kQV7xY6cLCE8vcI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "64e0bf055f9d25928c31fb12924e59ff8ce71e60",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-unstable",
"type": "indirect"
}
},
"nixpkgs-22_11": {
"locked": {
"lastModified": 1669558522,
"narHash": "sha256-yqxn+wOiPqe6cxzOo4leeJOp1bXE/fjPEi/3F/bBHv8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "ce5fe99df1f15a09a91a86be9738d68fadfbad82",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-22.11",
"type": "indirect"
}
},
"nixpkgs-23_05": {
"locked": {
"lastModified": 1684782344,
"narHash": "sha256-SHN8hPYYSX0thDrMLMWPWYulK3YFgASOrCsIL3AJ78g=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "8966c43feba2c701ed624302b6a935f97bcbdf88",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-23.05",
"type": "indirect"
}
},
"root": {
"inputs": {
"simple-nixos-mailserver": "simple-nixos-mailserver"
}
},
"simple-nixos-mailserver": {
"inputs": {
"blobs": "blobs",
"flake-compat": "flake-compat",
"nixpkgs": "nixpkgs",
"nixpkgs-22_11": "nixpkgs-22_11",
"nixpkgs-23_05": "nixpkgs-23_05",
"utils": "utils"
},
"locked": {
"lastModified": 1695910380,
"narHash": "sha256-CyzeiXQGm8ceEOSK1dffBCfO7JNp8XhQeNkUiJ5HxgY=",
"owner": "simple-nixos-mailserver",
"repo": "nixos-mailserver",
"rev": "84783b661ecf33927c534b6476beb74ea3308968",
"type": "gitlab"
},
"original": {
"owner": "simple-nixos-mailserver",
"repo": "nixos-mailserver",
"type": "gitlab"
}
},
"utils": {
"locked": {
"lastModified": 1605370193,
"narHash": "sha256-YyMTf3URDL/otKdKgtoMChu4vfVL3vCMkRqpGifhUn0=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5021eac20303a61fafe17224c087f5519baed54d",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View File

@ -0,0 +1,33 @@
{
description = "PoC SP module for the simple-nixos-mailserver";
inputs.mailserver.url =
gitlab:simple-nixos-mailserver/nixos-mailserver;
outputs = { self, mailserver }: {
# tricks to rename (alias) the original module
nixosModules.default = args@{ pkgs, ... }:
let
module = mailserver.nixosModules.default args;
in
module // {
imports = module.imports ++ [
./config.nix
({ config, ... }: {
mailserver =
config.selfprivacy.userdata.simple-nixos-mailserver;
})
];
options = module.options // {
selfprivacy.userdata.simple-nixos-mailserver =
module.options.mailserver;
};
};
configPathsNeeded =
builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
# TODO generate json docs from module? something like:
# nix eval --impure --expr 'let flake = builtins.getFlake (builtins.toPath ./.); pkgs = flake.inputs.mailserver.inputs.nixpkgs.legacyPackages.x86_64-linux; in (pkgs.nixosOptionsDoc { inherit (pkgs.lib.evalModules { modules = [ flake.nixosModules.default ]; }) options; }).optionsJSON'
# (doesn't work because of `assertions`)
};
}

View File

@ -0,0 +1,3 @@
{
"enable"
}

View File

@ -1,9 +1,6 @@
{ lib, userdata, ... }:
let
jsonData = userdata;
in
jsonData: { lib, ... }:
{
services.userdata = {
selfprivacy.userdata = jsonData // {
hostname = lib.attrsets.attrByPath [ "hostname" ] null jsonData;
domain = lib.attrsets.attrByPath [ "domain" ] null jsonData;
timezone = lib.attrsets.attrByPath [ "timezone" ] "Europe/Uzhgorod" jsonData;
@ -61,6 +58,5 @@ in
};
users = lib.attrsets.attrByPath [ "users" ] [ ] jsonData;
volumes = lib.attrsets.attrByPath [ "volumes" ] [ ] jsonData;
useBinds = lib.attrsets.attrByPath [ "useBinds" ] false jsonData;
};
}

View File

@ -1,6 +1,6 @@
{ pkgs, config, ... }:
let
cfg = config.services.userdata;
cfg = config.selfprivacy.userdata;
in
{
users.mutableUsers = false;

View File

@ -1,16 +1,8 @@
{ config, lib, pkgs, ... }:
{ lib, ... }:
with lib;
let
cfg = config.services.userdata;
directionArg =
if cfg.direction == ""
then ""
else "--direction=${cfg.direction}";
in
{
options.services.userdata = {
options.selfprivacy.userdata = {
# General server options
hostname = mkOption {
description = "The hostname of the server.";
@ -221,6 +213,7 @@ in
useBinds = mkOption {
type = types.nullOr types.bool;
default = false;
description = "Whether to bind-mount vmail and sieve folders";
};
};
}

View File

@ -1,11 +1,8 @@
{ pkgs, config, ... }:
let
domain = config.services.userdata.domain;
in
{ config, ... }:
{
services.jitsi-meet = {
enable = config.services.userdata.jitsi.enable;
hostName = "meet.${domain}";
enable = config.selfprivacy.userdata.jitsi.enable;
hostName = "meet.${config.selfprivacy.userdata.domain}";
nginx.enable = true;
interfaceConfig = {
SHOW_JITSI_WATERMARK = false;

View File

@ -1,9 +1,9 @@
{ pkgs, config, ... }:
{ config, ... }:
let
cfg = config.services.userdata;
cfg = config.selfprivacy.userdata;
in
{
fileSystems = { } // builtins.listToAttrs (builtins.map
fileSystems = builtins.listToAttrs (builtins.map
(volume: {
name = "${volume.mountPoint}";
value = {

View File

@ -1,6 +1,6 @@
{ pkgs, config, ... }:
{ config, ... }:
let
domain = config.services.userdata.domain;
domain = config.selfprivacy.userdata.domain;
in
{
users.groups.ocserv = {
@ -13,7 +13,7 @@ in
group = "ocserv";
};
services.ocserv = {
enable = config.services.userdata.ocserv.enable;
enable = config.selfprivacy.userdata.ocserv.enable;
config = ''
socket-file = /var/run/ocserv-socket

View File

@ -1,6 +1,6 @@
{ pkgs, config, lib, ... }:
{ config, lib, ... }:
let
domain = config.services.userdata.domain;
domain = config.selfprivacy.userdata.domain;
in
{
services.nginx = {