Merge pull request #201521 from hmenke/alps

nixos/alps: add hardening, extensible options, test

Changed files
+163 -14
nixos
modules
services
web-apps
tests
pkgs
servers
+43 -9
nixos/modules/services/web-apps/alps.nix
···
'';
};
};
+
+
package = mkOption {
+
internal = true;
+
type = types.package;
+
default = pkgs.alps;
+
};
+
+
args = mkOption {
+
internal = true;
+
type = types.listOf types.str;
+
default = [
+
"-addr" "${cfg.bindIP}:${toString cfg.port}"
+
"-theme" "${cfg.theme}"
+
"imaps://${cfg.imaps.host}:${toString cfg.imaps.port}"
+
"smpts://${cfg.smtps.host}:${toString cfg.smtps.port}"
+
];
+
};
};
config = mkIf cfg.enable {
···
after = [ "network.target" "network-online.target" ];
serviceConfig = {
-
ExecStart = ''
-
${pkgs.alps}/bin/alps \
-
-addr ${cfg.bindIP}:${toString cfg.port} \
-
-theme ${cfg.theme} \
-
imaps://${cfg.imaps.host}:${toString cfg.imaps.port} \
-
smpts://${cfg.smtps.host}:${toString cfg.smtps.port}
-
'';
-
StateDirectory = "alps";
-
WorkingDirectory = "/var/lib/alps";
+
ExecStart = "${cfg.package}/bin/alps ${escapeShellArgs cfg.args}";
DynamicUser = true;
+
## This is desirable but would restrict bindIP to 127.0.0.1
+
#IPAddressAllow = "localhost";
+
#IPAddressDeny = "any";
+
LockPersonality = true;
+
NoNewPrivileges = true;
+
PrivateDevices = true;
+
PrivateIPC = true;
+
PrivateTmp = true;
+
PrivateUsers = true;
+
ProtectClock = true;
+
ProtectControlGroups = true;
+
ProtectHome = true;
+
ProtectHostname = true;
+
ProtectKernelLogs = true;
+
ProtectKernelModules = true;
+
ProtectKernelTunables = true;
+
ProtectProc = "invisible";
+
ProtectSystem = "strict";
+
RemoveIPC = true;
+
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+
RestrictNamespaces = true;
+
RestrictRealtime = true;
+
RestrictSUIDSGID = true;
+
SystemCallArchitectures = "native";
+
SystemCallFilter = [ "@system-service @resources" "~@privileged @obsolete" ];
};
};
};
+1
nixos/tests/all-tests.nix
···
agda = handleTest ./agda.nix {};
airsonic = handleTest ./airsonic.nix {};
allTerminfo = handleTest ./all-terminfo.nix {};
+
alps = handleTest ./alps.nix {};
amazon-init-shell = handleTest ./amazon-init-shell.nix {};
apfs = handleTest ./apfs.nix {};
apparmor = handleTest ./apparmor.nix {};
+104
nixos/tests/alps.nix
···
+
let
+
certs = import ./common/acme/server/snakeoil-certs.nix;
+
domain = certs.domain;
+
in
+
import ./make-test-python.nix ({ pkgs, ... }: {
+
name = "alps";
+
+
nodes = {
+
server = {
+
imports = [ ./common/user-account.nix ];
+
security.pki.certificateFiles = [
+
certs.ca.cert
+
];
+
networking.extraHosts = ''
+
127.0.0.1 ${domain}
+
'';
+
networking.firewall.allowedTCPPorts = [ 25 465 993 ];
+
services.postfix = {
+
enable = true;
+
enableSubmission = true;
+
enableSubmissions = true;
+
tlsTrustedAuthorities = "${certs.ca.cert}";
+
sslCert = "${certs.${domain}.cert}";
+
sslKey = "${certs.${domain}.key}";
+
};
+
services.dovecot2 = {
+
enable = true;
+
enableImap = true;
+
sslCACert = "${certs.ca.cert}";
+
sslServerCert = "${certs.${domain}.cert}";
+
sslServerKey = "${certs.${domain}.key}";
+
};
+
};
+
+
client = { nodes, config, ... }: {
+
security.pki.certificateFiles = [
+
certs.ca.cert
+
];
+
networking.extraHosts = ''
+
${nodes.server.config.networking.primaryIPAddress} ${domain}
+
'';
+
services.alps = {
+
enable = true;
+
theme = "alps";
+
imaps = {
+
host = domain;
+
port = 993;
+
};
+
smtps = {
+
host = domain;
+
port = 465;
+
};
+
};
+
environment.systemPackages = [
+
(pkgs.writers.writePython3Bin "test-alps-login" { } ''
+
from urllib.request import build_opener, HTTPCookieProcessor, Request
+
from urllib.parse import urlencode, urljoin
+
from http.cookiejar import CookieJar
+
+
baseurl = "http://localhost:${toString config.services.alps.port}"
+
username = "alice"
+
password = "${nodes.server.config.users.users.alice.password}"
+
cookiejar = CookieJar()
+
cookieprocessor = HTTPCookieProcessor(cookiejar)
+
opener = build_opener(cookieprocessor)
+
+
data = urlencode({"username": username, "password": password}).encode()
+
req = Request(urljoin(baseurl, "login"), data=data, method="POST")
+
with opener.open(req) as ret:
+
# Check that the alps_session cookie is set
+
print(cookiejar)
+
assert any(cookie.name == "alps_session" for cookie in cookiejar)
+
+
req = Request(baseurl)
+
with opener.open(req) as ret:
+
# Check that the alps_session cookie is still there...
+
print(cookiejar)
+
assert any(cookie.name == "alps_session" for cookie in cookiejar)
+
# ...and that we have not been redirected back to the login page
+
print(ret.url)
+
assert ret.url == urljoin(baseurl, "mailbox/INBOX")
+
+
req = Request(urljoin(baseurl, "logout"))
+
with opener.open(req) as ret:
+
# Check that the alps_session cookie is now gone
+
print(cookiejar)
+
assert all(cookie.name != "alps_session" for cookie in cookiejar)
+
'')
+
];
+
};
+
};
+
+
testScript = ''
+
server.start()
+
server.wait_for_unit("postfix.service")
+
server.wait_for_unit("dovecot2.service")
+
server.wait_for_open_port(465)
+
server.wait_for_open_port(993)
+
+
client.start()
+
client.wait_for_unit("alps.service")
+
client.succeed("test-alps-login")
+
'';
+
})
+15 -5
pkgs/servers/alps/default.nix
···
-
{ lib, buildGoModule, fetchFromSourcehut }:
+
{ lib, buildGoModule, fetchFromSourcehut, fetchpatch, nixosTests }:
buildGoModule rec {
pname = "alps";
-
version = "2022-06-03";
+
version = "2022-10-18";
src = fetchFromSourcehut {
owner = "~migadu";
repo = "alps";
-
rev = "9cb23b09975e95f6a5952e3718eaf471c3e3510f";
-
hash = "sha256-BUV1/BRIXHEf2FU1rdmNgueo8KSUlMKbIpAg2lFs3hA=";
+
rev = "f01fbcbc48db5e65d69a0ebd9d7cb0deb378cf13";
+
hash = "sha256-RSug3YSiqYLGs05Bee4NoaoCyPvUZ7IqlKWI1hmxbiA=";
};
-
vendorSha256 = "sha256-cpY+lYM/nAX3nUaFknrRAavxDk8UDzJkoqFjJ1/KWeg=";
+
vendorSha256 = "sha256-XDm6LU9D/rVQHiko7EFpocv+IktGe6tQhJYRrOJxeSs=";
ldflags = [
"-s"
···
"-X git.sr.ht/~migadu/alps.PluginDir=${placeholder "out"}/share/alps/plugins"
];
+
patches = [
+
(fetchpatch {
+
name = "Issue-160-Alps-theme-has-a-enormous-move-to-list-sel";
+
url = "https://lists.sr.ht/~migadu/alps-devel/patches/30096/mbox";
+
hash = "sha256-Sz/SCkrrXZWrmJzjfPXi+UfCcbwsy6QiA7m34iiEFX0=";
+
})
+
];
+
postPatch = ''
substituteInPlace plugin.go --replace "const PluginDir" "var PluginDir"
'';
···
'';
proxyVendor = true;
+
+
passthru.tests = { inherit(nixosTests) alps; };
meta = with lib; {
description = "A simple and extensible webmail.";