at 23.11-pre 7.2 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7with lib; let 8 cfg = config.security.ipa; 9 pyBool = x: 10 if x 11 then "True" 12 else "False"; 13 14 ldapConf = pkgs.writeText "ldap.conf" '' 15 # Turning this off breaks GSSAPI used with krb5 when rdns = false 16 SASL_NOCANON on 17 18 URI ldaps://${cfg.server} 19 BASE ${cfg.basedn} 20 TLS_CACERT /etc/ipa/ca.crt 21 ''; 22 nssDb = 23 pkgs.runCommand "ipa-nssdb" 24 { 25 nativeBuildInputs = [pkgs.nss.tools]; 26 } '' 27 mkdir -p $out 28 certutil -d $out -N --empty-password 29 certutil -d $out -A --empty-password -n "${cfg.realm} IPA CA" -t CT,C,C -i ${cfg.certificate} 30 ''; 31in { 32 options = { 33 security.ipa = { 34 enable = mkEnableOption (lib.mdDoc "FreeIPA domain integration"); 35 36 certificate = mkOption { 37 type = types.package; 38 description = lib.mdDoc '' 39 IPA server CA certificate. 40 41 Use `nix-prefetch-url http://$server/ipa/config/ca.crt` to 42 obtain the file and the hash. 43 ''; 44 example = literalExpression '' 45 pkgs.fetchurl { 46 url = http://ipa.example.com/ipa/config/ca.crt; 47 sha256 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; 48 }; 49 ''; 50 }; 51 52 domain = mkOption { 53 type = types.str; 54 example = "example.com"; 55 description = lib.mdDoc "Domain of the IPA server."; 56 }; 57 58 realm = mkOption { 59 type = types.str; 60 example = "EXAMPLE.COM"; 61 description = lib.mdDoc "Kerberos realm."; 62 }; 63 64 server = mkOption { 65 type = types.str; 66 example = "ipa.example.com"; 67 description = lib.mdDoc "IPA Server hostname."; 68 }; 69 70 basedn = mkOption { 71 type = types.str; 72 example = "dc=example,dc=com"; 73 description = lib.mdDoc "Base DN to use when performing LDAP operations."; 74 }; 75 76 offlinePasswords = mkOption { 77 type = types.bool; 78 default = true; 79 description = lib.mdDoc "Whether to store offline passwords when the server is down."; 80 }; 81 82 cacheCredentials = mkOption { 83 type = types.bool; 84 default = true; 85 description = lib.mdDoc "Whether to cache credentials."; 86 }; 87 88 ifpAllowedUids = mkOption { 89 type = types.listOf types.string; 90 default = ["root"]; 91 description = lib.mdDoc "A list of users allowed to access the ifp dbus interface."; 92 }; 93 94 dyndns = { 95 enable = mkOption { 96 type = types.bool; 97 default = true; 98 description = lib.mdDoc "Whether to enable FreeIPA automatic hostname updates."; 99 }; 100 101 interface = mkOption { 102 type = types.str; 103 example = "eth0"; 104 default = "*"; 105 description = lib.mdDoc "Network interface to perform hostname updates through."; 106 }; 107 }; 108 109 chromiumSupport = mkOption { 110 type = types.bool; 111 default = true; 112 description = lib.mdDoc "Whether to whitelist the FreeIPA domain in Chromium."; 113 }; 114 }; 115 }; 116 117 config = mkIf cfg.enable { 118 assertions = [ 119 { 120 assertion = !config.krb5.enable; 121 message = "krb5 must be disabled through `krb5.enable` for FreeIPA integration to work."; 122 } 123 { 124 assertion = !config.users.ldap.enable; 125 message = "ldap must be disabled through `users.ldap.enable` for FreeIPA integration to work."; 126 } 127 ]; 128 129 environment.systemPackages = with pkgs; [krb5Full freeipa]; 130 131 environment.etc = { 132 "ipa/default.conf".text = '' 133 [global] 134 basedn = ${cfg.basedn} 135 realm = ${cfg.realm} 136 domain = ${cfg.domain} 137 server = ${cfg.server} 138 host = ${config.networking.hostName} 139 xmlrpc_uri = https://${cfg.server}/ipa/xml 140 enable_ra = True 141 ''; 142 143 "ipa/nssdb".source = nssDb; 144 145 "krb5.conf".text = '' 146 [libdefaults] 147 default_realm = ${cfg.realm} 148 dns_lookup_realm = false 149 dns_lookup_kdc = true 150 rdns = false 151 ticket_lifetime = 24h 152 forwardable = true 153 udp_preference_limit = 0 154 155 [realms] 156 ${cfg.realm} = { 157 kdc = ${cfg.server}:88 158 master_kdc = ${cfg.server}:88 159 admin_server = ${cfg.server}:749 160 default_domain = ${cfg.domain} 161 pkinit_anchors = FILE:/etc/ipa/ca.crt 162 } 163 164 [domain_realm] 165 .${cfg.domain} = ${cfg.realm} 166 ${cfg.domain} = ${cfg.realm} 167 ${cfg.server} = ${cfg.realm} 168 169 [dbmodules] 170 ${cfg.realm} = { 171 db_library = ${pkgs.freeipa}/lib/krb5/plugins/kdb/ipadb.so 172 } 173 ''; 174 175 "openldap/ldap.conf".source = ldapConf; 176 }; 177 178 environment.etc."chromium/policies/managed/freeipa.json" = mkIf cfg.chromiumSupport { 179 text = '' 180 { "AuthServerWhitelist": "*.${cfg.domain}" } 181 ''; 182 }; 183 184 system.activationScripts.ipa = stringAfter ["etc"] '' 185 # libcurl requires a hard copy of the certificate 186 if ! ${pkgs.diffutils}/bin/diff ${cfg.certificate} /etc/ipa/ca.crt > /dev/null 2>&1; then 187 rm -f /etc/ipa/ca.crt 188 cp ${cfg.certificate} /etc/ipa/ca.crt 189 fi 190 191 if [ ! -f /etc/krb5.keytab ]; then 192 cat <<EOF 193 194 In order to complete FreeIPA integration, please join the domain by completing the following steps: 195 1. Authenticate as an IPA user authorized to join new hosts, e.g. kinit admin@${cfg.realm} 196 2. Join the domain and obtain the keytab file: ipa-join 197 3. Install the keytab file: sudo install -m 600 krb5.keytab /etc/ 198 4. Restart sssd systemd service: sudo systemctl restart sssd 199 200 EOF 201 fi 202 ''; 203 204 services.sssd.config = '' 205 [domain/${cfg.domain}] 206 id_provider = ipa 207 auth_provider = ipa 208 access_provider = ipa 209 chpass_provider = ipa 210 211 ipa_domain = ${cfg.domain} 212 ipa_server = _srv_, ${cfg.server} 213 ipa_hostname = ${config.networking.hostName}.${cfg.domain} 214 215 cache_credentials = ${pyBool cfg.cacheCredentials} 216 krb5_store_password_if_offline = ${pyBool cfg.offlinePasswords} 217 ${optionalString ((toLower cfg.domain) != (toLower cfg.realm)) 218 "krb5_realm = ${cfg.realm}"} 219 220 dyndns_update = ${pyBool cfg.dyndns.enable} 221 dyndns_iface = ${cfg.dyndns.interface} 222 223 ldap_tls_cacert = /etc/ipa/ca.crt 224 ldap_user_extra_attrs = mail:mail, sn:sn, givenname:givenname, telephoneNumber:telephoneNumber, lock:nsaccountlock 225 226 [sssd] 227 debug_level = 65510 228 services = nss, sudo, pam, ssh, ifp 229 domains = ${cfg.domain} 230 231 [nss] 232 homedir_substring = /home 233 234 [pam] 235 pam_pwd_expiration_warning = 3 236 pam_verbosity = 3 237 238 [sudo] 239 debug_level = 65510 240 241 [autofs] 242 243 [ssh] 244 245 [pac] 246 247 [ifp] 248 user_attributes = +mail, +telephoneNumber, +givenname, +sn, +lock 249 allowed_uids = ${concatStringsSep ", " cfg.ifpAllowedUids} 250 ''; 251 252 services.ntp.servers = singleton cfg.server; 253 services.sssd.enable = true; 254 services.ntp.enable = true; 255 256 security.pki.certificateFiles = singleton cfg.certificate; 257 }; 258}