The remainder of PCI-DSS Section 8 can be split into two parts. Sections 8.5.1 to 8.5.8 describe procedural requirements which you should have in place around user management policy.
However, section 8.5.9 onwards describe a series of requirements for password strength and user account settings.
The final two requirements can be handled in their own right.
8.5.15 If a session has been idle for more than 15 minutes, require the user to re-authenticate to re-activate the terminal or session.
This is simple to implement with a universal shell timeout. At the the end of our
/etc/profile we can insert the following:
TMOUT=900 readonly TMOUT export TMOUT
This tells the shell to log out any users who are inactive for longer than 900 secs (15 minutes). In an effort to stop users getting into the habit of bypassing this timeout we can set the environment variable as
readonly so it can’t be easily changed (except by a superuser).
8.5.16 Authenticate all access to any database containing cardholder data. This includes access by applications, administrators, and all other users.
Restrict user direct access or queries to databases to database administrators.
Fairly simple to understand this requirement. Not always as simple to implement it depending on how your application is designed. But the intent is clear.
So we’ll move on to the main bulk of of the post which is user account and password policy.
Lets review the remaining requirements:
8.5.9 Change user passwords at least every 90 days.
8.5.10 Require a minimum password length of at least seven characters.
8.5.11 Use passwords containing both numeric and alphabetic characters.
8.5.12 Do not allow an individual to submit a new password that is the same as any of the last four passwords he or she has used.
8.5.13 Limit repeated access attempts by locking out the user ID after not more than six attempts.
8.5.14 Set the lockout duration to a minimum of 30 minutes or until administrator enables the user ID.
pam_cracklib & pam_tally
The first place many Linux system administrators look to fulfil these requirements is
pam_cracklib for password complexity rules (reqs 8.5.10, 8.5.11, 8.5.12), with
pam_tally to track failed login attempts (reqs 8.5.13, 8.5.14). And this is indeed a solution.
However if, like us, you have a fairly large estate of servers to roll this out on then the administrative overhead of maintaining a password policy on each server separately very quickly becomes unreasonable for users who will end up with many potentially unsynchronised passwords for keep track of.
Centralised User Authentication
The only real option for us was to centralise user authentication.
There are different degrees of centralisation depending on what you are trying to achieve. Many organisations go the whole hog and use LDAP as a centralised directory service (taking the job of managing individual systems users away from each server), often in addition to a separate mechanism to provide an authentication which is flexible enough to fulfil PCI-DSS requirements. Kerberos is one such authentication mechanism.
This LDAP/Kerberos would be a good approach if you have a large and dynamic user base to manage. LDAP allows a system admin to set up and delete users centrally, and Kerberos manages a central authentication method for each user.
But PCI-DSS is mostly concerned with authentication so will concentrate here on what Kerberos can provide.
I won’t go into too much detail on how Kerberos works and should be configured since there are many such guides around. But I will show how to use a functioning Kerberos realm to enforce PCI password policy.
So what we are trying to achieve with Kerberos is:
- Centralised Password Policy
- and as a bonus we can have, Single Sign-On
The servers required are the following:
- Master KDC & Admin Server
This server is responsible for authenticating requests from Kerberos clients, and issuing Ticket Granting Tickets if the request is successful. The client requests will be generated by
libpam-krb5which will map a Linux system user to a kerberos principle of the same name. If the Kerberos user is successfully authenticated, libpam-krb5 will allow the log in to proceed. This server is also responsible for servicing Kerberos administration requests (password changes, user additions etc.)
- Slave KDC
One of the weaknesses of kerberos is that it introduces a single point of failure for sign-ins given the KDC has to be available to allow anyone to log in. To mitigate this we can have a read-only slave KDC which will service log-in requests if the Master server becomes unavailable. (This is done using kprop on the master and kpropd on the slave.)
So given a functioning KDC propagating to a read-only slave we can now use it to enforce the PCI password policy.
This can be done using the
kadmin.local command under root on the KDC to add a default policy:
addpol -minlength 7 -minclasses 3 -maxlife "90 days" -history 4 -lockoutduration "30 mins" -failurecountinterval 0 -maxfailure 6 default
Each user needs to be added as a principle on the KDC AND also exist as a system user on the client itself. But since the system user cannot be used to log in by itself (see
pam configuration), it can be set to non-expiring:
chage --inactive=-1 --maxdays=-1 --expiredate=-1 username
libpam-krb5 must be installed (along with
krb5-config & krb5-user) to act as the sole PAM module servicing authentication requests.
Unix authentication MUST be disabled.
To check which PAM modules are enabled you can run:
/etc/pam.d/common-auth config will look something like the following:
# here are the per-package modules (the "Primary" block) auth [success=1 default=ignore] pam_krb5.so minimum_uid=1000 # here's the fallback if no module succeeds auth requisite pam_deny.so # prime the stack with a positive return value if there isn't one already; # this avoids us returning an error just because nothing sets a success code # since the modules above will each just jump around auth required pam_permit.so # and here are more per-package modules (the "Additional" block) # end of pam-auth-update config
So now a ssh login will attempt to authenticate against the kerberos master you have configured for this client (the
/etc/krb5.conf file should specify both the KDC master AND slave as the
krb_servers for the realm to allow the slave to service logins).
The use of kerberos means you will need to open some ports within the realm:
- 749 – kadmin to the master KDC
- 88,750 – KDC to master and slave KDCs
- 464 – kpasswd to the master KDC
An important thing to know about Kerberos is that it’s very sensitive to differences in time between servers (whenever a server unexpectedly refuses to authenticate you, check the time first!).
For this reason you really need time synchronisation within the realm… which you should have for PCI anyway. See our NTP Time Synchronisation post for details.
DNS Reverse Lookups
Another aspect which Kerberos is very fussy about is that reverse DNS lookups for local IP addresses must match the specified fqdn. This is not always the case in some private networks.
This ends can be achieved using
/etc/hosts or, more practically for larger networks, a local DNS server.
Single Sign On
Once you have all your clients using kerberos for authentication you can take advantage of credential delegation so you don’t have to re-authenticate while jumping from client to client within the same kerberos realm.
This is still PCI compliant as long as you ensure that all remote access to the network in question must under-go two-factor authentication via an SSH gateway server.
Firstly make sure the KDC is configured to use forwardable tickets in
[libdefaults] ... forwardable = true ...
Then, on the clients, configure your ssh client to delegate kerberos credentials in
GSSAPIAuthentication yes GSSAPIDelegateCredentials yes
This means when you ssh to another machine within the realm, your kerberos credentials will be forwarded, and if valid, you will be granted access.
One bug which can be annoying is when a Kerberos password expires. You will be prompted for a new one. However, if the new password fails to meet the policy requirements, PAM does not display a message to this effect. So to the user it looks like the new password has been accepted, where-as in the log you may find something like the following:
Jun 27 09:32:02 krbmaster kadmind: chpw request from 192.168.1.11 for user@QCODE.CO.UK: Password does not contain enough character classes
This shows in actual fact the password is unchanged. The user, being unaware of this, obviously attempts to log in using their new password and ends up being locked out.
This is a bug which was introduced in kadmind v1.7 and is fixed in 1.9.1 – so hopefully we should see an end to this in Wheezy which has 1.10.