Unverified Commit fc8a4f72 authored by Anderson Torres's avatar Anderson Torres Committed by GitHub
Browse files

Merge pull request #201521 from hmenke/alps

nixos/alps: add hardening, extensible options, test
parents 5dcab938 ee758abe
Loading
Loading
Loading
Loading
+43 −9
Original line number Diff line number Diff line
@@ -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" ];
      };
    };
  };
+1 −0
Original line number Diff line number Diff line
@@ -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 {};

nixos/tests/alps.nix

0 → 100644
+104 −0
Original line number Diff line number Diff line
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
Original line number Diff line number Diff line
{ 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";