alps: 2022-10-18 -> 1 (#529858)

This commit is contained in:
Ivan Mincik
2026-07-08 07:19:46 +00:00
committed by GitHub
5 changed files with 169 additions and 169 deletions
+2
View File
@@ -25,6 +25,8 @@
- `zerofs` has been updated from `1.x` to `2.x` which is a breaking change. Volumes created by earlier releases are refused at open with a clear error. There is no migration tool; older volumes remain readable and writable by the release that created them.
- `alps` has been rewritten upstream, see [upstream repository](https://github.com/migadu/alps) for documentation.
- `uhttpmock` providing 0.0 ABI was removed. `uhttpmock_1_0` providing 1.0 ABI was renamed to `uhttpmock` and `uhttpmock_1_0` was kept as an alias.
- `nix-serve-ng` (and `haskellPackages.nix-serve-ng`) is now built against Lix instead of CppNix, following upstream which has switched to Lix as its supported Nix implementation.
@@ -47,6 +47,8 @@
Deployments running the server and worker in the same network namespace must also set at least the worker
`AUTHENTIK_LISTEN__HTTP` address so that the server and worker do not bind to the same address.
- `services.alps` has been rewritten, see [upstream repository](https://github.com/migadu/alps) for configuration.
- Support for the legacy UBoot image format has been removed from the initrd generators, as it is deprecated upstream and no longer used by any platform in Nixpkgs.
- Rustical migrates from `settings.http.host` and `settings.http.port` to `settings.http.bind` to support UNIX domain sockets as well as TCP sockets in one setting.
+63 -106
View File
@@ -1,151 +1,108 @@
{
config,
lib,
pkgs,
config,
utils,
...
}:
with lib;
let
cfg = config.services.alps;
in
{
imports = [
(lib.mkRemovedOptionModule [ "services" "alps" "port" ] ''
Use `services.alps.settings.server.addr` instead.
'')
(lib.mkRemovedOptionModule [ "services" "alps" "bindIP" ] ''
Use `services.alps.settings.server.addr` instead.
'')
(lib.mkRemovedOptionModule [ "services" "alps" "theme" ] ''
Themes are no longer customizable.
'')
(lib.mkRemovedOptionModule [ "services" "alps" "imaps" "port" ] ''
Use `services.alps.settings.provider.imap.server` instead.
'')
(lib.mkRemovedOptionModule [ "services" "alps" "imaps" "host" ] ''
Use `services.alps.settings.provider.imap.server` instead.
'')
(lib.mkRemovedOptionModule [ "services" "alps" "smtps" "port" ] ''
Use `services.alps.settings.smtp.server` instead.
'')
(lib.mkRemovedOptionModule [ "services" "alps" "smtps" "host" ] ''
Use `services.alps.settings.smtp.server` instead.
'')
(lib.mkRemovedOptionModule [ "services" "alps" "args" ] ''
Use `services.alps.settings` instead.
'')
];
options.services.alps = {
enable = mkEnableOption "alps";
enable = lib.mkEnableOption "alps";
package = lib.mkPackageOption pkgs "alps" { };
port = mkOption {
type = types.port;
default = 1323;
settings = lib.mkOption {
description = ''
TCP port the service should listen on.
The ALPS configuration, see <https://github.com/migadu/alps/blob/main/docs/CONFIGURATION.md> for documentation.
Options containing secret data should be set to an attribute set
containing the attribute `_secret` - a string pointing to a file
containing the value the option should be set to.
'';
};
bindIP = mkOption {
default = "[::]";
type = types.str;
description = ''
The IP the service should listen on.
'';
};
theme = mkOption {
type = types.enum [
"alps"
"sourcehut"
];
default = "sourcehut";
description = ''
The frontend's theme to use.
'';
};
imaps = {
port = mkOption {
type = types.port;
default = 993;
description = ''
The IMAPS server port.
'';
default = { };
type = lib.types.submodule {
freeformType = lib.types.toml;
options = { };
};
host = mkOption {
type = types.str;
default = "[::1]";
example = "mail.example.org";
description = ''
The IMAPS server address.
'';
};
};
smtps = {
port = mkOption {
type = types.port;
default = 465;
description = ''
The SMTPS server port.
'';
};
host = mkOption {
type = types.str;
default = cfg.imaps.host;
defaultText = "services.alps.imaps.host";
example = "mail.example.org";
description = ''
The SMTPS server address.
'';
};
};
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}"
"smtps://${cfg.smtps.host}:${toString cfg.smtps.port}"
];
};
};
config = mkIf cfg.enable {
config = lib.mkIf cfg.enable {
systemd.packages = [ cfg.package ];
systemd.services.alps = {
description = "alps is a simple and extensible webmail.";
documentation = [ "https://git.sr.ht/~migadu/alps" ];
wantedBy = [ "multi-user.target" ];
wants = [ "network-online.target" ];
after = [
"network.target"
"network-online.target"
];
serviceConfig = {
ExecStart = "${cfg.package}/bin/alps ${escapeShellArgs cfg.args}";
AmbientCapabilities = "";
CapabilityBoundingSet = "";
DynamicUser = true;
RuntimeDirectory = "alps";
ExecStartPre =
let
script = pkgs.writeShellScript "alps-pre-start" ''
${utils.genJqSecretsReplacementSnippet cfg.settings "/run/alps/config.json"}
${lib.getExe pkgs.remarshal} -f json -t toml /run/alps/config.json /run/alps/config.toml
chown --reference=/run/alps /run/alps/config.json /run/alps/config.toml
'';
in
"+${script}";
ExecStart = [
""
"${lib.getExe cfg.package} -config \${RUNTIME_DIRECTORY}/config.toml"
];
LockPersonality = true;
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateDevices = true;
PrivateIPC = 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";
RemoveIPC = true;
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SocketBindAllow = cfg.port;
SocketBindDeny = "any";
SystemCallArchitectures = "native";
SystemCallArchitectures = [ "native" ];
SystemCallFilter = [
"@system-service"
"~@privileged @obsolete"
"~@privileged"
"~@resources"
];
};
};
+46 -32
View File
@@ -1,12 +1,16 @@
{ lib, ... }:
let
certs = import ./common/acme/server/snakeoil-certs.nix;
domain = certs.domain;
port = 1323;
in
{ pkgs, ... }:
{
name = "alps";
meta = with pkgs.lib.maintainers; {
maintainers = [ hmenke ];
meta = {
maintainers = with lib.maintainers; [
hmenke
prince213
];
};
nodes = {
@@ -53,9 +57,8 @@ in
};
};
};
client =
{ nodes, config, ... }:
{ nodes, pkgs, ... }:
{
security.pki.certificateFiles = [
certs.ca.cert
@@ -65,31 +68,44 @@ in
'';
services.alps = {
enable = true;
theme = "alps";
imaps = {
host = domain;
port = 993;
};
smtps = {
host = domain;
port = 465;
settings = {
server = {
addr = ":${toString port}";
};
provider = {
type = "imap";
imap = {
server = "imaps://${domain}:993";
};
};
smtp = {
server = "smtps://${domain}:465";
};
};
};
environment.systemPackages = [
(pkgs.writers.writePython3Bin "test-alps-login" { } ''
from urllib.request import build_opener, HTTPCookieProcessor, Request
from urllib.parse import urlencode, urljoin
from urllib.parse import urljoin
from http.cookiejar import CookieJar
import json
baseurl = "http://localhost:${toString config.services.alps.port}"
baseurl = "http://localhost:${toString port}"
username = "alice"
password = "${nodes.server.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")
data = json.dumps(
{"username": username, "password": password, "remember-me": ""}
).encode()
req = Request(
urljoin(baseurl, "session"),
data=data,
headers={"Content-Type": "application/json"},
method="POST",
)
with opener.open(req) as ret:
# Check that the alps_session cookie is set
print(cookiejar)
@@ -102,9 +118,9 @@ in
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")
assert ret.url != urljoin(baseurl, "#/login")
req = Request(urljoin(baseurl, "logout"))
req = Request(urljoin(baseurl, "session"), method="DELETE")
with opener.open(req) as ret:
# Check that the alps_session cookie is now gone
print(cookiejar)
@@ -114,18 +130,16 @@ in
};
};
testScript =
{ nodes, ... }:
''
server.start()
server.wait_for_unit("postfix.service")
server.wait_for_unit("dovecot.service")
server.wait_for_open_port(465)
server.wait_for_open_port(993)
testScript = ''
server.start()
server.wait_for_unit("postfix.service")
server.wait_for_unit("dovecot.service")
server.wait_for_open_port(465)
server.wait_for_open_port(993)
client.start()
client.wait_for_unit("alps.service")
client.wait_for_open_port(${toString nodes.client.services.alps.port})
client.succeed("test-alps-login")
'';
client.start()
client.wait_for_unit("alps.service")
client.wait_for_open_port(${toString port})
client.succeed("test-alps-login")
'';
}
+56 -31
View File
@@ -1,61 +1,86 @@
{
lib,
buildGoModule,
fetchFromSourcehut,
fetchpatch,
buildNpmPackage,
fetchFromGitHub,
nixosTests,
util-linux,
versionCheckHook,
}:
buildGoModule {
buildGoModule (finalAttrs: {
pname = "alps";
version = "2022-10-18";
version = "1";
__structuredAttrs = true;
src = fetchFromSourcehut {
owner = "~migadu";
src = fetchFromGitHub {
owner = "migadu";
repo = "alps";
rev = "f01fbcbc48db5e65d69a0ebd9d7cb0deb378cf13";
hash = "sha256-RSug3YSiqYLGs05Bee4NoaoCyPvUZ7IqlKWI1hmxbiA=";
tag = "v${finalAttrs.version}";
hash = "sha256-uzr0N50qKpIoOr7YFfuhnJ/CTaMvcP7TZujM5YpklMs=";
};
vendorHash = "sha256-QsGfINktk+rBj4b5h+NBVS6XV1SVz+9fDL1vtUqcKEU=";
postPatch = ''
substituteInPlace dist/alps.service \
--replace-fail /usr/local/bin "$out/bin" \
--replace-fail /bin/kill "${lib.getExe' util-linux "kill"}"
rm -r frontend/dist
cp -r ${finalAttrs.passthru.frontend} frontend/dist
'';
vendorHash = "sha256-Nm9TC0j/PSraO1AtxUJmFQWdhdLzeLP0CXY0FZZ6pV8=";
subPackages = [ "cmd/alps" ];
ldflags = [
"-s"
"-w"
"-X main.themesPath=${placeholder "out"}/share/alps/themes"
"-X git.sr.ht/~migadu/alps.PluginDir=${placeholder "out"}/share/alps/plugins"
"-X main.version=${finalAttrs.version}"
];
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"
'';
postInstall = ''
mkdir -p "$out/share/alps"
cp -r themes plugins "$out/share/alps/"
install -Dm644 -t "$out/lib/systemd/system/" dist/alps.service
'';
proxyVendor = true;
doInstallCheck = true;
nativeInstallCheckInputs = [ versionCheckHook ];
versionCheckProgramArg = "-version";
passthru.tests = { inherit (nixosTests) alps; };
passthru = {
frontend = buildNpmPackage (finalAttrs': {
pname = "${finalAttrs.pname}-frontend";
inherit (finalAttrs) version src;
sourceRoot = "${finalAttrs'.src.name}/frontend";
npmDepsHash = "sha256-gR9leLQSPo/qBNf6Yy1b2klawwuhKIvofCSPYkHOJKk=";
postPatch = ''
rm -r dist
'';
installPhase = ''
runHook preInstall
mkdir -p "$out"
cp -r dist/. "$out"
runHook postInstall
'';
});
tests = { inherit (nixosTests) alps; };
};
meta = {
description = "Simple and extensible webmail";
homepage = "https://git.sr.ht/~migadu/alps";
homepage = "https://github.com/migadu/alps";
downloadPage = "https://github.com/migadu/alps/releases";
license = lib.licenses.mit;
maintainers = with lib.maintainers; [
booklearner
madonius
hmenke
prince213
];
teams = with lib.teams; [ ngi ];
mainProgram = "alps";
};
}
})