Script to generate PKI keys and CSRs

Openssl has always been a great tool for creating SSL/TLS PKI keys and certs, but I’ve not ever really had a one-liner for it… at least, not until today, messing with some automation for ChatOps and Let’s Encrypt. This could be easily adapted to create self-signed certs if desired…

One prerequisite is that you need to either edit the openssl.cnf and set defaults for all but the hostname, or edit below and put them in the \n\n\n string. Oh, and set the key passphrase, and any other configure section items (default for RHEL/CentOS environment).

#!/bin/bash
# 
# autogen hostname [san hostname2] [san hostname3] etc
#
# With more than one hostname, a Subject Alternate Name cert request is created
# CSR and KEY are put in /etc/pki/CA/certs/auto/ directory.
#
# Assumes openssl.cnf is set up with all defaults except CN
#
# 4/3/2016, nwg 

### CONFIGURE
cert_pwd=''
cert_dir="/etc/pki/CA/certs/auto"
openssl="/usr/bin/openssl"
openssl_cnf="/etc/pki/tls/openssl.cnf"
### END CONFIGURE

hostname=$1
if [ "$hostname" == "" ]; then 
    echo "Syntax: autogen hostname [san hostname2] [san hostname3] etc"
    exit 1
fi

keyfile=$cert_dir/${hostname}-key
csrfile=$cert_dir/${hostname}-csr

if [ -r "$keyfile" ]; then
    echo "$keyfile already exists - either (re)move it or choose another hostname\n";
    exit 1
fi

if [ "$2" == '' ]; then
    # no san
    printf "\n\n\n\n\n${hostname}\n\n\n\n" | $openssl req -newkey rsa:2048 -sha256 -keyout $keyfile -out $csrfile -passout "pass:$cert_pwd" > /dev/null 2>&1
else
    # san
    sanstring=''

    for i in $*; do 
	if [ "$sanstring" = "" ]; then
	    sanstring="subjectAltName=DNS:$i"
	else
	    sanstring="$sanstring,DNS:$i"
	fi
    done
    #echo $sanstring

    printf "\n\n\n\n\n${hostname}\n\n\n\n" | $openssl req -newkey rsa:2048 -sha256 -keyout $keyfile -out $csrfile -passout "pass:$cert_pwd" -reqexts SAN -config <(cat $openssl_cnf <(printf "[SAN]\n$sanstring\n")) > /dev/null 2>&1

fi

chmod 400 $keyfile
ls -l $csrfile $keyfile
echo " "
cat $csrfile

exit

As an example running it:

root:/etc/pki/CA #./autogen guyton.net www.guyton.net
-rw-r--r-- 1 root root 1212 Apr  3 16:55 /etc/pki/CA/certs/auto/guyton.net-csr
-r-------- 1 root root 1751 Apr  3 16:55 /etc/pki/CA/certs/auto/guyton.net-key
 
