Last year I posted a simple script for keeping your Let’s EncryptSSL certificates current.
In conjunction with my last post sharing the “best” SSL configs you can use with Apache on CentOS, here is the current state of the cron’d renewal script I use.
systemctl stop httpd.service
systemctl stop postfix
~/letsencrypt/letsencrypt-auto -t -n --agree-tos --keep --expand --standalone certonly --rsa-key-size 4096 -m user@domain.tld -d domain.tld
# you can append more [sub]domains to a single cert with additional `-d` directives ([-d otherdomain.tld [-d sub.domain.tld...]])
#...repeat for every domain / domain group
systemctl start httpd.service
systemctl start postfix
I have this script running @weekly in cron. You should be able to get away with doing it only every month or two .. but I like to err on the side of caution.
I’m stopping and starting Postfix in addition to httpd (Apache on my system) for only two reasons: first, I am using some of the LE-issued certs in conjunction with my Postfix install; second, because I don’t know if Dovecot and my webmail system need to make sure Postfix is restarted if underlying certs change.
In follow-up to previous posts I’ve had about SSL (specifically with Let’s Encrypt), here is the set of SSL configurations I use with all my sites. These, if used correctly, should score you an “A+” with no warnings from ssllabs.com. Note: I have an improved entropy package installed (twuewand). This is adapted from the Mozilla config generator with specific options added for individual sites and/or to match Let’s Encrypt’s recommendations.
Please note: you will need to modify the config files to represent your own domains, if you choose to use these as models.
[/etc/httpd/conf.d/defaults.conf]
#SSL options for all sites
Listen 443
SSLPassPhraseDialog builtin
SSLSessionCache shmcb:/var/cache/mod_ssl/scache(512000)
SSLSessionCacheTimeout 300
Mutex sysvsem default
SSLRandomSeed startup builtin
SSLRandomSeed startup file:/dev/urandom 1024
# requires twuewand to be installed
SSLRandomSeed startup exec:/bin/twuewand 64
SSLRandomSeed connect builtin
SSLRandomSeed connect file:/dev/urandom 1024
SSLCryptoDevice builtin
# the SSLSessionTickets directive should work - but on Apache 2.4.6-45, it does not
#SSLSessionTickets off
SSLCompression off
SSLHonorCipherOrder on
# there may be an unusual use case for enabling TLS v1.1 or 1 - but I don't know what that would be
SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
SSLOptions +StrictRequire
SSLUseStapling on
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors off
SSLStaplingCache shmcb:/var/run/ocsp(128000)
#all unknown requests get domain.tld (over http)
<VirtualHost *:80>
DocumentRoot /var/html
ServerName domain.tld
ServerAlias domain.tld *.domain.tld
ErrorLog logs/domain-error_log
CustomLog logs/domain-access_log combined
ServerAdmin user@domain.tld
<Directory "/var/html">
Options All +Indexes +FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
SetOutputFilter DEFLATE
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/javascript text/css text/php
[/etc/httpd/conf.d/z-[sub-]domain-tld.conf]
<Virtualhost *:80>
ServerName domain.tld
# could use * instead of www if you don't use subdomains for anything special/separate
ServerAlias domain.tld www.domain.tld
Redirect permanent / https://domain.tld/
</VirtualHost>
<VirtualHost *:443>
SSLCertificateFile /etc/letsencrypt/live/domain.tld/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/domain.tld/privkey.pem
# if you put "fullchain.pem" here, you will get an error from ssllabs
SSLCertificateChainFile /etc/letsencrypt/live/domain.tld/chain.pem
DocumentRoot /var/www/domain
ServerName domain.tld
ErrorLog logs/domain-error_log
CustomLog logs/domain-access_log \
"%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
ServerAdmin user@domain.tld
# could put this in defaults.conf - I prefer it in each site config
SSLEngine on
<Files ~ "\.(cgi|shtml|phtml|php3?)$">
SSLOptions +StdEnvVars
</files>
<Directory "/var/www/cgi-bin">
SSLOptions +StdEnvVars
</Directory>
SetEnvIf User-Agent ".*MSIE.*" \
nokeepalive ssl-unclean-shutdown \
downgrade-1.0 force-response-1.0
<Directory "/var/www/domain">
Options All +Indexes +FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
I use the z....conf formatting to ensure all site-specific configs are loaded after everything else. That conveniently breaks every site into its own config file, too.
The config file for a non-https site is much simpler:
<VirtualHost *:80>
DocumentRoot /var/www/domain
ServerName domain.tld
ServerAlias domain.tld *.domain.tld
ErrorLog logs/domain-error_log
CustomLog logs/domain-access_log combined
ServerAdmin user@domain.tld
<Directory "/var/www/domain">
Options All +Indexes +FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
If you’re running something like Nextcloud, you may want to turn on Header always set Strict-Transport-Security "max-age=15552000; includeSubDomains" in the <VirtualHost> directive for the site. I haven’t decided yet if I should put this in every SSL-enabled site’s configs or not.
run the standalone webserver to verify “ownership” of the domain
generate just the cert
administrative email (optional, but “encouraged”)
domain(s) to issue cert for (must be individually identified with successive -d flags; LE does not support wildcard certs)
restart httpd
I set mine to run @weekly in cron – @monthly is likely good enough, but since it’s “free” to run, running slightly more than is necessary seems good to me. Plus, if you’re getting SSL certs for many domains all being served from the same server, they may have different expiration dates, so running more often is better.
Last week I helped a friend of mine get his first Let’s Encrypt certificate generated and configured for his website. One of the things I found incredibly frustrating is that Let’s Encrypt does not have a package for Red Hat/CentOS/Fedora! Ignoring such a massive installed base seems monumentally dumb – so I hope that they correct it soon. Until they do, however, here’s a tutorial that should cover the gotchas for getting Let’s Encrypt to work on a CentOS 6 server with Apache 2.
The documentation (as of 06 Jan 2015) on the Let’s Encrypt website is in error in a few places (or, at least, not as correct as is could/should be). One big thing to note, for example, is that it says Python2.6 is supported (the current release for RHEL/CentOS 6). If you run the certificate generator without the --debug flag, though, it will error-out saying Python 2.6 is not supported.
While I used an existing CentOS 6 server, I’ll start this tutorial as I have many others – by telling you to go get a CentOS 6 server from Digital Ocean or Chunk Host.
Preliminaries
Login as root (or a sudo-privileged account – but root is easier), and install Apache, Python, and SSL – yum install httpd python mod_ssl.
Also enable the EPEL repository: yum install epel-repository (available from the CentOSExtras repository. I’m going to assume you are familiar with configuring Apache, and will only provide the relevant snippets from ssl.conf herein.
Now that the basics are done, let’s move to Let’s Encrypt. I ran the tool in interactive mode (which is going to require ncurses to be available – it’s probably already installed on your system) – but you’ll want to add a crontab entry since Let’s Encrypt certs expire after 90 days, so I’ll compact the interactive session into a single command-line call at the end, which you’ll need to “know” how to do, since the --help argument doesn’t do anything yet (that I could find).
Initial Certificate Creation
First, grab the latest Let’s Encrypt from GitHub: git clone https://github.com/letsencrypt/letsencrypt &&Â cd letsencrypt
Stop Apache: service httpd stop. Let’s Encrypt is going to try to bind to ports 80 and 443 to ensure you have control the domain.
Now run the letsencrypt-auto tool – in debug mode so it’ll work with Python 2.6: ./letsencrypt-auto --debug certonly.
Use certonly because the plugins to automate installing for Apache and Nginx don’t work on CentOS yet.
Enter your domain name(s) for which you want to issue a certificate. If you accept incoming connections to www.domain.tld and domain.tld, be sure to put both in the list (likewise, if you have, say, blog.domain.tld that you want included).
Enter an administrative email address.
When the tool finishes, it’ll put symlinks in /etc/letsencrypt/live/domain.tld, with the “actual” certs in /etc/letsencrypt/archive/domain.tld. We’re going to reference the symlinks in /etc/letsencrypt/live/domain.tld next.
Edit /etc/httpd/conf.d/ssl.conf (I prefer emacs – but use whatever you prefer), and add the following lines in your VirtualHost directive: SSLCertificateFile /etc/letsencrypt/live/domain.tld/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/domain.tld/privkey.pem
SSLCACertificateFile /etc/letsencrypt/live/domain.tld/cert.pem
Try hitting https://domain.tld in your web browser – and you should be golden!
Automating Renewal
Create a small shell script called renew-LE-certs.sh somewhere you’ll remember where it is – like /root: service httpd stop
# add additional '-d' entries for more subdomains
/path/to/letsencrypt/letsencrypt-auto --debug --keep --agree-tos --rsa-key-size 2048 certonly -m ssladmin@domain.tld -d domain.tld -d www.domain.tld
service httpd start
For your crontab entry, do the following to setup monthly cert renewal: @monthly /path/to/renew-LE-certs.sh
After a friend of mine told me he wanted to deploy Jitsi on my main webserver, and me saying “sure”, I decided I wanted to get it up and running on a new server both so I knew how to do it, and to avoid the latency issues of videoconferencing from central North America to Germany and back.
Before I go into how I got it working, let me say that the official Quick Start guide is good – but it doesn’t cover anything but itself.
Now configure Apache for SSL. Start with this reference I posted.
But in the [sub]domain-specific conf file z-[sub]domain-tld.conf, add proxy and authentication lines (so that only people you allow to use your video conference can actually use it):
ProxyPreserveHost on
ProxyPass / http://localhost:8000/ nocanon
ProxyPassReverse / http://localhost:8000/
ProxyRequests off
ServerAdmin warren@warrenmyers.com
AllowEncodedSlashes NoDecode
<Proxy http://localhost:8000/*>
Order deny,allow
Allow from all
Authtype Basic
Authname "Password Required"
AuthUserFile /etc/httpd/.htpasswd
Require valid-user
</Proxy>
RewriteEngine on
RewriteRule ^/meetwith/(.*)$ http://%{HTTP_HOST}/$1 [P]
ProxyPassReverseCookiePath /meetwith /
Reload your configs, and make sure they’re happy, fixing any errors that may exist:
apachectl graceful
Setup at least one user who’ll be able to access the site:
htpasswd -B -c /etc/httpd/.htpasswd <user>
You should also configure firewalld to allow only what you want (http, https, ssh):
With any luck, when you now navigate to https://[sub.]domain.tld in your web browser, and enter your username and password you created with htpasswd, you’ll get the Jitsi welcome page!
About 2 years ago, I started running Pi-hole as a DNS resolver and ad-blocker. Then last year, I ditched it.
After seeing a recent post by Troy Hunt, though, I thought it might be worth revisiting..but I needed a better way to control how it worked.
Enter OpenVPN – a service I already run on three endpoints. Here’s what I did:
Install Pi-hole per the usual (curl -sSL https://install.pi-hole.net | bash if you’re feeling brave, curl -sSL https://install.pi-hole.net, inspect, then run, if you’re feeling a little more wary).
This time, though, I set my upstream DNS providers to Cloudflare (1.1.1.1) and Quad9 (9.9.9.9) instead of Freenom and Google.
I also did a two-step install – once with Pi-hole listening on the primary network interface on my OpenVPN endpoint (ie the public IP), and then, once I made sure all was happy, I flipped it to listen on tun0 – the OpenVPN-provided interface. This means Pi-hole can only hear DNS queries if you’re connected to the VPN.
Why the change from how I’d done it before? Two reasons (at least):
First, if you leave Pi-hole open to the world, you can get involved in DNS amplification attacks. That is muy no bueno.
Second, sometimes I don’t care about ads – sometimes I do. I don’t care, for example, most of the time when I’m home. But when I’m traveling or on my iPhone? I care a lot more then.
Bonus – since it’s only “working” when connected to my VPN, it’s super easy to check if a site isn’t working because of Pi-hole, or because it just doesn’t like my browser (hop off the VPN, refresh, and see if all is well that wasn’t when on the VPN).
Changes you need to make to your OpenVPN’s server.conf:
push "dhcp-option DNS 10.8.0.1"
This ensures clients use the OpenVPN server as their DNS resolver. (Note: 10.8.0.1 might not be your OpenVPN parent IP address; adjust as necessary.) Restart OpenVPN after making this change.
I tried getting lighttpd to only listen on on port 443 so I could use Let’s Encrypt’s SSL certs following a handful of tutorials and walk-throughs, but was unsuccessful. So I disabled lighttpd, and only start it by hand if I want to check on my Pi-hole’s status.
Speaking of which, as I write this, here is what the admin console looks like:
No man thinks more highly than I do of the patriotism, as well as abilities, of the very worthy gentlemen who have just addressed the House. But different men often see the same subject in different lights; and, therefore, I hope that it will not be thought disrespectful to those gentlemen, if, entertaining as I […]
What is reincarnation, a rancher asked his friend. Why it’s something that happens when your life is at its end: They comb your hair, and wash your neck, and clean your fingernails, and put you in a coffin away from life’s travails. Now this box and you goes in a hole that’s been dug into […]
“Famed was this Beowulf: far flew the boast of him, son of Scyld, in the Scandian lands. So becomes it a youth to quit him well with his father’s friends, by fee and gift, that to aid him, aged, in after days, come warriors willing, should war draw nigh, liegemen loyal: by lauded deeds shall […]
One bright morning in the middle of the night, two dead boys got up to fight. Back to back they faced each other, Drew their swords and shot each other. A deaf policeman heard the noise and ran to save the two dead boys. If you don’t believe this lie is true ask the blind […]