Skip to content

Commit

Permalink
Combine create/activate CA into one script/cronjob
Browse files Browse the repository at this point in the history
Also:
- Add code to ping2 to ask clients to renew their cert if a new CA cert
  has been installed

Closes #27.
  • Loading branch information
oyvindhagberg committed Mar 13, 2019
1 parent 788c9bb commit c027423
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 74 deletions.
11 changes: 6 additions & 5 deletions rpm/nivlheim.spec
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Requires: perl(Sys::Syslog)
%package server
Summary: Server components of Nivlheim
Group: Applications/System
Requires: perl, openssl, httpd, mod_ssl, systemd
Requires: perl, openssl, httpd, mod_ssl, systemd, cronie
Requires: postgresql, postgresql-server, postgresql-contrib
Requires: unzip, file
Requires: perl(Archive::Tar)
Expand Down Expand Up @@ -187,11 +187,10 @@ install -p -m 0755 server/cgi/renewcert %{buildroot}/var/www/cgi-bin/secure/
install -p -m 0755 server/cgi/post %{buildroot}/var/www/cgi-bin/secure/
install -p -m 0644 server/log4perl.conf %{buildroot}/var/www/nivlheim/
install -p -m 0755 server/setup.sh %{buildroot}%{_localstatedir}/nivlheim/
install -p -m 0755 server/create_new_CA.sh %{buildroot}%{_sbindir}/
install -p -m 0755 server/activate_new_CA.sh %{buildroot}%{_sbindir}/
install -p -m 0755 server/cgi/processarchive %{buildroot}/var/www/cgi-bin/
install -p -m 0644 server/nivlheim.service %{buildroot}%{_unitdir}/%{name}.service
install -p -m 0644 -D client/cronjob %{buildroot}%{_sysconfdir}/cron.d/nivlheim_client
install -p -m 0755 -D server/client_CA_cert.sh %{buildroot}%{_sysconfdir}/cron.daily/client_CA_cert.sh
rm -rf server/website/mockapi server/website/templates server/website/libs
cp -a server/website/* %{buildroot}%{_localstatedir}/www/html/
install -p -m 0644 ../jquery-3.3.1/dist/jquery.min.js %{buildroot}%{_localstatedir}/www/html/libs/jquery-3.3.1.min.js
Expand Down Expand Up @@ -246,10 +245,9 @@ rm -rf %{buildroot}
%config(noreplace) %{_sysconfdir}/httpd/conf.d/nivlheim.conf
%config %{_sysconfdir}/nivlheim/openssl_ca.conf
%config(noreplace) %{_sysconfdir}/nivlheim/server.conf
%{_sysconfdir}/cron.daily/client_CA_cert.sh
%{_unitdir}/%{name}.service
%{_sbindir}/nivlheim_service
%{_sbindir}/create_new_CA.sh
%{_sbindir}/activate_new_CA.sh
%dir /var/log/nivlheim
/var/www/cgi-bin/*
/var/www/html/*
Expand All @@ -267,6 +265,9 @@ rm -rf %{buildroot}
%systemd_postun_with_restart %{name}.service

%changelog
* Mon Mar 11 2019 Øyvind Hagberg <[email protected]> - 0.12.2-20190311
- New cron job that maintains the client CA certificates

* Tue Dec 11 2018 Øyvind Hagberg <[email protected]> - 0.11.0-20181211
- Include 3rd party javascript and css libraries in the rpm file

Expand Down
25 changes: 0 additions & 25 deletions server/activate_new_CA.sh

This file was deleted.

14 changes: 12 additions & 2 deletions server/cgi/ping2
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,23 @@ $timestamp =~ s/\s+GMT$//;
my $time = Time::Piece->strptime($timestamp, "%b %d %H:%M:%S %Y");
my $left = $time - gmtime;
if ($left->days < 30) {
print "Status: 403\nContent-Type: text/plain\n\nYour cert is about to expire, please renew it.\n";
print "Status: 400\nContent-Type: text/plain\n\nYour certificate is about to expire, please renew it.\n";
exit;
}

# Compute the client cert fingerprint
# If the client cert was signed by a different CA than the one that's currently active,
# politely ask it to renew
my $clientcert = $ENV{'SSL_CLIENT_CERT'};
my $x509 = Crypt::OpenSSL::X509->new_from_string($clientcert);
my $value1 = $x509->issuer();
my $ca = Crypt::OpenSSL::X509->new_from_file('/var/www/nivlheim/CA/nivlheimca.crt');
my $value2 = $ca->subject();
if ($value1 ne $value2) {
print "Status: 400\nContent-Type: text/plain\n\nThe server has a new CA certificate, please renew your certificate.\n";
exit;
}

# Compute the client cert fingerprint
my $fingerprint = $x509->fingerprint_sha1();
$fingerprint =~ s/://g;

Expand Down
92 changes: 92 additions & 0 deletions server/client_CA_cert.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#!/bin/bash

# This script is a part of Nivlheim.
# It is used to create CA certificates that are used for signing
# client certificates.
#
# It is intended to be run without parameters, as a cron job.
# It will check the expiry date of the existing CA certificate,
# and replace it with a new one when necessary.
#
# To make it easier for 3rd party software to verify client certificates,
# a new CA certificate will appear in the bundle https://<server>/clientca.pem
# 3 weeks before it is actually put to use.


if [ `whoami` != "root" ]; then
echo "This script must be run as root."
exit 1
fi

# What operations to perform
CREATE=0
ACTIVATE=0
VERBOSE=0

# Parameters may override normal operations
while (( "$#" )); do
if [[ "$1" == "--force-create" ]]; then
CREATE=1
elif [[ "$1" == "--force-activate" ]]; then
ACTIVATE=1
elif [[ "$1" == "--verbose" ]] || [[ "$1" == "-v" ]]; then
VERBOSE=1
else
echo "Unknown argument: $1"
exit 1
fi
shift
done

cd /var/www/nivlheim/CA

# If the CA certificate will expire in less than 30 days, create a new one
if [ ! -f nivlheimca.crt ] || ! openssl x509 -checkend 2592000 -noout -in nivlheimca.crt -enddate >/dev/null; then
CREATE=1
fi

# If the CA certificate will expire in less than 9 days, change to the new one
if [ ! -f nivlheimca.crt ] || ! openssl x509 -checkend 777600 -noout -in nivlheimca.crt >/dev/null; then
ACTIVATE=1
fi

if [[ $CREATE -eq 1 ]]; then
if [ ! -f new_nivlheimca.crt ] || [ ! -f new_nivlheimca.key ]; then
[ $VERBOSE -eq 1 ] && echo "Creating a new CA certificate"

# Generate a new certificate
rm -f old_*
openssl genrsa -out new_nivlheimca.key 4096 >/dev/null 2>&1
openssl req -new -key new_nivlheimca.key -out new_nivlheimca.csr -subj "/C=NO/ST=Oslo/L=Oslo/O=UiO/OU=USIT/CN=Nivlheim$RANDOM"
openssl x509 -req -days 365 -in new_nivlheimca.csr -out new_nivlheimca.crt -signkey new_nivlheimca.key >/dev/null 2>&1

# Fix permissions
chgrp apache new_nivlheimca.*
chmod 640 new_nivlheimca.key

# Show results
[ $VERBOSE -eq 1 ] && openssl x509 -in new_nivlheimca.crt -noout -enddate

# create a bundle with the old and the new CA
cat nivlheimca.crt new_nivlheimca.crt > /var/www/html/clientca.pem
else
echo "Won't create a new CA certificate; One has already been created and is waiting"
fi
fi

if [[ $ACTIVATE -eq 1 ]]; then
if [ -f new_nivlheimca.crt ] && [ -f new_nivlheimca.key ]; then
[ $VERBOSE -eq 1 ] && echo "Activating the new CA certificate"
# Activate/change to the new CA certificate
mv nivlheimca.key old_nivlheimca.key
mv nivlheimca.csr old_nivlheimca.csr
mv nivlheimca.crt old_nivlheimca.crt
mv new_nivlheimca.key nivlheimca.key
mv new_nivlheimca.csr nivlheimca.csr
mv new_nivlheimca.crt nivlheimca.crt
systemctl restart httpd
else
echo "There's no new CA certificate to activate"
exit 1
fi
fi
34 changes: 0 additions & 34 deletions server/create_new_CA.sh

This file was deleted.

6 changes: 3 additions & 3 deletions tests/test_cert_handling.sh
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,14 @@ if sudo curl -skf --cert /var/nivlheim/my.crt --key /var/nivlheim/my.key \
fi
# Test post (it will get a 403 anyway, because the nonce is missing)
sudo curl -sk --cert /var/nivlheim/my.crt --key /var/nivlheim/my.key \
https://localhost/cgi-bin/secure/post > $tempdir/postresult
https://localhost/cgi-bin/secure/post > $tempdir/postresult || true
if ! grep -qi "revoked" $tempdir/postresult; then
echo "Post worked even though cert was blacklisted."
exit 1
fi
# Test renew
sudo curl -skf --cert /var/nivlheim/my.crt --key /var/nivlheim/my.key \
https://localhost/cgi-bin/secure/renewcert > $tempdir/renewresult
sudo curl -sk --cert /var/nivlheim/my.crt --key /var/nivlheim/my.key \
https://localhost/cgi-bin/secure/renewcert > $tempdir/renewresult || true
if ! grep -qi "revoked" $tempdir/renewresult; then
echo "Renewcert worked even though cert was blacklisted."
exit 1
Expand Down
28 changes: 23 additions & 5 deletions tests/test_change_ca.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ if [[ ! -f /var/run/nivlheim_client_last_run ]]; then
fi

# Create a new CA certificate
sudo /usr/sbin/create_new_CA.sh
sudo /etc/cron.daily/client_CA_cert.sh --force-create --verbose

# Verify that the old client certificate still works
if ! sudo curl -sSkf --cert /var/nivlheim/my.crt --key /var/nivlheim/my.key \
Expand All @@ -34,6 +34,15 @@ if ! sudo curl -sSkf --cert /var/nivlheim/my.crt --key /var/nivlheim/my.key \
exit 1
fi

# Verify that the client doesn't ask for a new certificate yet
OLDMD5=$(md5sum /var/nivlheim/my.crt)
sudo /usr/sbin/nivlheim_client
NEWMD5=$(md5sum /var/nivlheim/my.crt)
if [[ "$OLDMD5" != "$NEWMD5" ]]; then
echo "The client got get a new certificate before the new CA was activated."
exit 1
fi

# Ask for a new certificate, verify that they are still being signed with the old CA cert
A=`openssl x509 -in /var/nivlheim/my.crt -noout -issuer_hash`
sudo rm -f /var/nivlheim/my.* /var/run/nivlheim_client_last_run
Expand All @@ -49,22 +58,31 @@ if [[ "$A" != "$B" ]]; then
fi

# Activate the new CA certificate
sudo /usr/sbin/activate_new_CA.sh
sudo /etc/cron.daily/client_CA_cert.sh --force-activate --verbose

# Verify that the old client certificate still works
sudo cp /var/www/cgi-bin/ping /var/www/cgi-bin/secure/foo
if ! sudo curl -sSkf --cert /var/nivlheim/my.crt --key /var/nivlheim/my.key \
https://localhost/cgi-bin/secure/ping; then
https://localhost/cgi-bin/secure/foo; then
echo "The client cert didn't work after a new CA was activated."
exit 1
fi

# Ask for a new certificate, verify that it was signed with the new CA cert
sudo rm -f /var/nivlheim/my.* /var/run/nivlheim_client_last_run
# Run the client again, verify that it asked for (and got) a new certificate
# (because secure/ping should return 400)
# and verify that it was signed with the new CA cert
OLDMD5=$(md5sum /var/nivlheim/my.crt)
sudo rm -f /var/run/nivlheim_client_last_run
sudo /usr/sbin/nivlheim_client
if [[ ! -f /var/run/nivlheim_client_last_run ]]; then
echo "The client failed to run the third time."
exit 1
fi
NEWMD5=$(md5sum /var/nivlheim/my.crt)
if [[ "$OLDMD5" == "$NEWMD5" ]]; then
echo "The client didn't get a new certificate after the server got a new CA."
exit 1
fi
C=`openssl x509 -in /var/nivlheim/my.crt -noout -issuer_hash`
if [[ "$B" == "$C" ]]; then
echo "Still signing with the old CA cert, even after the new one was activated."
Expand Down

0 comments on commit c027423

Please sign in to comment.