-----BEGIN CERTIFICATE REQUEST-----
MIIDRjCCAi4CAQAwgaoxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIEwVUZXhhczEQMA4G
A1UEBxMHSG91c3RvbjEUMBIGA1UEChMLSW52ZXNjbyBMdGQxHTAbBgNVBAsTFElu
dmVzY28gV2ViIFNlcnZpY2VzMRMwEQYDVQQDEwpndXl0b24ubmV0MS8wLQYJKoZI
hvcNAQkBFiBHQkwtRXRlY2hXZWJTZXJ2aWNlc0BpbnZlc2NvLmNvbTCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMHBPu9r3VslWsx5vzHLw1g/l69mbtHj
oS0I9yv9q174cndjfCKZ8FCS0uElqWRi8+5xS2mg7WAVekXXRMQbgQbmcMdhlekr
vTJXPoxHnEInqGWI+lGesGSJFmfPnwFF4hpCKK592Lj9NpYDT+QlXxhYwRXqT9Gk
ruAZ695ANC4QtvQ7HkLMWlhY2ou2kAhMUovC5yUpfHwc9XqrY73baVjbnOk4ZoIU
fttAxXkJ+zPTygxpzzLOudGxYugYJS6TGuZy+0qqdnoRqmg+DNAj0xTHefNOuA4H
48xFDjCDNcygTe2V55bOBo0NR7HcYCPQqINSinp0y4LorYKI0oiUy4UCAwEAAaBW
MBwGCSqGSIb3DQEJBzEPDA1lVGVjaEAxbnZlc2NvMDYGCSqGSIb3DQEJDjEpMCcw
JQYDVR0RBB4wHIIKZ3V5dG9uLm5ldIIOd3d3Lmd1eXRvbi5uZXQwDQYJKoZIhvcN
AQELBQADggEBAF//ndly8PSEhfA9vAIROLjHFYJ6qEg9ic20Y5HRR1xhwGzG1iP+
9H/uDUg1DumTLOSFxb/f6FgV0tv4M5B3gzR7Sn+Vm3zAyluQSKPrRNgzuvSWSlBw
3b+mXAoRcNJnj8ZFPr83bLccB7y2deG3pnAfr6vA5XIOahmLah5WuBBzImcnwQTJ
JbUyZ1RF5BbZnFst5/W6SxqzSKQMjuOlKReAaytDhKzksSGsNO4pOSRg2+UiZuwZ
UxumGZyCLjonM+ylHinigy0sJM2I3ovMjeioaFJqHsUd44cgOn72J6xjsfHcKX8C
qiU6zBmiQMXBb2nz/WyruO1cgbj9aSiYeFg=
-----END CERTIFICATE REQUEST-----

Apache Load Balancer Persistence

Apache 2.2’s load balancer is pretty neat. However, to get persistence to work properly, you have to be careful. Here we are setting the balancer manager to watch a client cookie called BALANCEID, and each member has a particular route string tied to it that is set in the cookie. It’s important to note that the cookie format must be: something.routestring, ie, nat.server1. If it is just server1, it will not work.

ProxyPass / balancer://mycluster/ stickysession=BALANCEID 
ProxyPassReverse / http://localhost:71/ 
ProxyPassReverse / http://localhost:72/ 
<Proxy balancer://mycluster> 
 BalancerMember http://localhost:71 route=server1 
 BalancerMember http://localhost:72 route=server2 
</Proxy> 

Now there’s the issue of the client cookie being set. What if you are load balancing a third party app or webserver and can’t easily get the cookie set on the client? No problem! While it didn’t work in 2.2.3 (in particular, the BALANCER_ROUTE_CHANGED var being set), when I tried in 2.2.11, I was able to set the cookie myself based on which balancer member was selected:

# Set session cookie if BALANCER_ROUTE_CHANGED, containing BALANCER_WORKER_ROUTE env variable, which is set to the route above 
# Note that cookie value should be a session id, followed by a period, followed by the route. 
# Since session id cookie usually not advised to be mutable, best create own cookie with anything you want 
# for the session part, just make sure to have a period and route part last 
### Used for setting cookie LoadModule headers_module modules/mod_headers.so 
Header add Set-Cookie "BALANCEID=balancer.%{BALANCER_WORKER_ROUTE}e; path=/;" env=BALANCER_ROUTE_CHANGED 
# Just give some debug info in the header, don't use once you have it working 
Header add X-Var "BALANCER_ROUTE_CHANGED=%{BALANCER_ROUTE_CHANGED}e" env=BALANCER_ROUTE_CHANGED 
Header add X-Var "BALANCER_WORKER_ROUTE=%{BALANCER_WORKER_ROUTE}e" env=BALANCER_WORKER_ROUTE 
Header add X-Var "BALANCER_SESSION_ROUTE=%{BALANCER_SESSION_ROUTE}e" env=BALANCER_SESSION_ROUTE 

Presto! Session persistence, all handled at the reverse proxy load balancer level.

Apache: Handling weak browsers

