Author — Fedor Potapov, Windows DevOps Hostkey team
Any modern telecommunications company is faced with the need to organize a comfortable workspace not only for users of its services, but also for its employees. At Hostkey, some of our staff work in offices in Russia and the Netherlands, and some work remotely. Naturally, everyone works on those operating systems that are convenient and familiar to them, so there is no uniformity in this matter either.
In companies with a developed Microsoft infrastructure, these issues are resolved as part of the regular use of workstations, mail service, etc. If everyone works in a Linux OS environment, FreeIPA is a good solution. We also started with IPA and talked about our experience of integrating FreeIPA with Active Directory in one of the previous articles. But Single sign-on (SSO) as a product is used by all employees, only a fraction of which use SSH. As a result, web services and vendor hardware have led to the appearance of a small Microsoft infrastructure as an appendage to IPA. SSO at the first stages was most often implemented through LDAP, then we migrated to SAML and oAuth. For a long time, before the appearance of these protocols in the authorization system, it was necessary to solve the issue of regular password rotation.
Moving along the path of least resistance, we did not immediately launch complex systems, rather trying to solve the problem as simply as possible from the point of view of operations, and conveniently from the point of view of the users. We would like to share our experience, as small as it may be. We hope it will be of some benefit to administrators of small or hybrid infrastructures.
We needed to implement a procedure for regularly changing passwords in a way that would work everywhere and be convenient for our users. Our company uses Python, and initially we used a script created in this programming language, but it was very simple and solved only one task - changing the password, but we also needed to solve multi-factor authentication tasks. As a result, we finalized this script.
Our solution was based on the following assembly — Web UI for changing LDAP password.
It contains two of the most important files:
- App.py — is a single-page compact Python-script, which can be customized easily.
- Settings.ini.example — support for simultaneous password changes in several LDAPs - the parameters of the LDAP server are specified.
This tool allows you to change the password on several LDAP servers, but there is an important nuance that does not allow you to use a ready-made solution. In our infrastructure, between the AD and FreeIPA servers (we talked about their interaction in a separate article) the Password Sync service works. It turned out that sometimes Password Sync works faster than a Python script. Because of this, periodically there were problems when changing the password by users.
Note that we changed the password only on the one LDAP server. Ours is the AD server, since we were more satisfied with the basic requirements for password complexity (the administrator must be lazy, so if you can use a ready-made option, it is often best stop at that), for example, in addition to length and complexity the password must contain the following:
- capital letters
- lower case letters
- special characters
- numbers
In order to create a password, it must contain at least three of these points. Also, AD has several additional requirements; for example, the password cannot contain the login, while FreeIPA does not have such a requirement. Thus, with a little tweaking of the FreeIPA policy, we came up with a structure in which if AD accepted the password, then FreeIPA would also accept it.
The password change script can be found here.
In fact, the script works as follows: the configuration contains two LDAP servers, one of which is AD. Python in the course of its work reads the configuration, the LDAP block, the block without the type = ad clause is the first in the configuration, Python executes the verify_mfa clause, first looks for the user_dn of the user on the FreeIPA server, then sends it to the Multifactor proxy LDAP server. Next, in the configuration is a block with the item type = ad, Python goes to the appropriate server and performs a password change (change_password_ad). If no confirmation is received from MULTIFACTOR, Python does not execute the second step.
Practice shows that the user always forgets to change his password on time. This places an unnecessary and avoidable burden on administrators, and the users themselves are uncomfortable, which is not good. The system of automatic reminders that was added to the corporate messenger (in our case, Rocket.Chat) helped to solve this problem.
We used the regular Rocket.Chat mechanism - webHook integration. The URL is generated automatically.
Then a simple script runs on the AD server:
$BodyTxt1 = 'Password expiration for '# message text in Rocket.Chat
$BodyTxt2 = ' ends with '# message text in Rocket.Chat
$BodyTxt3 = ' days. Don't forget to change your password beforehand.' # message text in Rocket.Chat
$Hook = "https://localhost;8080/hooks/hook_id" #using Hook generated in Rocket.Chat
$warnDays = (get-date).adddays(7) # number of days until password expires
$2Day = get-date
$Users = Get-ADUser -SearchBase 'OU=users,OU=ipa,DC=win,DC=hostkey,DC=ru' -filter {Enabled -eq $True -and PasswordNeverExpires -eq $False} -Properties msDS-UserPasswordExpiryTimeComputed, EmailAddress, SamAccountName | select Name, @{Name ="ExpirationDate";Expression= {[datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed")}}, EmailAddress, SamAccountName
foreach ($user in $users) {
if (($user.ExpirationDate -lt $warnDays) -and ($2Day -lt $user.ExpirationDate) ) {
$lastdays = ( $user.ExpirationDate -$2Day).days
$Username = "@"+ $user.SamAccountName
$Text = $BodyTxt1 + $Username + $BodyTxt2 + $lastdays + $BodyTxt3
$Payload = @{
"text"= $Text
}
Invoke-RestMethod -Uri $Hook -Body $Payload -Method Post -ErrorAction Stop
sleep -Milliseconds 300 # delay allows Rocket.Chat to process messages without crashing
}
}
This script is placed in AD and a task is specified in the AD task scheduler to work out expass.
As a result, we were left with a convenient and easy-to-implement system for managing passwords for employee accounts without complex services, with two-factor verification when changing the password and a self-service portal as well.