infrastructure/machines/compute01/satosa/module.nix

200 lines
4.9 KiB
Nix

{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
mkDefault
mkEnableOption
mkIf
mkOption
types
;
yamlFormat = pkgs.formats.yaml { };
configFile = yamlFormat.generate "proxy_conf.yaml" cfg.proxyConf;
cfg = config.services.satosa;
mkYamlFiles =
files: builtins.attrValues (builtins.mapAttrs (name: yamlFormat.generate "${name}.yaml") files);
pyEnv = cfg.package.python.withPackages (ps: [
cfg.package
ps.gunicorn
]);
in
{
options.services.satosa = {
enable = mkEnableOption "SATOSA, a SAML and OIDC proxy.";
package = mkOption {
type = types.package;
default = import ./package { inherit pkgs; };
};
port = mkOption {
type = types.port;
default = 8080;
};
host = mkOption { type = types.str; };
workers = mkOption {
type = types.int;
default = 1;
};
configureNginx = mkOption {
type = types.bool;
default = true;
};
proxyConf = mkOption {
inherit (yamlFormat) type;
default = { };
};
envFile = mkOption {
type = with types; nullOr path;
default = null;
};
internalAttributes = mkOption {
inherit (yamlFormat) type;
default = { };
};
frontendModules = mkOption {
type = types.attrsOf yamlFormat.type;
default = { };
};
backendModules = mkOption {
type = types.attrsOf yamlFormat.type;
default = { };
};
microServices = mkOption {
type = types.attrsOf yamlFormat.type;
default = { };
};
};
config = mkIf cfg.enable {
services.satosa.proxyConf = builtins.mapAttrs (_: mkDefault) {
BASE = "https://${cfg.host}";
COOKIE_STATE_NAME = "satosa_state";
COOKIE_SECURE = true;
COOKIE_HTTPONLY = true;
COOKIE_SAMESITE = "None";
COOKIE_MAX_AGE = "1200";
CONTEXT_STATE_DELETE = true;
INTERNAL_ATTRIBUTES = yamlFormat.generate "internal_attributes.yaml" {
attributes = cfg.internalAttributes;
};
BACKEND_MODULES = mkYamlFiles cfg.backendModules;
FRONTEND_MODULES = mkYamlFiles cfg.frontendModules;
MICRO_SERVICES = mkYamlFiles cfg.microServices;
LOGGING = {
version = 1;
formatters.simple.format = "[%(asctime)s] [%(levelname)s] [%(name)s.%(funcName)s] %(message)s";
handlers.stdout = {
class = "logging.StreamHandler";
stream = "ext://sys.stdout";
level = "DEBUG";
formatter = "simple";
};
loggers = {
satosa.level = "DEBUG";
saml2.level = "DEBUG";
oidcendpoint.level = "DEBUG";
pyop.level = "DEBUG";
oic.level = "DEBUG";
root = {
level = "DEBUG";
handlers = [ "stdout" ];
};
};
};
};
systemd.services = {
satosa-metadata = {
script = ''
umask 077
# Generate a secret key/certificate if none are present
mkdir -p ssl
if [ ! -f "ssl/.created" ]; then
${pkgs.openssl}/bin/openssl req -x509 \
-newkey rsa:2048 \
-keyout ssl/key.pem \
-out ssl/cert.pem \
-sha256 \
-days 3650 \
-nodes \
-subj "/C=FR/ST=Île de France/L=Paris/O=DGNum/OU=./CN=saml-idp.dgnum.eu" \
&& touch ssl/.created
fi
mkdir -p metadata
${cfg.package}/bin/satosa-saml-metadata \
--dir metadata \
--sign ${configFile} ssl/key.pem ssl/cert.pem
'';
serviceConfig = {
Type = "oneshot";
User = "satosa";
Group = "satosa";
DynamicUser = true;
StateDirectory = "satosa";
WorkingDirectory = "/var/lib/satosa";
EnvironmentFile = lib.optional (cfg.envFile != null) cfg.envFile;
};
};
satosa = {
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
wants = [ "satosa-metadata.service" ];
serviceConfig = {
User = "satosa";
Group = "satosa";
DynamicUser = true;
Type = "notify";
RuntimeDirectory = "satosa";
StateDirectory = "satosa";
WorkingDirectory = cfg.package;
ExecStart = ''
${pyEnv}/bin/gunicorn \
-w ${builtins.toString cfg.workers} \
-b 127.0.0.1:${builtins.toString cfg.port} \
--pythonpath ${pyEnv}/${pkgs.python3.sitePackages} \
satosa.wsgi:app
'';
ExecReload = "${pkgs.util-linux}/bin/kill -s HUP $MAINPID";
KillMode = "mixed";
TimeoutStopSec = "5";
EnvironmentFile = lib.optional (cfg.envFile != null) cfg.envFile;
};
environment = {
SATOSA_CONFIG = configFile;
};
};
};
users.users.satosa = {
isSystemUser = true;
group = "satosa";
home = "/var/lib/satosa";
};
users.groups.satosa = { };
};
}