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 package = mkPackageOption pkgs "dex-oidc" { };
51
52 environmentFile = mkOption {
53 type = types.nullOr types.path;
54 default = null;
55 description = ''
56 Environment file (see {manpage}`systemd.exec(5)`
57 "EnvironmentFile=" section for the syntax) to define variables for dex.
58 This option can be used to safely include secret keys into the dex configuration.
59 '';
60 };
61
62 settings = mkOption {
63 type = settingsFormat.type;
64 default = { };
65 example = literalExpression ''
66 {
67 # External url
68 issuer = "http://127.0.0.1:5556/dex";
69 storage = {
70 type = "postgres";
71 config.host = "/var/run/postgres";
72 };
73 web = {
74 http = "127.0.0.1:5556";
75 };
76 enablePasswordDB = true;
77 staticClients = [
78 {
79 id = "oidcclient";
80 name = "Client";
81 redirectURIs = [ "https://example.com/callback" ];
82 secretFile = "/etc/dex/oidcclient"; # The content of `secretFile` will be written into to the config as `secret`.
83 }
84 ];
85 }
86 '';
87 description = ''
88 The available options can be found in
89 [the example configuration](https://github.com/dexidp/dex/blob/v${cfg.package.version}/config.yaml.dist).
90
91 It's also possible to refer to environment variables (defined in [services.dex.environmentFile](#opt-services.dex.environmentFile))
92 using the syntax `$VARIABLE_NAME`.
93 '';
94 };
95 };
96
97 config = mkIf cfg.enable {
98 systemd.services.dex = {
99 description = "dex identity provider";
100 wantedBy = [ "multi-user.target" ];
101 after = [
102 "networking.target"
103 ]
104 ++ (optional (cfg.settings.storage.type == "postgres") "postgresql.target");
105 path = with pkgs; [ replace-secret ];
106 restartTriggers = restartTriggers;
107 serviceConfig = {
108 ExecStart = "${cfg.package}/bin/dex serve /run/dex/config.yaml";
109 ExecStartPre = [
110 "${pkgs.coreutils}/bin/install -m 600 ${configFile} /run/dex/config.yaml"
111 "+${startPreScript}"
112 ];
113
114 RuntimeDirectory = "dex";
115 BindReadOnlyPaths = [
116 "/nix/store"
117 "-/etc/dex"
118 "-/etc/hosts"
119 "-/etc/localtime"
120 "-/etc/nsswitch.conf"
121 "-/etc/resolv.conf"
122 "${config.security.pki.caBundle}:/etc/ssl/certs/ca-certificates.crt"
123 ];
124 BindPaths = optional (cfg.settings.storage.type == "postgres") "/var/run/postgresql";
125 # ProtectClock= adds DeviceAllow=char-rtc r
126 DeviceAllow = "";
127 DynamicUser = true;
128 LockPersonality = true;
129 MemoryDenyWriteExecute = true;
130 NoNewPrivileges = true;
131 PrivateDevices = true;
132 PrivateMounts = true;
133 # Port needs to be exposed to the host network
134 #PrivateNetwork = true;
135 PrivateTmp = true;
136 PrivateUsers = true;
137 ProcSubset = "pid";
138 ProtectClock = true;
139 ProtectHome = true;
140 ProtectHostname = true;
141 ProtectSystem = "strict";
142 ProtectControlGroups = true;
143 ProtectKernelLogs = true;
144 ProtectKernelModules = true;
145 ProtectKernelTunables = true;
146 ProtectProc = "invisible";
147 RestrictAddressFamilies = [
148 "AF_INET"
149 "AF_INET6"
150 "AF_UNIX"
151 ];
152 RestrictNamespaces = true;
153 RestrictRealtime = true;
154 RestrictSUIDSGID = true;
155 SystemCallArchitectures = "native";
156 SystemCallFilter = [
157 "@system-service"
158 "~@privileged @setuid @keyring"
159 ];
160 UMask = "0066";
161 }
162 // optionalAttrs (cfg.environmentFile != null) {
163 EnvironmentFile = cfg.environmentFile;
164 };
165 };
166 };
167
168 # uses attributes of the linked package
169 meta.buildDocsInSandbox = false;
170}