feat(compute01): Deploy docuseal on docuseal.dgnum.eu

This commit is contained in:
Tom Hubrecht 2025-04-14 11:20:36 +02:00
parent ad5d108bc0
commit 41a4b98cc5
Signed by: thubrecht
SSH key fingerprint: SHA256:r+nK/SIcWlJ0zFZJGHtlAoRwq1Rm+WcKAm5ADYMoQPc
4 changed files with 259 additions and 0 deletions

View file

@ -18,6 +18,7 @@ lib.extra.mkConfig {
# INFO: This list needs to stay sorted alphabetically
"arkheon"
"dgsi"
"docuseal"
"ds-fr"
"extranix"
"grafana"

View file

@ -0,0 +1,26 @@
# SPDX-FileCopyrightText: 2025 Tom Hubrecht <tom.hubrecht@dgnum.eu>
#
# SPDX-License-Identifier: EUPL-1.2
let
host = "docuseal.dgnum.eu";
port = 2500;
in
{
nixpkgs.overlays = [
(self: _: {
docuseal = self.callPackage ../../../../pkgs/by-name/docuseal { };
})
];
services.docuseal = {
enable = true;
inherit host port;
};
dgn-web.simpleProxies.docuseal = {
inherit host port;
};
}

View file

@ -0,0 +1,231 @@
# SPDX-FileCopyrightText: 2025 Tom Hubrecht <tom.hubrecht@dgnum.eu>
#
# SPDX-License-Identifier: EUPL-1.2
{
config,
lib,
pkgs,
utils,
...
}:
let
inherit (lib)
getExe'
mkEnableOption
mkIf
mkOption
mkPackageOption
optional
optionalAttrs
;
inherit (lib.types)
attrsOf
bool
nullOr
oneOf
package
path
port
str
;
inherit (utils) escapeSystemdExecArgs;
cfg = config.services.docuseal;
in
{
options.services.docuseal = {
enable = mkEnableOption "docuseal, an open source DocuSign alternative";
package = mkPackageOption pkgs "docuseal" { };
host = mkOption {
type = str;
description = ''
Hostname of the web server.
'';
};
port = mkOption {
type = port;
default = 3000;
description = ''
Listening port for the web server.
'';
};
environment = mkOption {
type = attrsOf (
nullOr (oneOf [
package
path
str
])
);
description = ''
Evironment variables available to Docuseal.
'';
};
environmentFile = mkOption {
type = nullOr path;
default = null;
description = ''
Path to a file containing environment variables.
Required secrets are `SECRET_KEY_BASE` and `OTP_SECRET_KEY`,
which can be generated using `rails secret`.
'';
};
redis = {
createLocally = mkOption {
type = bool;
default = true;
description = ''
Whether to create a local redis automatically.
'';
};
};
interactScript = mkOption {
type = package;
default = pkgs.writeShellApplication {
name = "docuseal";
runtimeInputs = [
cfg.package
config.systemd.package
pkgs.util-linux
];
text = ''
MainPID=$(systemctl show -p MainPID --value docuseal.service)
nsenter -e -a -w -t "$MainPID" -G follow -S follow "$@"
'';
};
description = ''
Script to run docuseal tasks.
'';
};
};
config = mkIf cfg.enable {
services = {
docuseal.environment =
{
RAILS_ENV = "production";
WORKDIR = "/var/lib/docuseal";
DATABASE_URL = "postgresql:///docuseal?host=/run/postgresql";
HOST = cfg.host;
PORT = builtins.toString cfg.port;
}
// (optionalAttrs cfg.redis.createLocally {
REDIS_URL = "unix://${config.services.redis.servers.docuseal.unixSocket}";
});
postgresql = {
enable = true;
ensureDatabases = [ "docuseal" ];
ensureUsers = [
{
name = "docuseal";
ensureDBOwnership = true;
}
];
};
redis.servers.docuseal = mkIf cfg.redis.createLocally {
enable = true;
};
};
environment.systemPackages = [ cfg.interactScript ];
systemd.services.docuseal = {
description = "Docuseal";
after = [
"network.target"
"postgresql.target"
] ++ (optional cfg.redis.createLocally "redis.service");
wantedBy = [ "multi-user.target" ];
inherit (cfg) environment;
path = [ cfg.package ];
serviceConfig =
{
CacheDirectory = "docuseal";
CacheDirectoryMode = "0700";
DynamicUser = true;
EnvironmentFile = optional (cfg.environmentFile != null) cfg.environmentFile;
ExecStart = escapeSystemdExecArgs [
(getExe' cfg.package "bundle")
"exec"
"puma"
"-C"
"${cfg.package}/config/puma.rb"
"--dir"
cfg.package
];
LogsDirectory = "docuseal";
LogsDirectoryMode = "0700";
StateDirectory = "docuseal";
StateDirectoryMode = "0700";
SupplementaryGroups = optional cfg.redis.createLocally "redis-docuseal";
SystemCallFilter = [
"@system-service"
"~@privileged"
];
User = "docuseal";
UMask = "0077";
WorkingDirectory = "/var/lib/docuseal";
# Proc filesystem
ProcSubset = "pid";
ProtectProc = "invisible";
# Capabilities
CapabilityBoundingSet = "";
# Security
NoNewPrivileges = true;
# Sandboxing
ProtectSystem = "strict";
ProtectHome = true;
PrivateTmp = true;
PrivateDevices = true;
PrivateUsers = cfg.port >= 1024;
ProtectClock = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectControlGroups = true;
RestrictAddressFamilies = [
"AF_UNIX"
"AF_INET"
"AF_INET6"
"AF_NETLINK"
];
RestrictNamespaces = true;
LockPersonality = true;
MemoryDenyWriteExecute = false;
RestrictRealtime = true;
RestrictSUIDSGID = true;
RemoveIPC = true;
PrivateMounts = true;
# System Call Filtering
SystemCallArchitectures = "native";
}
// optionalAttrs (cfg.port < 1024) {
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
};
};
};
}

View file

@ -74,6 +74,7 @@ let
"code" # Collabora Online
"demarches" # Démarches Normaliennes
"docs" # Outline
"docuseal" # Docuseal
"grafana" # Grafana
"netbox-v2" # Netbox
"nms" # LibreNMS