Add a Nix flake with NixOS module and test
This commit is contained in:
parent
8324adaef6
commit
10b6b350bd
81
flake.lock
generated
Normal file
81
flake.lock
generated
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"naersk": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1713520724,
|
||||||
|
"narHash": "sha256-CO8MmVDmqZX2FovL75pu5BvwhW+Vugc7Q6ze7Hj8heI=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "naersk",
|
||||||
|
"rev": "c5037590290c6c7dae2e42e7da1e247e54ed2d49",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "naersk",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1716218643,
|
||||||
|
"narHash": "sha256-i/E7gzQybvcGAYDRGDl39WL6yVk30Je/NXypBz6/nmM=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "a8695cbd09a7ecf3376bd62c798b9864d20f86ee",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"ref": "nixos-23.11",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"naersk": "naersk",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"utils": "utils"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1710146030,
|
||||||
|
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
77
flake.nix
Normal file
77
flake.nix
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
{
|
||||||
|
description = "idCoop: lightweight identity provider";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "nixpkgs/nixos-23.11";
|
||||||
|
|
||||||
|
utils.url = "github:numtide/flake-utils";
|
||||||
|
|
||||||
|
naersk.url = "github:nix-community/naersk";
|
||||||
|
naersk.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, utils, naersk }:
|
||||||
|
utils.lib.eachDefaultSystem (system: let
|
||||||
|
pkgs = nixpkgs.legacyPackages."${system}";
|
||||||
|
lib = pkgs.lib;
|
||||||
|
|
||||||
|
naersk' = pkgs.callPackage naersk {
|
||||||
|
};
|
||||||
|
|
||||||
|
idcoop = naersk'.buildPackage {
|
||||||
|
pname = "idcoop";
|
||||||
|
|
||||||
|
# filter out .nix files from the input so they don't cause a recompilation when changed
|
||||||
|
src = lib.cleanSourceWith {
|
||||||
|
filter = path: type: !lib.hasSuffix ".nix" path;
|
||||||
|
src = ./.;
|
||||||
|
};
|
||||||
|
|
||||||
|
nativeBuildInputs = [
|
||||||
|
pkgs.makeWrapper
|
||||||
|
|
||||||
|
pkgs.pkg-config
|
||||||
|
pkgs.openssl
|
||||||
|
];
|
||||||
|
|
||||||
|
postInstall = ''
|
||||||
|
mkdir -p $out/share/idcoop
|
||||||
|
echo :::
|
||||||
|
ls
|
||||||
|
echo :::
|
||||||
|
cp -r static templates translations -t $out/share/idcoop/
|
||||||
|
|
||||||
|
wrapProgram $out/bin/idcoop --set HORNBEAM_BASE $out/share/idcoop
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
# `nix build`
|
||||||
|
packages = {
|
||||||
|
inherit idcoop;
|
||||||
|
default = idcoop;
|
||||||
|
};
|
||||||
|
|
||||||
|
# `nix run`
|
||||||
|
apps = rec {
|
||||||
|
idcoop = utils.lib.mkApp {
|
||||||
|
drv = idcoop;
|
||||||
|
};
|
||||||
|
default = idcoop;
|
||||||
|
};
|
||||||
|
}) // (let
|
||||||
|
forAllNixosSystems = nixpkgs.lib.genAttrs ["x86_64-linux" "aarch64-linux"];
|
||||||
|
in {
|
||||||
|
# NixOS Modules
|
||||||
|
nixosModules = {
|
||||||
|
idcoop = import ./nixos_module.nix self;
|
||||||
|
};
|
||||||
|
checks = forAllNixosSystems (system: let
|
||||||
|
checkArgs = {
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
inherit self;
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
starts = import ./nixos_tests/starts.nix checkArgs;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
192
nixos_module.nix
Normal file
192
nixos_module.nix
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
flake: { config, pkgs, ... }:
|
||||||
|
let
|
||||||
|
inherit (flake.packages.${pkgs.stdenv.hostPlatform.system}) idcoop;
|
||||||
|
inherit (pkgs.lib) mkOption mkDefault mkIf types literalExpression mdDoc;
|
||||||
|
inherit (pkgs.writers) writeTOML;
|
||||||
|
inherit (builtins) mapAttrs;
|
||||||
|
|
||||||
|
defaultUser = "idcoop";
|
||||||
|
|
||||||
|
cfg = config.services.idcoop;
|
||||||
|
format = pkgs.formats.toml { };
|
||||||
|
|
||||||
|
oidcClientSubmodule = types.submodule {
|
||||||
|
# freeformType = format.type; — one day we may want to enable freeform types, but for now just keep it strongly defined
|
||||||
|
options = {
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = ''
|
||||||
|
User-friendly name of the OIDC Client.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
redirect_uris = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
description = ''
|
||||||
|
List of redirect URIs that the client can use to redirect login attempts back to itself.
|
||||||
|
Consult the documentation for the other service if you aren't sure.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
allow_user_classes = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
description = ''
|
||||||
|
List of user classes which are authorised (allowed) to use this client (access this service).
|
||||||
|
As of idCoop v0.0.1, this setting is unimplemented and has no effect.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.services.idcoop = {
|
||||||
|
enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Whether to enable idCoop, a simple identity provider.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
configurePostgres = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
Whether to configure a Postgres database for idCoop.
|
||||||
|
Enabled by default.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
user = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = defaultUser;
|
||||||
|
description = ''
|
||||||
|
User to run the service as.
|
||||||
|
Will be created automatically if it is left at the default.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
group = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = defaultUser;
|
||||||
|
description = ''
|
||||||
|
User to run the service as.
|
||||||
|
Will be created automatically if it is left at the default.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
secretsPath = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = builtins.toFile "blank.toml" "";
|
||||||
|
description = ''
|
||||||
|
Path to a file containing secrets. This file should be kept out of the Nix store.
|
||||||
|
Consult the idCoop documentation for the format of the secrets file.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
settings = mkOption {
|
||||||
|
default = {};
|
||||||
|
description = "idCoop configuration.";
|
||||||
|
|
||||||
|
type = types.submodule {
|
||||||
|
# freeformType = format.type; — one day we may want to enable freeform types, but for now just keep it strongly defined
|
||||||
|
options = {
|
||||||
|
listen = {
|
||||||
|
bind = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "127.0.0.1:8072";
|
||||||
|
description = ''
|
||||||
|
Host and port combination upon which to bind the web interface.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
public_base_uri = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "http://${cfg.settings.listen.bind}";
|
||||||
|
defaultText = "`http://{listen.bind}`";
|
||||||
|
description = ''
|
||||||
|
Public-facing HTTP(S) base URL.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
oidc = {
|
||||||
|
issuer = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = cfg.settings.listen.public_base_uri;
|
||||||
|
defaultText = "`listen.public_base_uri`";
|
||||||
|
description = ''
|
||||||
|
The identity provider's 'issuer' identifier, as used in OpenID Connect.
|
||||||
|
This should be configured in clients (relying parties) and should likely not be changed.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
rsa_keypair = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
description = ''
|
||||||
|
Path to an RSA keypair used for signing JSON Web Tokens.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
clients = mkOption {
|
||||||
|
type = types.attrsOf oidcClientSubmodule;
|
||||||
|
default = {};
|
||||||
|
description = ''
|
||||||
|
OpenID Connect 'clients' (also known as relying parties).
|
||||||
|
These entries are for the different services you want users to be able to log in to using idCoop.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
postgres = {
|
||||||
|
connect = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "postgres:";
|
||||||
|
description = mdDoc ''
|
||||||
|
Connection string for the Postgres database. The default of `postgres:` uses the [libpq environment variables] to form a connection;
|
||||||
|
usually this by default connects to the local UNIX socket with the current user's name as a username and database name,
|
||||||
|
if no environment variables are set.
|
||||||
|
|
||||||
|
[libpq environment variables]: https://www.postgresql.org/docs/current/libpq-envars.html
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
users.users.idcoop = mkIf (cfg.enable && cfg.user == defaultUser) {
|
||||||
|
isSystemUser = true;
|
||||||
|
group = cfg.group;
|
||||||
|
home = mkDefault "/var/lib/idcoop";
|
||||||
|
createHome = true;
|
||||||
|
};
|
||||||
|
users.groups.idcoop = mkIf (cfg.enable && cfg.group == defaultUser) {};
|
||||||
|
|
||||||
|
systemd.services.idcoop = mkIf cfg.enable {
|
||||||
|
description = "idCoop: simple identity provider";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
after = [ "networking.target" "network-online.target" "postgresql.service" ];
|
||||||
|
|
||||||
|
serviceConfig =
|
||||||
|
let
|
||||||
|
configPath = writeTOML "idcoop_config.toml" cfg.settings;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
ExecStart = "${idcoop}/bin/idcoop --config ${configPath} --secrets ${cfg.secretsPath} serve";
|
||||||
|
User = cfg.user;
|
||||||
|
Group = cfg.group;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.postgresql = mkIf cfg.configurePostgres {
|
||||||
|
ensureUsers = [
|
||||||
|
{
|
||||||
|
name = "idcoop";
|
||||||
|
ensureDBOwnership = true;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
ensureDatabases = ["idcoop"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
21
nixos_tests/lib.nix
Normal file
21
nixos_tests/lib.nix
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# tests/lib.nix
|
||||||
|
# The first argument to this function is the test module itself
|
||||||
|
test:
|
||||||
|
# These arguments are provided by `flake.nix` on import, see checkArgs
|
||||||
|
{ pkgs, self}:
|
||||||
|
let
|
||||||
|
inherit (pkgs) lib;
|
||||||
|
# this imports the nixos library that contains our testing framework
|
||||||
|
nixos-lib = import (pkgs.path + "/nixos/lib") {};
|
||||||
|
in
|
||||||
|
(nixos-lib.runTest {
|
||||||
|
hostPkgs = pkgs;
|
||||||
|
# This speeds up the evaluation by skipping evaluating documentation (optional)
|
||||||
|
defaults.documentation.enable = lib.mkDefault false;
|
||||||
|
# This makes `self` available in the NixOS configuration of our virtual machines.
|
||||||
|
# This is useful for referencing modules or packages from your own flake
|
||||||
|
# as well as importing from other flakes.
|
||||||
|
node.specialArgs = { inherit self; };
|
||||||
|
imports = [ test ];
|
||||||
|
}).config.result
|
||||||
|
|
71
nixos_tests/starts.nix
Normal file
71
nixos_tests/starts.nix
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
let
|
||||||
|
rsaKeypair = ''
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDDu6acOa+3ae2S
|
||||||
|
0llp5oMsXjBMd5QJeQJCcY5Q9NAITF2U9VBwAiMf2wmaTZ1aWWFGSb/zWef7Hx1e
|
||||||
|
qNhsK9MYL+QdJih2I+KpMtDWm7hhy9FtCHVc9i1z9PruXb0om2jDWLuBkPdCqJZT
|
||||||
|
C58ObZKmgL4OH5F1Qv5JR/ZX21OjLolXPJo1sonLv9mlgufvhUmnC17onSSqFLBA
|
||||||
|
nhUedjbfdnLShkp0xa8G0nW7Ls7Idaxyo8M5S2M+azJyLI87eqjjfz0yIW0Am890
|
||||||
|
mRa81hO2D+YNcVA2wIE9MEI/ie480YxLQ0VHCjX4DcVir4ceExysYkdL8VK+U14g
|
||||||
|
NO67k4NVAgMBAAECggEABnseJyoZ0V7miOgCIemKClwMCVwkQLQLCRwtdCzG/p9Y
|
||||||
|
sef1g9/uPc3I4Z0USruO5v7mJi6h6cS7+jhpAhvpX3GmgfiTemXxyVxvYcvCLSrM
|
||||||
|
gmm3SR61npNMA7yC2OdcbqtvefjM1x4x7AoEeDvUkULOCDWvYUyYkuCZHYubl1mS
|
||||||
|
Rtcp9rxzky2tjdp8CHySBa9Kz9LEjWdFGky7g3vSyqZtw6tkK5CTwMPb9aHwiEq1
|
||||||
|
yDWCbqAPPnb300dXSqx7z3AcsxBi/lpCs79fQS1vSJ1/L9POpYxX0SMtffkR5+Bl
|
||||||
|
Mkg9dZUVenfbP4n40FdMypTTX2KJiMkc8+f0+Tz9QQKBgQDrdT7xs0lqMeZWp2rD
|
||||||
|
y2KLzRl0iO/+yKse+BgkeBggZ/Vh2TeF9ylTRYdmimxkJ8eZRipTr0F64BS/LPEk
|
||||||
|
RgWviuf9dUl0gyuhYTOJgUw1wcgtB6e+04UKEUsQW6JoNZekkM+xTa7FskLmlJ4J
|
||||||
|
zzowjF1lgJeEyX1tvWXKIVe+/QKBgQDUzy5nZoUqBrVTYxL2uadIUh8oiBKPU93U
|
||||||
|
Gz3DUq90yfDa7lFhwMRQRXfNqGUy6tshsaF4fT1b62hZDSz1OH3h/y1LKQOdF5kc
|
||||||
|
JJyk/4b7NJna16kwBLzWje5SjQKr51aQWU8JftZ5/8uck2j7vMi+mgwzpG45J7kv
|
||||||
|
Q1I5decBOQKBgFq1sKotB/uBfdukY91KXYy+VzAuEUd2x3YG3kYufhz97+riZCGY
|
||||||
|
NrN99cvrSBbNvHewMF5NBkzwRw3foob28vnN6dIbfVEFt6lUaSZwSYvsO9IdQOKj
|
||||||
|
Wn2ma+TBaK/89Y7QuzLzWoGPS3bJipj83M4XRWP1RmpBtbCxZqWYctWBAoGAMZPi
|
||||||
|
16wGsffGHpsiO+CcnDilkafByypar6N5DBwjTC4PsrF6vC9QjPLiKkNk8CvOyVa8
|
||||||
|
q3lh5hw9vyFWq/pxOUldn/j6Iorw3KGa7MWrCLMEdPtxKwKvi7ydHRZE3Q+UFyT3
|
||||||
|
SNsH1HxHTz74Yk1k5yK0XQOduisK9XvVmBVjr+ECgYEAyoSbo/1cyLKWgrIr0K/f
|
||||||
|
stiKL9SmBmYbaGaxtQToB5Hnqso7Hz5YEDlrcr8s1ukEFghgeNYuDYw3ZKKGGfZm
|
||||||
|
yVQKAt8ouoO8rfkLrtt0H+/0uJgouhewDEqf/O+MfzwDnFcT89J5ZTEf+9n6pjry
|
||||||
|
fuiQnuwEsPYGCCFuWWlrdHQ=
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
|
'';
|
||||||
|
|
||||||
|
|
||||||
|
in
|
||||||
|
(import ./lib.nix) {
|
||||||
|
name = "idcoop-starts";
|
||||||
|
nodes = {
|
||||||
|
# `self` here is set by using specialArgs in `lib.nix`
|
||||||
|
node1 = { self, pkgs, ... }: {
|
||||||
|
imports = [ self.nixosModules.idcoop ];
|
||||||
|
environment.systemPackages = [ pkgs.curl pkgs.jq ];
|
||||||
|
|
||||||
|
services.idcoop = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
settings = {
|
||||||
|
oidc.rsa_keypair = builtins.toFile "rsa_keypair.pem" rsaKeypair;
|
||||||
|
|
||||||
|
# TODO for some reason the default doesn't work ???
|
||||||
|
postgres.connect = "postgres://%2Frun%2fpostgresql";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.postgresql = {
|
||||||
|
enable = true;
|
||||||
|
enableTCPIP = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
# This is the test code that will check if our service is running correctly:
|
||||||
|
testScript = ''
|
||||||
|
start_all()
|
||||||
|
# wait for our service to start
|
||||||
|
node1.wait_for_unit("idcoop")
|
||||||
|
# wait for the port to open
|
||||||
|
node1.wait_for_open_port(8072)
|
||||||
|
# check the OpenID Connect discovery is served
|
||||||
|
output = node1.succeed("curl http://localhost:8072/.well-known/openid-configuration | jq -e .")
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
|
0
static/.gitkeep
Normal file
0
static/.gitkeep
Normal file
0
templates/.gitkeep
Normal file
0
templates/.gitkeep
Normal file
0
translations/.gitkeep
Normal file
0
translations/.gitkeep
Normal file
Loading…
Reference in New Issue
Block a user