This blog was written to provide a step-by-step tutorial for IT administrators on deploying a web-based password reset portal following best practices. The guide was written for a broad audience but will make reasonable assumptions on your knowledge of certain technologies. Feel free to ask questions in the comment section!
Three years ago, I was working at a school district with ~3000 users that were suddenly outnumbering our Windows computers with their "BYOD" cellphones, tablets, laptops, etc. We were also purchasing less and less traditional computing devices. We were moving to Google Chromebooks and the ability to press "Ctrl Alt Del" to initiate a password reset wasn't realistic anymore. We had moved off Outlook in favor of Gmail and did not have access to leveraging OWA (Outlook Web Access) as a method of resetting passwords.
When I was doing some research about password self service, I stumbled upon an open source "PWM" project hosted on Google Code. It seemed a little over my head (I had never setup a Linux server before) but I was obsessed with Google and figured my boss would be more likely to entertain my ideas if I had a working proof of concept. It took me a few tries but eventually I wrote one of my first well-received blog posts. It was a detailed guide to installing PWM v1.7.0 on Ubuntu 14.04 LTS.
A lot has changed since I wrote that blog post. The PWM source code is now a different major revision (v1.8) hosted on GitHub and Ubuntu 14.04 LTS is not the current LTS build. Even worse yet, my original guide had some wacky suggestions like using port 8080/8443 and self-signed certificates (to be fair... LetsEncrypt certificates weren't available until 2015.) It occurred to me that my aging PWM tutorial was no longer relevant.
Hosting: You will need to make a decision about whether or not you want to host the web server on-premises. If you decide to go with a "cloud" service (e.g. Google Cloud, Amazon AWS, DigitalOcean, etc.) or in a colocation, you will still need to communicate with your LDAP server. If your LDAP server is only accessible on-premises, then you will likely need to tunnel the traffic through VPN. When I was working at the school district, it made sense to host on-premises since we had the existing server infrastructure, existing static IP addresses, and an LDAP server that was not externally accessible.
Risk vs. Reward: Hosting an open source password reset portal saves you a bunch of money because it's free, right? Not necessarily. It may be worth the money to offload the burden of security to a paid product (e.g. ServiceNow, ManageEngine, Azure AD Password Reset.) By designing your own custom solution, you also have a responsibility to routinely patch and secure it. The estimated cost of that responsibility should be taken into consideration.
[ Prerequisites ]
- Fresh installation of Ubuntu 16.04 LTS
- Ability to communicate with existing LDAP server
- Established an external IP address / DNS record for your web server
I will be demonstrating these prerequisites using an example server. In my case, I created a new Ubuntu LTS 16.04.1 x64 DigitalOcean "droplet". Upon creation, it was automatically assigned a static public IPv4 address (138.68.2.0). To create a new DNS record, I went to my domain registrar website (Domain.com) and added a new A record called "demo" that points to the IP address of 138.68.2.0. It will take some time for DNS to propagate, but eventually I should be able to confirm that demo.milesgratz.com resolves to 138.68.2.0.
nslookup demo.milesgratz.com
Server: 192.168.1.1
Address: 192.168.1.1#53
Non-authoritative answer:
Name: demo.milesgratz.com
Address: 138.68.2.0
- Tomcat8 (Java web server framework for pwm application)
- LetsEncrypt (Free HTTPS certificate for Apache2)
- Apache2 (SSL Proxy between client and Tomcat8)
- ufw (Firewall to secure web server)
- MySQL Server (Database for pwm application)
Let's update our package lists and download some necessary utilities first:
sudo apt-get update
sudo apt-get install git unzip
A better tutorial of this section is available here: Installing Apache Tomcat 8 on Ubuntu 16.04
Let's start off by installing the Java Development Kit.
sudo apt-get install default-jdk
sudo groupadd tomcat
sudo useradd -s /bin/false -g tomcat -d /opt/tomcat tomcat
cd /tmp
wget http://www-us.apache.org/dist/tomcat/tomcat-8/v8.5.4/bin/apache-tomcat-8.5.4.tar.gz
sudo mkdir /opt/tomcat
sudo tar xzvf apache-tomcat*.tar.gz -C /opt/tomcat --strip-components=1
cd /opt/tomcat
sudo chown -R tomcat *
sudo chgrp -R tomcat conf/
sudo chmod g+rwx conf/
sudo chmod g+r conf/*
sudo update-java-alternatives -l
java-1.8.0-openjdk-amd64 1081 /usr/lib/jvm/java-1.8.0-openjdk-amd64
sudo vim /etc/systemd/system/tomcat.service
[Unit]
Description=Apache Tomcat Web Application Container
After=network.target
[Service]
Type=forking
Environment=JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64/jre
Environment=CATALINA_PID=/opt/tomcat/temp/tomcat.pid
Environment=CATALINA_HOME=/opt/tomcat
Environment=CATALINA_BASE=/opt/tomcat
Environment='CATALINA_OPTS=-Xms512M -Xmx1024M -server -XX:+UseParallelGC'
Environment='JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom'
ExecStart=/opt/tomcat/bin/startup.sh
ExecStop=/opt/tomcat/bin/shutdown.sh
User=tomcat
Group=tomcat
RestartSec=10
Restart=always
[Install]
WantedBy=multi-user.target
http://your_server_IP_address:8080
sudo systemctl daemon-reload
sudo systemctl start tomcat
sudo systemctl enable tomcat
LetsEncrypt is a new Certificate Authority that provides free HTTPS certificates by using their client that runs locally on the web server and uses a combination of safety mechanisms (e.g. DNS resolution) to confirm that you own the domain that you are requesting a certificate for. As I mentioned earlier in my guide, this option did not exist prior to 2015. If you already have a wildcard certificate for your domain, you could use that instead.
sudo git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt
sudo vim /opt/letsencrypt/config.ini
authenticator = standalone
renew-by-default
agree-tos
email = milesgratz@gmail.com
domain = demo.milesgratz.com
sudo /opt/letsencrypt/letsencrypt-auto --config /opt/letsencrypt/config.ini certonly
crontab -e
0 0 1 * * systemctl stop apache2 && /opt/letsencrypt/letsencrypt-auto --config /opt/letsencrypt/config.ini certonly && systemctl start apache2
You might be wondering why you need both Apache2 and Tomcat8. By default, Tomcat listens on alternate ports (8080 and 8443) and was not designed to run natively on privileged ports like 80 and 443. Instead, we will be using Apache2 to proxy a secure connection between the client and Tomcat. Additionally, we will configure Apache2 to redirect all unencrypted HTTP requests to HTTPS. I have included a mediocre diagram of that concept in action.
sudo apt-get install apache2
sudo vim /etc/apache2/sites-available/tomcat.conf
NOTE: The trailing forward slash on the end of the redirect URL is required.
<VirtualHost *:80>
# redirect to https
Redirect permanent / https://demo.milesgratz.com/
</VirtualHost>
<VirtualHost *:443>
# LogLevel info ssl:warn
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
# Enable SSL for this virtual host.
SSLEngine on
SSLCertificateKeyFile /etc/letsencrypt/live/demo.milesgratz.com/privkey.pem
SSLCertificateFile /etc/letsencrypt/live/demo.milesgratz.com/cert.pem
SSLCertificateChainFile /etc/letsencrypt/live/demo.milesgratz.com/chain.pem
# Configure proxy with tomcat
ProxyPreserveHost On
ProxyRequests Off
ProxyPass / http://localhost:8080/
ProxyPassReverse / http://localhost:8080/
</VirtualHost>
sudo a2enmod ssl proxy proxy_http
sudo a2dissite 000-default
sudo a2ensite tomcat
sudo systemctl restart apache2
Now that we have a working web server, we need to properly secure it. Enabling the firewall can also accidentally disable your SSH access. The rules below allow SSH, HTTP, and HTTPS from all addresses. You will likely want to be more restrictive.
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw allow from 10.0.0.0/8 to any port 22
sudo ufw allow from 172.16.0.0/12 to any port 22
sudo ufw allow from 192.168.0.0/16 to any port 22
sudo ufw enable
Depending on what you are planning on doing with PWM, you may not need to install the MySQL database. Unless you are confident you will not need it, it may make more sense to install it. For example, a database is required to store secret questions/answers for "Forgot My Password" feature.
sudo apt-get install mysql-server
sudo mysql_secure_installation
Would you like to setup VALIDATE PASSWORD plugin? (Press y|Y for Yes, any other key for No) :
Y
There are three levels of password validation policy:
Please enter 0 = LOW, 1 = MEDIUM and 2 = STRONG: 2
Estimated strength of the password: 100
Change the password for root? (Press y|Y for Yes, any other key for No) :
N
Remove anonymous users? (Press y|Y for Yes, any other key for No) :
Y
Disallow root login remotely? (Press y|Y for Yes, any other key for No) :
Y
Reload privilege tables now? (Press y|Y for Yes, any other key for No) :
Y
sudo mysql --user="root" --password
mysql>
CREATE USER 'pwm'@'localhost' IDENTIFIED BY 'YOUR_DB_PASSWORD';
CREATE DATABASE pwm;
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP ON pwm.* TO 'pwm'@'localhost';
FLUSH PRIVILEGES;
exit
You've made it to the easy part! Let's install the actual PWM application. You can download the latest build from their website (http://www.pwm-project.org/artifacts/pwm). This blog post was written using the "28 Aug 2016" build.
cd /tmp
wget http://www.pwm-project.org/artifacts/pwm/pwm-1.8.0-SNAPSHOT-2016-08-28T22%3A16%3A03Z-pwm-bundle.zip
unzip pwm*bundle.zip
sudo rm -rf /opt/tomcat/webapps/ROOT
sudo mv pwm.war /opt/tomcat/webapps/ROOT.war
sudo vim /opt/tomcat/webapps/ROOT/WEB-INF/web.xml
<param-name>applicationPath</param-name>
<param-value>/opt/tomcat/webapps/ROOT/WEB-INF</param-value>
The Configuration Guide is going to have you configure your LDAP settings and a handful of other "initial setup" settings. If you are having issues getting through this part, feel free to ask questions in the comment section. Other useful resources include the PWM Administration Guide or the PWM General forum.
Once you have completed the Configuration Guide, we also need to install the MySQL "Connector/J" JDBC driver that allows the PWM application to communicate with the MySQL database. I recommend downloading the latest version from their website (https://dev.mysql.com/downloads/connector/j) but I am using v5.1.39 in this guide.
cd /tmp
wget https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.39.tar.gz
tar -xzvf mysql-connector-java*.tar.gz
cd mysql-connector-java*
sudo mv mysql-connector-java*.jar /opt/tomcat/webapps/ROOT/WEB-INF/lib
Database Class:
com.mysql.jdbc.Driver
Database Connection String:
jdbc:mysql://localhost:3306/pwm
Database Username:
pwm
Database Password:
(enter PWM database password)
Database Vendor:
Other
Well done, you made it! Thanks for reading my blog. I appreciate all the helpful feedback I've gotten from the community over the years. If you are struggling with any part of the guide, please comment below.