-
-
Notifications
You must be signed in to change notification settings - Fork 14.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
302 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
{ | ||
config, | ||
lib, | ||
pkgs, | ||
... | ||
}: | ||
let | ||
|
||
cfg = config.services.froide-govplan; | ||
|
||
# Service hardening | ||
defaultServiceConfig = { | ||
# Secure the services | ||
ReadWritePaths = [ cfg.dataDir ]; | ||
CacheDirectory = "froide-govplan"; | ||
CapabilityBoundingSet = ""; | ||
# ProtectClock adds DeviceAllow=char-rtc r | ||
DeviceAllow = ""; | ||
LockPersonality = true; | ||
MemoryDenyWriteExecute = true; | ||
NoNewPrivileges = true; | ||
PrivateDevices = true; | ||
PrivateMounts = true; | ||
PrivateNetwork = true; | ||
PrivateTmp = true; | ||
PrivateUsers = true; | ||
ProtectClock = true; | ||
ProtectHome = true; | ||
ProtectHostname = true; | ||
ProtectSystem = "strict"; | ||
ProtectControlGroups = true; | ||
ProtectKernelLogs = true; | ||
ProtectKernelModules = true; | ||
ProtectKernelTunables = true; | ||
ProtectProc = "invisible"; | ||
ProcSubset = "pid"; | ||
RestrictAddressFamilies = [ | ||
"AF_UNIX" | ||
"AF_INET" | ||
"AF_INET6" | ||
]; | ||
RestrictNamespaces = true; | ||
RestrictRealtime = true; | ||
RestrictSUIDSGID = true; | ||
SystemCallArchitectures = "native"; | ||
SystemCallFilter = [ | ||
"@system-service" | ||
"~@privileged @setuid @keyring" | ||
]; | ||
UMask = "0066"; | ||
}; | ||
|
||
in | ||
{ | ||
options.services.froide-govplan = { | ||
|
||
enable = lib.mkEnableOption "Gouvernment planer web app Govplan"; | ||
|
||
package = lib.mkPackageOption pkgs "froide-govplan" { }; | ||
|
||
listenAddress = lib.mkOption { | ||
type = lib.types.str; | ||
default = "[::1]"; | ||
description = '' | ||
Address the server will listen on. | ||
''; | ||
}; | ||
|
||
port = lib.mkOption { | ||
type = lib.types.port; | ||
default = 8080; | ||
description = "Web interface port."; | ||
}; | ||
|
||
dataDir = lib.mkOption { | ||
type = lib.types.str; | ||
default = "/var/lib/froide-govplan"; | ||
description = "Directory to store the Froide-Govplan server data."; | ||
}; | ||
|
||
openFirewall = lib.mkOption { | ||
type = lib.types.bool; | ||
default = false; | ||
description = '' | ||
Whether to open ports in the firewall for the server. | ||
''; | ||
}; | ||
|
||
settings = lib.mkOption { | ||
default = { }; | ||
description = '' | ||
IMAP authentication configuration for rspamd-trainer. For supplying | ||
the IMAP password, use the `secrets` option. | ||
''; | ||
example = lib.literalExpression '' | ||
{ | ||
HOST = "localhost"; | ||
USERNAME = "[email protected]"; | ||
INBOXPREFIX = "INBOX/"; | ||
} | ||
''; | ||
}; | ||
|
||
secrets = lib.mkOption { | ||
type = with lib.types; listOf path; | ||
description = '' | ||
A list of files containing the various secrets. Should be in the | ||
format expected by systemd's `EnvironmentFile` directory. For the | ||
IMAP account password use `PASSWORD = mypassword`. | ||
''; | ||
default = [ ]; | ||
}; | ||
|
||
}; | ||
|
||
config = lib.mkIf cfg.enable { | ||
|
||
services.postgresql = { | ||
enable = true; | ||
ensureDatabases = [ "govplan" ]; | ||
ensureUsers = [ | ||
{ | ||
name = "govplan"; | ||
ensureDBOwnership = true; | ||
} | ||
]; | ||
extensions = ps: with ps; [ postgis ]; | ||
authentication = '' | ||
host govplan govplan localhost trust | ||
''; | ||
initialScript = pkgs.writeText "backend-initScript" '' | ||
ALTER USER govplan WITH SUPERUSER; | ||
''; | ||
}; | ||
|
||
systemd = { | ||
services = { | ||
|
||
postgresql.serviceConfig.ExecStartPost = | ||
let | ||
sqlFile = pkgs.writeText "immich-pgvectors-setup.sql" '' | ||
ALTER USER govplan WITH SUPERUSER; | ||
#CREATE EXTENSION IF NOT EXISTS postgis; | ||
#ALTER SCHEMA govplan OWNER TO govplan; | ||
#ALTER EXTENSION govplan UPDATE; | ||
''; | ||
in | ||
[ | ||
'' | ||
${lib.getExe' config.services.postgresql.package "psql"} -d govplan -f "${sqlFile}" | ||
'' | ||
]; | ||
|
||
froide-govplan = { | ||
description = "Gouvernment planer Govplan"; | ||
serviceConfig = { | ||
ExecStart = "${lib.getExe cfg.package} runserver ${cfg.listenAddress}:${toString cfg.port}"; | ||
WorkingDirectory = cfg.dataDir; | ||
StateDirectory = lib.mkIf (cfg.dataDir == "/var/lib/froide-govplan") "froide-govplan"; | ||
DynamicUser = true; | ||
EnvironmentFile = [ cfg.secrets ]; | ||
}; | ||
after = [ "postgresql.service" ]; | ||
wantedBy = [ "multi-user.target" ]; | ||
preStart = '' | ||
# Auto-migrate on first run or if the package has changed | ||
versionFile="${cfg.dataDir}/src-version" | ||
version=$(cat "$versionFile" 2>/dev/null || echo 0) | ||
if [[ $version != ${cfg.package.version} ]]; then | ||
${lib.getExe cfg.package} migrate --no-input | ||
${lib.getExe cfg.package} migrate djangocms_alias --no-input | ||
${lib.getExe cfg.package} collectstatic --no-input --clear | ||
echo ${cfg.package.version} > "$versionFile" | ||
fi | ||
''; | ||
}; | ||
}; | ||
|
||
}; | ||
|
||
networking.firewall = lib.mkIf cfg.openFirewall { allowedTCPPorts = [ cfg.port ]; }; | ||
|
||
environment.systemPackages = [ cfg.package ]; | ||
|
||
}; | ||
|
||
meta.maintainers = with lib.maintainers; [ onny ]; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import ./make-test-python.nix ( | ||
{ lib, ... }: | ||
{ | ||
name = "paperless"; | ||
meta.maintainers = with lib.maintainers; [ | ||
leona | ||
SuperSandro2000 | ||
erikarvstedt | ||
]; | ||
|
||
nodes = | ||
let | ||
self = { | ||
simple = | ||
{ pkgs, ... }: | ||
{ | ||
environment.systemPackages = with pkgs; [ | ||
imagemagick | ||
jq | ||
]; | ||
services.paperless = { | ||
enable = true; | ||
passwordFile = builtins.toFile "password" "admin"; | ||
}; | ||
}; | ||
postgres = | ||
{ config, pkgs, ... }: | ||
{ | ||
imports = [ self.simple ]; | ||
services.postgresql = { | ||
enable = true; | ||
ensureDatabases = [ "paperless" ]; | ||
ensureUsers = [ | ||
{ | ||
name = config.services.paperless.user; | ||
ensureDBOwnership = true; | ||
} | ||
]; | ||
}; | ||
services.paperless.settings = { | ||
PAPERLESS_DBHOST = "/run/postgresql"; | ||
PAPERLESS_OCR_LANGUAGE = "deu"; | ||
}; | ||
}; | ||
}; | ||
in | ||
self; | ||
|
||
testScript = '' | ||
import json | ||
def test_paperless(node): | ||
node.wait_for_unit("paperless-consumer.service") | ||
with subtest("Add a document via the file system"): | ||
node.succeed( | ||
"convert -size 400x40 xc:white -font 'DejaVu-Sans' -pointsize 20 -fill black " | ||
"-annotate +5+20 'hello world 16-10-2005' /var/lib/paperless/consume/doc.png" | ||
) | ||
with subtest("Web interface gets ready"): | ||
node.wait_for_unit("paperless-web.service") | ||
# Wait until server accepts connections | ||
node.wait_until_succeeds("curl -fs localhost:28981") | ||
# Required for consuming documents via the web interface | ||
with subtest("Task-queue gets ready"): | ||
node.wait_for_unit("paperless-task-queue.service") | ||
with subtest("Add a png document via the web interface"): | ||
node.succeed( | ||
"convert -size 400x40 xc:white -font 'DejaVu-Sans' -pointsize 20 -fill black " | ||
"-annotate +5+20 'hello web 16-10-2005' /tmp/webdoc.png" | ||
) | ||
node.wait_until_succeeds("curl -u admin:admin -F document=@/tmp/webdoc.png -fs localhost:28981/api/documents/post_document/") | ||
with subtest("Add a txt document via the web interface"): | ||
node.succeed( | ||
"echo 'hello web 16-10-2005' > /tmp/webdoc.txt" | ||
) | ||
node.wait_until_succeeds("curl -u admin:admin -F document=@/tmp/webdoc.txt -fs localhost:28981/api/documents/post_document/") | ||
with subtest("Documents are consumed"): | ||
node.wait_until_succeeds( | ||
"(($(curl -u admin:admin -fs localhost:28981/api/documents/ | jq .count) == 3))" | ||
) | ||
docs = json.loads(node.succeed("curl -u admin:admin -fs localhost:28981/api/documents/"))['results'] | ||
assert "2005-10-16" in docs[0]['created'] | ||
assert "2005-10-16" in docs[1]['created'] | ||
assert "2005-10-16" in docs[2]['created'] | ||
# Detects gunicorn issues, see PR #190888 | ||
with subtest("Document metadata can be accessed"): | ||
metadata = json.loads(node.succeed("curl -u admin:admin -fs localhost:28981/api/documents/1/metadata/")) | ||
assert "original_checksum" in metadata | ||
metadata = json.loads(node.succeed("curl -u admin:admin -fs localhost:28981/api/documents/2/metadata/")) | ||
assert "original_checksum" in metadata | ||
metadata = json.loads(node.succeed("curl -u admin:admin -fs localhost:28981/api/documents/3/metadata/")) | ||
assert "original_checksum" in metadata | ||
test_paperless(simple) | ||
simple.send_monitor_command("quit") | ||
simple.wait_for_shutdown() | ||
test_paperless(postgres) | ||
''; | ||
} | ||
) |