I had the pleasure recently of having to figure out how to get SmartCard(CaC) authentication up and running on our Red Hat workstations. The workstations are mostly STIG’d already, but now they required us to get CaC authentication up and runnning. I was happy really to do this as it removes the rediculous additional password that I have to remember and also do not have to try and keep 20 passwd/shadow/group files in sync.
Hopefully this will help somebody configure this as well. There are quite a few steps but once you get it configured at the end I show you how to save the config which you can very very easily deploy to other workstations. It takes us about 5 minutes now to do a workstation from scratch!
First, what we wanted out of this was as follows:
- CaC authentication
- Kerberos ticket issued through login
- Access to any CIFS shares via kerberos ticket
- SSH access via kerberos ticket to any servers we may have
The process was quite bumpy figuring out all the certificate issues and what not, but the end result was a single shell script that is easily run by anybody that can join a computer to the domain.
Really, in the long run, authconfig is your best friend. It does a pretty good job of getting the basics going, then you just have to modify the /etc/krb5.conf and /etc/samba/smb.conf files.
First define some variables that we can use
yourdomain='your.example.com'
adminuser='domain.admin'
computerou='some/ou/path'
Now you can run these little gems to setup the rest of the variables you need
adinfo=$(dig _kerberos.${yourdomain} | awk '/SOA/ {printf("%s %s\n",$1,$5)}')
adserver=${adinfo#*. }
adserver=${adserver%%.}
domain=${adinfo%*. *}
workgroup=${domain%%.*}
echo "--- Info ---"
echo "Server: $adserver"
echo "Domain: $domain"
echo "Workgroup: $workgroup"
Update and Install some dependencies
Most of these packages are contained inside of package groups, but apparently
our local repo didn’t sync the group lists. So you get all the package names instead
yum update -y
yum install -y ccid coolkey esc gdm-plugin-smartcard pam_pkcs11 \
opencryptoki krb5-pkinit-openssl pam_krb5 python-ldap ipa-client \
oddjob-mkhomedir sssd ypbind certmonger hesinfo krb5-appl-clients \
krb5-pkinit-openssl krb5-workstation ldapjdk libsss_sudo nscd \
nss-pam-ldapd openldap-clients pam_krb5 pam_ldap samba-winbind \
cifs-utils gdm gdm-plugin-smartcard plymouth-gdm-hooks gdm-libs
Have authconfig setup your config files
authconfig --enablesmartcard --enablekrb5 --enablewinbind \
--enablewinbindauth --krb5kdc=${adserver} \
--krb5adminserver=${adserver} --krb5realm ${domain} \
--passalgo=sha512 --winbindseparator='+' \
--winbindtemplateshell='/bin/bash' --smbsecurity=ads \
--smbservers=${adserver} --smbworkgroup ${workgroup} \
--smbrealm=${domain} --smartcardmodule='coolkey' \
--smartcardaction=0 --enablemkhomedir --update
Tweak /etc/samba/smb.conf
If you have multiple password servers(aka AD servers) put them all after password server speparated by spaces.
I’ve removed the information for my workplace so just make sure your’s is correct.
DOMAINNETBIOSNAME is the short name for your domain and should probably be in uppercase
[global]
#--authconfig--start-line--
# Generated by authconfig on ......
# DO NOT EDIT THIS SECTION (delimited by --start-line--/--end-line--)
# Any modification may be deleted or altered by authconfig in future
workgroup = DOMAINNETBIOSNAME
password server = adserver1.example.com adserver2.example.com
realm = EXAMPLE.COM
security = ads
winbind separator = +
template shell = /bin/bash
winbind use default domain = yes
winbind offline logon = false
idmap config DOMAINNETBIOSNAME: backend = rid
idmap config DOMAINNETBIOSNAME: range = 10000000-19999999
winbind enum users = no
winbind enum groups = no
kerberos method = system keytab
dedicated keytab file = /etc/krb5.keytab
idmap uid = 10000-19999
idmap gid = 10000-19999
template homedir = /home/%D/%U
template shell = /bin/bash
winbind use default domain = yes
Tweak /etc/krb5.conf
[logging]
default = FILE:/var/log/krb5libs.log
kdc = FILE:/var/log/krb5kdc.log
admin_server = FILE:/var/log/kadmind.log
[libdefaults]
default_realm = EXAMPLE.COM
dns_lookup_realm = false
dns_lookup_kdc = false
ticket_lifetime = 24h
renew_lifetime = 7d
forwardable = true
allow_weak_crypto = 1
default_keytab_name = FILE:/etc/krb5.keytab
[realms]
EXAMPLE.COM = {
kdc = adserver1.example.com
kdc = adserver2.example.com
admin_server = adserver1.example.com
# ---------- PKINIT CAC Section ----------
pkinit_kdc_hostname = ADSERVER1.example.com
#preferred_preauth_types = 30,31
pkinit_identities = PKCS11:/usr/lib64/pkcs11/libcoolkeypk11.so
# This specifies the certificate on your CaC
#that has your Microsoft Principal Name in form of EPID@mil
pkinit_cert_match = <SAN>^[0-9]{10}@mil$
pkinit_anchors = FILE:/etc/pki/CA/certs/adcerts.pem
pkinit_eku_checking = kpServerAuth
# ---------- END PKINIT ------------------
}
[domain_realm]
.example.com = EXAMPLE.COM
example.com = EXAMPLE.COM
[appdefaults]
autologin = true
forward = true
encrypt = true
forwardable = true
krb5_run_aklog = false
default_lifetime = 0d 10h 0m 0s
Setup ntp to sync from the AD server
Not really sure if this is the correct ntp config, but it seems to be working
sed -i 's/^server/#server/' /etc/ntp.conf
echo "server $adserver" >> /etc/ntp.conf
service ntpd restart
Setup AD certificates
Here is a python script I created that should pull all necessary certificates from your AD server and import them where needed.
I really don’t know enough about how the internals of AD work with regards to certificates, but I do know you need the certificates from AD so that you can authenticate your SmartCard as well as log in and such.
I realized when I downloaded this script there was a specific part to our setup, but you should be able to change it by editing the script and changing the
‘cn=Configuration,dc=example,dc=com’
to your domain
Then you should be able to just run
python setup_certs.py ${adminuser} ${adserver}
Setup your /etc/pam_pkcs11/cn_map file
There are quite a few different mappers that you can look at using, but we ended up just using the cn_map.
You just put in that file all SmartCard users and their AD user name one per line
For instance:
LASTNAME.FIRSTNAME.MIDDLE.EPID -> firstname.lastname
You can use pklogin_finder debug to get the LASTNAME.FIRSTNAME.MIDDLE.EPID stuff. I might consider making a plugin at some point to do this stuff automagically, but for now, cn_map it is.
Ensure permissions are correct
chmod 744 /etc/pki/CA/certs
chmod 444 /etc/pki/CA/certs/*
chmod 644 /etc/krb5*
Edit /etc/hosts
Change the top two lines that begin with 127.0.0.1 and ::1 such that your fully qualified domain name is the first entry on that line. So something like this.
If you don’t do this, your computer will join to AD with the dnsHostname of localhost.localdomain or something funky and kerberos will be cranky for some things
127.0.0.1 myworkstation.example.com myworkstation localhost....
::1 myworkstation.example.com myworkstation localhost....
Restart services
chkconfig winbind on
service winbind restart
Join the computer to the domain
net ads join -S $adserver -U $adminuser createcomputer="${computerou}"
If your AD server does not handle dynamic DNS updates then this command will likely say that it joined you to the domain, but the DNS updated failed which is fine.
Recently, the command has hung after joining to the domain and I have had to CTRL+C it, but it still joins the account to the domain so all is good in the world.
Initialize machine kerberos ticket
net ads kerberos kinit -P
Setup kerberos keytab stuff on bootup
This is a bit ugly and really it is only needed if you are doing CIFS mounts that require kerberos and using the sec=kerberos,multiuser options.
I suspect there is a more elegant solution(like doing something to tell CIFS to ensure kerberos keytab before mounting) but time is money and this works too
cat <<EOF >> /etc/rc.local
net ads kerberos kinit -P
mount -a
EOF
Save your authconfig!!!!
If you are sure everything is working, you can save all the config
authconfig --savebackup=/root/workingauthconfig
tar czvf certs_config.tgz \
/etc/pki/CA/certs \
/etc/pki/nssdb \
/etc/ntp.conf \
/etc/pam_pkcs11/cn_map
And you can restore the config(maybe to deploy to other workstations…)
tar xzvf certs_config.tgz
authconfig --restorebackup=/root/workingauthconfig
When all the planets are not aligned and things don’t work
This probably will not work the first time you try. Lots of pieces in motion. Here are some things that will hopefully help you identify what piece is not working
Account lockout and SmartCard PIN lock
If you put your pin in with this command and it gives an error, it likely means your PIN is locked
pklogin_finder debug
This command will check to see if an account is locked in AD
[[ "$(net ads search '(sAMAccountName=tyghe.vallard)' lockoutTime -P)" =~ "lockoutTime: 0" ]] \
&& echo "Account is not locked" || echo "Account is locked"
Test your AD connection and such
net ads testjoin
Should return: Join is OK
Gives you information about the machine’s account in AD you can use to debug with
net ads status -P
Make sure you get a passwd line back from wbinfo
wbinfo -i ad.user.name
This should return a passwd entry. The only time it seemed to fail was when we didn’t have /etc/samba/smb.conf setup correctly for rid uid/gid mapping
If you fix the /etc/samba/smb.conf file here is the command to flush your stuff
net cache flush; service winbind restart;
Test your kinit/kerberos/certificates
Run this command and put in the pin for your CaC
export KRB5_TRACE=/dev/stdout
kinit
We were getting this error
Preauth module pkinit (17) (flags=1) returned: -1765328308/KDC name mismatc
This error indicates that the pkinit_kdc_hostname you put in the /etc/krb5.conf file does not match the certificate you imported from AD. Most likely, the host name needs to be uppercase.
Verify your connection to AD’s ldap works(really if the setup_certs.py script works this shouldn’t be a problem)
openssl s_client -connect adserver1.example.com:636 \
-CAfile /etc/pki/CA/certs/adcerts.pem 2>&1 | grep 'Verify return code'