Merge pull request #201521 from hmenke/alps
nixos/alps: add hardening, extensible options, test
This commit is contained in:
@@ -70,6 +70,23 @@ in {
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
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 {
|
||||
@@ -80,16 +97,33 @@ in {
|
||||
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" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -74,6 +74,7 @@ in {
|
||||
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 {};
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
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")
|
||||
'';
|
||||
})
|
||||
@@ -1,17 +1,17 @@
|
||||
{ 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"
|
||||
@@ -20,6 +20,14 @@ buildGoModule rec {
|
||||
"-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"
|
||||
'';
|
||||
@@ -31,6 +39,8 @@ buildGoModule rec {
|
||||
|
||||
proxyVendor = true;
|
||||
|
||||
passthru.tests = { inherit(nixosTests) alps; };
|
||||
|
||||
meta = with lib; {
|
||||
description = "A simple and extensible webmail.";
|
||||
homepage = "https://git.sr.ht/~migadu/alps";
|
||||
|
||||
Reference in New Issue
Block a user