Normally most webservers these days that hold sensitive information allow SSL ciphers of 128 bit or higher. However, it would be nice to redirect older browsers to a different page, suggesting that they upgrade their browser to one supporting decent encryption. This can be done in Apache with mod_rewrite and enabling lower strength ciphers. Read on to see example configuration code…

The following belongs in your SSL VirtualHost:

# if the SSL key does not contain 3 characters
RewriteCond %{SSL:SSL_CIPHER_USEKEYSIZE}  <128
# AND there were some arguments in the URL (it was followed by ?something)
RewriteCond %{QUERY_STRING} .
# Redirect to lowcrypt, passing the requested URL as an argument with the
# original args (QUERY_STRING) intact
RewriteRule .*  http://lowcrypt.gatech.edu/index.php?https://%{SERVER_NAME}%{REQUEST_URI}?%{QUERY_STRING} [R,NE,L]

# if the key does not contain 3 characters
RewriteCond %{SSL:SSL_CIPHER_USEKEYSIZE} <128
# Redirect to lowcrypt, passing the requested URL as an argument
RewriteRule .*  http://lowcrypt.gatech.edu/index.php?https://%{SERVER_NAME}%{REQUEST_URI} [R,NE,L]

# You can tweak this to your liking, but here is a rather permissive example
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+SSLv2:+EXP:+eNULL

Note that to test this with firefox, I had to do the following to allow weak ciphers and disable strong ones:

  1. Point the Firefox browser to, “about:config”
  2. For filter, enter “ssl”
  3. Disable SSL v3 by setting "security.enable_ssl3 = false"
  4. Enable SSL v2 by setting "security.enable_ssl2 = true"
  5. I found that I had to go in and actually enable an SSL V2 cipher as well: "security.ssl2.rc2_40 = true"

Works great!

Apache: time-sensitive redirects, backdoor entry

Apache’s mod_rewrite can be used to do time-sensitive redirects… handy if you have to make a scheduled change at an inconvenient time. But even better, what if you need to get to the original site? This example also includes a url /backdoorthat sets a 15 min cookie, redirects to the main page, and an exclusion to not redirect anyone who has that cookie set. Cool stuff.

    RewriteEngine On
    # Start redirecting after this datetime
    RewriteCond %{TIME_YEAR}%{TIME_MON}%{TIME_DAY}%{TIME_HOUR}%{TIME_MIN} >200904040900
    # Don't redirect certain paths
    RewriteCond %{REQUEST_URI} !^/favicon.ico
    RewriteCond %{REQUEST_URI} !^/webservices
    RewriteCond %{REQUEST_URI} !^/backdoor
    # Don't redirect if backdoor cookie is active
    RewriteCond %{HTTP_COOKIE} !backdoor
    # Do the rewrite
    RewriteRule .* http://mynewhostname/ [R,L]

    # Allow back door access to old site (this site) - hit /backdoor and they get a cookie for
    # 15 mins such that they won't be redirected while it is active.
    RewriteRule ^/backdoor http://myoldhostname/ [CO=backdoor:yes:myoldhostname:15:/]

Apache: Convert uppercase to lowercase

We had a requirement from a client whose windows website we were migrating to UNIX that the new site be able to handle mixed case tickers, ie, /pwc, /Pwc, /PWC, /pwC, etc. Using mod_rewrite, it was doable:

 # Take any mixed or uppercase ticker and set to lower
 RewriteMap lowercase int:tolower
 RewriteRule ^(/[A-Z]...?)$ ${lowercase:$1} [R,L]
 RewriteRule ^(/.[A-Z]..?)$ ${lowercase:$1} [R,L]
 RewriteRule ^(/..[A-Z].?)$ ${lowercase:$1} [R,L]
 RewriteRule ^(/...[A-Z])$ ${lowercase:$1} [R,L] 

This case conversion will be true for any 3 or 4 char URI with an uppercase letter. (It would probably be better to replace the “.” above with [a-zA-Z], as it’s likely intended for only chars to replace. Above will transform /a/BB as well, which is probably not desired.)