1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.services.sssd;
9
10 dataDir = "/var/lib/sssd";
11 settingsFile = "${dataDir}/sssd.conf";
12 settingsFileUnsubstituted = pkgs.writeText "${dataDir}/sssd-unsubstituted.conf" cfg.config;
13in
14{
15 options = {
16 services.sssd = {
17 enable = lib.mkEnableOption "the System Security Services Daemon";
18
19 config = lib.mkOption {
20 type = lib.types.lines;
21 description = "Contents of {file}`sssd.conf`.";
22 default = ''
23 [sssd]
24 services = nss, pam
25 domains = shadowutils
26
27 [nss]
28
29 [pam]
30
31 [domain/shadowutils]
32 id_provider = proxy
33 proxy_lib_name = files
34 auth_provider = proxy
35 proxy_pam_target = sssd-shadowutils
36 proxy_fast_alias = True
37 '';
38 };
39
40 sshAuthorizedKeysIntegration = lib.mkOption {
41 type = lib.types.bool;
42 default = false;
43 description = ''
44 Whether to make sshd look up authorized keys from SSS.
45 For this to work, the `ssh` SSS service must be enabled in the sssd configuration.
46 '';
47 };
48
49 kcm = lib.mkOption {
50 type = lib.types.bool;
51 default = false;
52 description = ''
53 Whether to use SSS as a Kerberos Cache Manager (KCM).
54 Kerberos will be configured to cache credentials in SSS.
55 '';
56 };
57 environmentFile = lib.mkOption {
58 type = lib.types.nullOr lib.types.path;
59 default = null;
60 description = ''
61 Environment file as defined in {manpage}`systemd.exec(5)`.
62
63 Secrets may be passed to the service without adding them to the world-readable
64 Nix store, by specifying placeholder variables as the option value in Nix and
65 setting these variables accordingly in the environment file.
66
67 ```
68 # snippet of sssd-related config
69 [domain/LDAP]
70 ldap_default_authtok = $SSSD_LDAP_DEFAULT_AUTHTOK
71 ```
72
73 ```
74 # contents of the environment file
75 SSSD_LDAP_DEFAULT_AUTHTOK=verysecretpassword
76 ```
77 '';
78 };
79 };
80 };
81 config = lib.mkMerge [
82 (lib.mkIf cfg.enable {
83 # For `sssctl` to work.
84 environment.etc."sssd/sssd.conf".source = settingsFile;
85 environment.etc."sssd/conf.d".source = "${dataDir}/conf.d";
86
87 systemd.services.sssd = {
88 description = "System Security Services Daemon";
89 wantedBy = [ "multi-user.target" ];
90 before = [
91 "systemd-user-sessions.service"
92 "nss-user-lookup.target"
93 ];
94 after = [
95 "network-online.target"
96 "nscd.service"
97 ];
98 requires = [
99 "network-online.target"
100 "nscd.service"
101 ];
102 wants = [ "nss-user-lookup.target" ];
103 restartTriggers = [
104 config.environment.etc."nscd.conf".source
105 settingsFileUnsubstituted
106 ];
107 environment.LDB_MODULES_PATH = "${pkgs.ldb}/modules/ldb:${pkgs.sssd}/modules/ldb";
108 serviceConfig = {
109 # systemd needs to start sssd directly for "NotifyAccess=main" to work
110 ExecStart = "${pkgs.sssd}/bin/sssd -i -c ${settingsFile}";
111 Type = "notify";
112 NotifyAccess = "main";
113 PIDFile = "/run/sssd.pid";
114 CapabilityBoundingSet = [
115 "CAP_IPC_LOCK"
116 "CAP_CHOWN"
117 "CAP_DAC_READ_SEARCH"
118 "CAP_KILL"
119 "CAP_NET_ADMIN"
120 "CAP_SYS_NICE"
121 "CAP_FOWNER"
122 "CAP_SETGID"
123 "CAP_SETUID"
124 "CAP_SYS_ADMIN"
125 "CAP_SYS_RESOURCE"
126 "CAP_BLOCK_SUSPEND"
127 ];
128 Restart = "on-abnormal";
129 StateDirectory = baseNameOf dataDir;
130 # We cannot use LoadCredential here because it's not available in ExecStartPre
131 EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile;
132 };
133 unitConfig = {
134 StartLimitIntervalSec = "50s";
135 StartLimitBurst = 5;
136 };
137 preStart = ''
138 mkdir -p "${dataDir}/conf.d"
139 [ -f ${settingsFile} ] && rm -f ${settingsFile}
140 old_umask=$(umask)
141 umask 0177
142 ${pkgs.envsubst}/bin/envsubst \
143 -o ${settingsFile} \
144 -i ${settingsFileUnsubstituted}
145 umask $old_umask
146 mkdir -p /var/lib/sss/{pubconf,db,mc,pipes,gpo_cache,secrets} /var/lib/sss/pipes/private /var/lib/sss/pubconf/krb5.include.d
147 '';
148 };
149
150 system.nssModules = [ pkgs.sssd ];
151 system.nssDatabases = {
152 group = [ "sss" ];
153 passwd = [ "sss" ];
154 services = [ "sss" ];
155 shadow = [ "sss" ];
156 };
157 services.dbus.packages = [ pkgs.sssd ];
158 })
159
160 (lib.mkIf cfg.kcm {
161 systemd.services.sssd-kcm = {
162 description = "SSSD Kerberos Cache Manager";
163 requires = [ "sssd-kcm.socket" ];
164 serviceConfig = {
165 ExecStartPre = "-${pkgs.sssd}/bin/sssd --genconf-section=kcm";
166 ExecStart = "${pkgs.sssd}/libexec/sssd/sssd_kcm --uid 0 --gid 0";
167 CapabilityBoundingSet = [
168 "CAP_IPC_LOCK"
169 "CAP_CHOWN"
170 "CAP_DAC_READ_SEARCH"
171 "CAP_FOWNER"
172 "CAP_SETGID"
173 "CAP_SETUID"
174 ];
175 };
176 restartTriggers = [
177 settingsFileUnsubstituted
178 ];
179 };
180 systemd.sockets.sssd-kcm = {
181 description = "SSSD Kerberos Cache Manager responder socket";
182 wantedBy = [ "sockets.target" ];
183 # Matches the default in MIT krb5 and Heimdal:
184 # https://github.com/krb5/krb5/blob/krb5-1.19.3-final/src/include/kcm.h#L43
185 listenStreams = [ "/var/run/.heim_org.h5l.kcm-socket" ];
186 };
187 security.krb5.settings.libdefaults.default_ccache_name = "KCM:";
188 })
189
190 (lib.mkIf cfg.sshAuthorizedKeysIntegration {
191 # Ugly: sshd refuses to start if a store path is given because /nix/store is group-writable.
192 # So indirect by a symlink.
193 environment.etc."ssh/authorized_keys_command" = {
194 mode = "0755";
195 text = ''
196 #!/bin/sh
197 exec ${pkgs.sssd}/bin/sss_ssh_authorizedkeys "$@"
198 '';
199 };
200 services.openssh.authorizedKeysCommand = "/etc/ssh/authorized_keys_command";
201 services.openssh.authorizedKeysCommandUser = "nobody";
202 })
203 ];
204
205 meta.maintainers = with lib.maintainers; [ bbigras ];
206}