at master 6.3 kB view raw
1import ../../make-test-python.nix ( 2 { pkgs, ... }: 3 let 4 DITRoot = "dc=example,dc=com"; 5 realm = "EXAMPLE.COM"; 6 7 krb5Package = pkgs.krb5.override { withLdap = true; }; 8 9 # Password used by Kerberos services to bind to their identities 10 krbSrvPwd = "kerberos_service_password"; 11 # Stash file read by Kerberos daemons containing the service password 12 # DO NOT DO THIS IN PRODUCTION! The stash file is a fundamental secret! 13 krbPwdStash = pkgs.runCommand "krb-pwd-stash" { } '' 14 for srv in cn=kadmin,${DITRoot} cn=kdc,${DITRoot} 15 do 16 echo -e "${krbSrvPwd}\n${krbSrvPwd}" | \ 17 ${krb5Package}/bin/kdb5_ldap_util -r ${realm} stashsrvpw -f $out $srv 2>&1 > /dev/null 18 done 19 ''; 20 21 # The LDAP schema for Kerberos 5 objects is part of the source distribution of Kerberos 5 22 krbLdapSchema = pkgs.runCommand "krb-ldap-schema" { } '' 23 tar -Oxf ${krb5Package.src} \ 24 ${krb5Package.sourceRoot}/plugins/kdb/ldap/libkdb_ldap/kerberos.openldap.ldif > $out 25 ''; 26 27 # Initial LDAP tree containing only the Kerberos services 28 ldapDIT = '' 29 dn: ${DITRoot} 30 objectClass: organization 31 objectClass: dcObject 32 dc: example 33 o: Example Company 34 35 dn: cn=kdc,${DITRoot} 36 objectClass: krbKdcService 37 objectClass: simpleSecurityObject 38 cn: kdc 39 userPassword: ${krbSrvPwd} 40 41 dn: cn=kadmin,${DITRoot} 42 objectClass: krbAdmService 43 objectClass: simpleSecurityObject 44 cn: kadmin 45 userPassword: ${krbSrvPwd} 46 ''; 47 48 rootDnPwd = "ldap_root_password"; 49 in 50 { 51 name = "kerberos_server-mit-ldap"; 52 53 nodes.machine = 54 { pkgs, ... }: 55 { 56 57 services.openldap = { 58 enable = true; 59 urlList = [ 60 "ldapi:///" 61 "ldap://" 62 ]; 63 declarativeContents."${DITRoot}" = ldapDIT; 64 settings = { 65 children = { 66 "cn=schema".includes = [ 67 "${pkgs.openldap}/etc/schema/core.ldif" 68 "${pkgs.openldap}/etc/schema/cosine.ldif" 69 "${pkgs.openldap}/etc/schema/inetorgperson.ldif" 70 "${pkgs.openldap}/etc/schema/nis.ldif" 71 "${krbLdapSchema}" 72 ]; 73 "olcDatabase={0}config" = { 74 attrs = { 75 objectClass = [ "olcDatabaseConfig" ]; 76 olcDatabase = "{0}config"; 77 }; 78 }; 79 "olcDatabase={1}mdb" = { 80 attrs = { 81 objectClass = [ 82 "olcDatabaseConfig" 83 "olcMdbConfig" 84 ]; 85 olcDatabase = "{1}mdb"; 86 olcDbDirectory = "/var/lib/openldap/db"; 87 olcSuffix = DITRoot; 88 olcRootDN = "cn=root,${DITRoot}"; 89 olcRootPW = rootDnPwd; 90 # A tiny but realistic ACL 91 olcAccess = [ 92 '' 93 to attrs=userPassword 94 by anonymous auth 95 by * none'' 96 '' 97 to dn.subtree="cn=${realm},cn=realms,${DITRoot}" 98 by dn.exact="cn=kdc,${DITRoot}" write 99 by dn.exact="cn=kadmin,${DITRoot}" write 100 by * none'' 101 '' 102 to * 103 by * read'' 104 ]; 105 }; 106 }; 107 }; 108 }; 109 }; 110 111 services.kerberos_server = { 112 enable = true; 113 settings = { 114 libdefaults.default_realm = realm; 115 realms = { 116 "${realm}" = { 117 acl = [ 118 { 119 principal = "admin"; 120 access = "all"; 121 } 122 ]; 123 }; 124 }; 125 dbmodules = { 126 "${realm}" = { 127 db_library = "kldap"; 128 ldap_kerberos_container_dn = "cn=realms,${DITRoot}"; 129 ldap_kdc_dn = "cn=kdc,${DITRoot}"; 130 ldap_kadmind_dn = "cn=kadmin,${DITRoot}"; 131 ldap_service_password_file = toString krbPwdStash; 132 ldap_servers = "ldapi:///"; 133 }; 134 }; 135 }; 136 }; 137 138 security.krb5 = { 139 enable = true; 140 package = krb5Package; 141 settings = { 142 libdefaults = { 143 default_realm = realm; 144 }; 145 realms = { 146 "${realm}" = { 147 admin_server = "machine"; 148 kdc = "machine"; 149 }; 150 }; 151 }; 152 }; 153 154 users.extraUsers.alice = { 155 isNormalUser = true; 156 }; 157 }; 158 159 testScript = '' 160 machine.wait_for_unit("openldap.service") 161 162 with subtest("realm container initialization"): 163 machine.succeed( 164 # Passing a master key directly avoids the need for a separate master key stash file 165 "kdb5_ldap_util -D cn=root,${DITRoot} create -w ${rootDnPwd} -s -P master_key", 166 ) 167 168 # These units are bound to fail, as they are started before the directory service is ready 169 machine.execute("systemctl restart kadmind.service kdc.service") 170 171 with subtest("service bind"): 172 for unit in ["kadmind", "kdc"]: 173 machine.wait_for_unit(f"{unit}.service") 174 175 with subtest("administration principal initialization"): 176 machine.succeed("kadmin.local add_principal -pw admin_pw admin") 177 178 with subtest("user principal creation and kinit"): 179 machine.succeed( 180 "kadmin -p admin -w admin_pw addprinc -pw alice_pw alice", 181 "echo alice_pw | sudo -u alice kinit", 182 ) 183 # Make extra sure that the user principal actually exists in the directory 184 machine.succeed( 185 "ldapsearch -x -D cn=root,${DITRoot} -w ${rootDnPwd} \ 186 -b ${DITRoot} 'krbPrincipalName=alice@${realm}' | grep 'numEntries: 1'" 187 ) 188 ''; 189 190 meta.maintainers = [ pkgs.lib.maintainers.nessdoor ]; 191 } 192)