1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8with lib;
9
10let
11 cfg = config.services.dex;
12 fixClient =
13 client:
14 if client ? secretFile then
15 (
16 (builtins.removeAttrs client [ "secretFile" ])
17 // {
18 secret = client.secretFile;
19 }
20 )
21 else
22 client;
23 filteredSettings = mapAttrs (
24 n: v: if n == "staticClients" then (builtins.map fixClient v) else v
25 ) cfg.settings;
26 secretFiles = flatten (
27 builtins.map (c: optional (c ? secretFile) c.secretFile) (cfg.settings.staticClients or [ ])
28 );
29
30 settingsFormat = pkgs.formats.yaml { };
31 configFile = settingsFormat.generate "config.yaml" filteredSettings;
32
33 startPreScript = pkgs.writeShellScript "dex-start-pre" (
34 concatStringsSep "\n" (
35 map (file: ''
36 replace-secret '${file}' '${file}' /run/dex/config.yaml
37 '') secretFiles
38 )
39 );
40
41 restartTriggers =
42 [ ]
43 ++ (optionals (cfg.environmentFile != null) [ cfg.environmentFile ])
44 ++ (filter (file: builtins.typeOf file == "path") secretFiles);
45in
46{
47 options.services.dex = {
48 enable = mkEnableOption "the OpenID Connect and OAuth2 identity provider";
49
50 environmentFile = mkOption {
51 type = types.nullOr types.path;
52 default = null;
53 description = ''
54 Environment file (see {manpage}`systemd.exec(5)`
55 "EnvironmentFile=" section for the syntax) to define variables for dex.
56 This option can be used to safely include secret keys into the dex configuration.
57 '';
58 };
59
60 settings = mkOption {
61 type = settingsFormat.type;
62 default = { };
63 example = literalExpression ''
64 {
65 # External url
66 issuer = "http://127.0.0.1:5556/dex";
67 storage = {
68 type = "postgres";
69 config.host = "/var/run/postgres";
70 };
71 web = {
72 http = "127.0.0.1:5556";
73 };
74 enablePasswordDB = true;
75 staticClients = [
76 {
77 id = "oidcclient";
78 name = "Client";
79 redirectURIs = [ "https://example.com/callback" ];
80 secretFile = "/etc/dex/oidcclient"; # The content of `secretFile` will be written into to the config as `secret`.
81 }
82 ];
83 }
84 '';
85 description = ''
86 The available options can be found in
87 [the example configuration](https://github.com/dexidp/dex/blob/v${pkgs.dex-oidc.version}/config.yaml.dist).
88
89 It's also possible to refer to environment variables (defined in [services.dex.environmentFile](#opt-services.dex.environmentFile))
90 using the syntax `$VARIABLE_NAME`.
91 '';
92 };
93 };
94
95 config = mkIf cfg.enable {
96 systemd.services.dex = {
97 description = "dex identity provider";
98 wantedBy = [ "multi-user.target" ];
99 after = [
100 "networking.target"
101 ] ++ (optional (cfg.settings.storage.type == "postgres") "postgresql.service");
102 path = with pkgs; [ replace-secret ];
103 restartTriggers = restartTriggers;
104 serviceConfig =
105 {
106 ExecStart = "${pkgs.dex-oidc}/bin/dex serve /run/dex/config.yaml";
107 ExecStartPre = [
108 "${pkgs.coreutils}/bin/install -m 600 ${configFile} /run/dex/config.yaml"
109 "+${startPreScript}"
110 ];
111
112 RuntimeDirectory = "dex";
113 BindReadOnlyPaths = [
114 "/nix/store"
115 "-/etc/dex"
116 "-/etc/hosts"
117 "-/etc/localtime"
118 "-/etc/nsswitch.conf"
119 "-/etc/resolv.conf"
120 "${config.security.pki.caBundle}:/etc/ssl/certs/ca-certificates.crt"
121 ];
122 BindPaths = optional (cfg.settings.storage.type == "postgres") "/var/run/postgresql";
123 # ProtectClock= adds DeviceAllow=char-rtc r
124 DeviceAllow = "";
125 DynamicUser = true;
126 LockPersonality = true;
127 MemoryDenyWriteExecute = true;
128 NoNewPrivileges = true;
129 PrivateDevices = true;
130 PrivateMounts = true;
131 # Port needs to be exposed to the host network
132 #PrivateNetwork = true;
133 PrivateTmp = true;
134 PrivateUsers = true;
135 ProcSubset = "pid";
136 ProtectClock = true;
137 ProtectHome = true;
138 ProtectHostname = true;
139 ProtectSystem = "strict";
140 ProtectControlGroups = true;
141 ProtectKernelLogs = true;
142 ProtectKernelModules = true;
143 ProtectKernelTunables = true;
144 ProtectProc = "invisible";
145 RestrictAddressFamilies = [
146 "AF_INET"
147 "AF_INET6"
148 "AF_UNIX"
149 ];
150 RestrictNamespaces = true;
151 RestrictRealtime = true;
152 RestrictSUIDSGID = true;
153 SystemCallArchitectures = "native";
154 SystemCallFilter = [
155 "@system-service"
156 "~@privileged @setuid @keyring"
157 ];
158 UMask = "0066";
159 }
160 // optionalAttrs (cfg.environmentFile != null) {
161 EnvironmentFile = cfg.environmentFile;
162 };
163 };
164 };
165
166 # uses attributes of the linked package
167 meta.buildDocsInSandbox = false;
168}