Getting correctly signed SSL Certificates for cPanel/WHM Services

Back in the day I set up a few VPSes with WHM/cPanel on to host some sites. As back then the certificates for the cPanel services (FTPD, SMTP, WHM, …) were self-signed and Let’s Encrypt was still in its early days, I also ordered a wildcard certificate for *.3rds.be along with that and configured WHM to use that certificate for its services.

With the release of cPanel/WHM 56, cPanel started offering free cPanel-signed hostname certificates, removing the requirement for a user to get and install a custom one. New installs immediately use those cPanel-signed certificates, but setups from before that – like mine – still kept their old configuration. As my wildcard certificate is up for renewal, I decided to switch to the cPanel-signed hostname certificate … but how to do that?

In short

  1. Adjust one of the services to use a self-signed certificate.
  2. Run /usr/local/cpanel/bin/checkallsslcerts on the server to have it replace the self-signed certificated with a cPanel-signed one.
  3. Apply the cPanel-signed certificate to the other services.

The longer version

  1. Log on to your WHM server and go to the “Manage Service SSL Certificates” section. There you should see all cPanel services with your current certificate activated


    Services with a soon to expire certificate

  2. For one of the services, hit “Reset Certificate”. That will remove your custom certificate and replace it with a self-signed one


    The FTP Server Service, now with a self-signed certificate

  3. SSH to your server (as root) and run /usr/local/cpanel/bin/checkallsslcerts. That will replace the self-signed certificate for the one service with a cPanel-signed one

    root@s01 [~]# /usr/local/cpanel/bin/checkallsslcerts
    The system will check for the certificate for the “cpanel” service.
    The system will attempt to verify that the certificate for the “cpanel” service is still valid using OCSP (Online Certificate Status Protocol).
    The certificate for the “cpanel” service passed all checks.
    The system will check for the certificate for the “dovecot” service.
    The system will attempt to verify that the certificate for the “dovecot” service is still valid using OCSP (Online Certificate Status Protocol).
    The certificate for the “dovecot” service passed all checks.
    The system will check for the certificate for the “exim” service.
    The system will attempt to verify that the certificate for the “exim” service is still valid using OCSP (Online Certificate Status Protocol).
    The certificate for the “exim” service passed all checks.
    The system will check for the certificate for the “ftp” service.
    The system will attempt to replace the self-signed certificate for the “ftp” service with a signed certificate from the cPanel Store.
    The system will attempt to install a certificate for the “ftp” service from the system ssl storage.
    None of the certificates in the system ssl storage were acceptable to use for the “ftp” service.
    The system will attempt to install a certificate for the “ftp” service from the cPanel store.
    Received error “X::NoCertificate” from cPanel Store (No free ssl certificate found for this IP); requesting new certificate …
    Setting up HTTP DCV (/var/www/html/.well-known/pki-validation/54BDF83083ED3F53405BDE8A940D13C0.txt) …
    	… complete.
    Setting up DNS DCV (CNAME _54bdf83083ed3f53405bde8a940d13c0.s01.3rds.be) …
    	… complete.
    Attempting DNS DCV preflight check …
    	… success!
    Attempting to verify your certificate.....
    Querying Apache TLS for installations of the previous certificate …
    root@s01 [~]# exit

    The output for /usr/local/cpanel/bin/checkallsslcerts

  4. Reload the “Manage Service SSL Certificates” section and verify that the self-signed certificate got replaced


    The FTP Server Service, now with a cPanel-signed certificate

  5. Hit “Apply Certificate to Another Service” for the service with the cPanel-signed certificate. The form beneath the grid will be prefilled with the certificated details.

    Check all boxes of the other services, and hit apply. After that the certificate details will have been copied to those services too


    All cPanel/WHM Services, now with a cPanel-signed certificate

That’s it! 🎉

Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

☕️ Buy me a Coffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

Get HTTPS working on localhost, with green padlock

In On “Secure Contexts” in Firefox, HTTPS for local development, and a potential nice gesture by Chrome I said:

One of the things that’s still not really frictionless for your local development domains is the use of certificates.

To circumvent the use of self-signed certificates I explained in said article how I have a dedicated domain with all subdomains pointing to 127.0.0.1. That way I can use proper certificates for my local dev needs.

In How to get HTTPS working on your local development environment in 5 minutes another technique – one which works with all domains, even localhost itself – is unearthed: create a root certificate yourself, and trust it on your system. Any certificate linked to that root CA will then also be trusted, thus any host using it will have valid certificate no matter how it’s being accessed (curl, browser, etc.)

I like this “hack” because you only have to trust the generated root CA once (and not each certificate separately). I guess if you combine it with xip.io you can also get it to work on your smartphone talking to local dev stuff (or is there any way to have manual DNS entries on your iOS device?).

How to get HTTPS working on your local development environment →

On “Secure Contexts” in Firefox, HTTPS for local development, and a potential nice gesture by Chrome

👋 This post also got published on Medium. If you like it, please give it some love a clap over there.

Earlier today, in a post entitled Secure Contexts Everywhere, it was announced on the Mozilla Security Blog that Firefox from now on will only expose new features such as new CSS properties to secure contexts:

Effective immediately, all new features that are web-exposed are to be restricted to secure contexts. Web-exposed means that the feature is observable from a web page or server, whether through JavaScript, CSS, HTTP, media formats, etc. A feature can be anything from an extension of an existing IDL-defined object, a new CSS property, a new HTTP response header, to bigger features such as WebVR. In contrast, a new CSS color keyword would likely not be restricted to secure contexts.

Whilst I somewhat applaud this move, especially in the age of data theft and digital espionage, it’s – pardon my French – quite a ballsy thing to do. CSS Properties, really?

You might not be entirely aware of it, but features like Geolocation, the Payment Request API, Web Bluetooth, Service Workers, etc. are already locked down to secure contexts only.

But what is this secure context they speak of exactly? Is it HTTPS? Or is it more than that?

Let’s take a look at the MDN Web Docs on Secure Contexts:

A context will be considered secure when it’s delivered securely (or locally), and when it cannot be used to provide access to secure APIs to a context that is not secure. In practice, this means that for a page to have a secure context, it and all the pages along its parent and opener chain must have been delivered securely.

Roughly translated: http://localhost/ is considered to be a secure context, so that one will have the latest and greatest. However, the docs aren’t that clear on whether the definition of “local” involves actual DNS resolving or not:

Locally delivered files such as http://localhost and file:// paths are considered to have been delivered securely.

What about development domains – think project.local or project.test – pointing to 127.0.0.1? Right now we’re left in the blue here …

Do note however that Mozilla “will provide developer tools to ease the transition to secure contexts and enable testing without an HTTPS server”, but the related bug is still unassigned.

~

It’s best to have your local web development stack mimic the production environment as closely as possible. Over the years we’ve seen the rise of tools like Vagrant and Docker to easily allow us to do so: you can run the same OS and version with the exact same Apache version with the exact same PHP version with the exact same MySQL version with … oh you get the point.

One of the things that’s still not really frictionless right now for your local development domains is the use of certificates.

Yes, you could use self-signed certificates for this, but that’s:

  • Not always possible: The Fetch API for example doesn’t allow the ignoring of self signed certificates.
  • Asking for potential security problems the moment you go into production: I once read a post about some production code that still had the --insecure flag enabled on the curl requests 🤦‍♂️. It was put there in the first place because of to the self-signed certificates used during development.

Another approach – and one that I am taking – is to use an actual domain for your dev needs. Each project I build is reachable via a subdomain on that dedicated domain, all pointing to 127.0.0.1 thanks to a an entry in my hosts file. Since it’s an actual domain I am using, I can use an actual and correctly signed certificate along with that when it comes to HTTPS.

At the cost of only a domain name (you can get your certificates for free via Let’s Encrypt, which now support wildcards too) that’s quite a nice solution I think.

~

Back in December 2017 version 63 of Google Chrome was released. One of the changes included is that Chrome now requires https for .dev and .foo domains. This was ill-received by quite a lot of developers as most of them – including me before – use(d) the .dev TLD for local development purposes.

And yes, the pain is real … just do a quick search on Twitter on chrome https dev and you’ll see that lots of developer have wasted – and still are wasting – many hours on this breaking change.

Yes, there’s a (lengthy) workaround available to get the green lock in Chrome, but I wouldn’t recommend it really. It would require you to repeat a likewise procedure in all browsers you have running/are testing in, and this per .dev domain you have. Above that it won’t fix any out-of-browser contexts.

Could Google have prevented this .dev debacle? Of course. But I don’t see a reversal of this change coming any time soon though …

Can Google still do something about it? Well … I have an idea about this:

Just imagine if Google were to point all .dev domains to 127.0.0.1, that’d truly be paving the cowpaths! Add free SSL certificates for all .dev domains along with that and we’d jump quite far. With announcements like Secure Contexts Everywhere (referenced at the very top of this page) this idea seems like a real winner:

  1. (Chrome) Us developers can keep using the .dev TLD for projects in development.
  2. Us developers no longer need to use self-signed certificates anymore for projects in development. We could use certificates directly signed by Google’s CA (or by Let’s Encrypt in case Google doesn’t feel like providing certificates)
  3. (Firefox) Domains with the .dev TLD (and with an SSL Certificate) can be considered as a Secure Context, just like any other domain reachable via HTTPS.

One can dream, right?

FIN.

Did this help you out? Like what you see?
Consider donating.

I don’t run ads on my blog nor do I do this for profit. A donation however would always put a smile on my face though. Thanks!

☕️ Buy me a Coffee ($3)

(Laravel) Uptime Monitor

monitor-list

Today Freek from spatie released a new package:

Laravel-uptime-monitor is a powerful, easy to configure uptime monitor. It will notify you when your site is down (and when it comes back up). You can also be notified a few days before an SSL certificate on one of your sites expires. Under the hood, the package leverages Laravel 5.3’s notifications, so it’s easy to use Slack, Telegram or your preferred notification provider.

UPDATE 2016.01.03: A standalone version (read: version that doesn’t require Laravel knowledge) has also been released:

Under the hood the uptime-monitor is a vanilla Laravel 5.3 application with the laravel-uptime-monitor installed into it.

An easy to install uptime monitor →

Laravel Uptime Monitor →
An uptime and ssl certificate monitor written in PHP →

Validating SSL certificates with PHP

php-logo

Great stuff again by Freek Murze from Spatie: A PHP class to easily check the validity of an SSL Certificate. Easily? Yes, easily:

$certificate = SslCertificate::createForHostName('spatie.be');

$certificate->getIssuer(); // returns "Let's Encrypt Authority X3"
$certificate->isValid(); // returns true if the certificate is currently valid
$certificate->isValid(domain.com); // returns true if the certificate is currently valid for the given `domain.com`
$certificate->expirationDate(); // returns an instance of Carbon
$certificate->expirationDate()->diffInDays(); // returns an int

spatie/ssl-certificate (GitHub) →
Validating SSL certificates with PHP →

Diff two certificate files with certdiff

If you’ve ever had to handle certificates, it can sometimes – especially when renewing them – become confusing to know which certificate is which. Enter certdiff, a handy diff tool for certificates, by Mattias Geniar:

$ ./certdiff cronweekly.com/cert.pem sysca.st/cert.pem
subject= /CN=cronweekly.com                               |  subject= /CN=sysca.st
issuer= /C=US/O=Let's Encrypt/CN=Let's Encrypt Authority     issuer= /C=US/O=Let's Encrypt/CN=Let's Encrypt Authority
notBefore=Feb 15 08:48:00 2016 GMT                        |  notBefore=Feb 15 09:11:00 2016 GMT
notAfter=May 15 08:48:00 2016 GMT                         |  notAfter=May 15 09:11:00 2016 GMT

Differences are highlighted using the diff -y output (side-by-side diff), output with the | character in between indicates differences between the files.

certdiff (GitHub) →
certdiff (Introductory Blogpost) →

Migrating your WordPress website from HTTP to HTTPS

In light of #https2015 I flipped the switch on bram.us earlier today: from today forth bram.us is only accessible over HTTPS.

If you run a news site, or any site at all, we’d like to issue a friendly challenge to you. Make a commitment to have your site fully on HTTPS by the end of 2015 and pledge your support with the hashtag #https2015.

The switch itself should be transparent, as all “old” http:// URLs automatically redirect to their https:// counterparts.

When initially looking for information regarding this switch for WordPress sites I’ve found a lot of cruft out there. A rather high hit on Google was one dating back from 2006 (2006? … Chrome didn’t even exist for another two years) ; A few other ones recommend you to install a WordPress plugin which is just wrong (You really needn’t install a WordPress plugin to enable HTTPS!); A slew of other ones had it right, yet were incomplete; etc.

~

Here’s how to move your WordPress website from HTTP to HTTPS in just a few simple steps:

1. Install an SSL Certificate

Buy a certificate for your domain and activate it on the server. Most of the time your hosting provider offers a simple UI for this activation: paste in the private key, the certificate itself, and the CA-bundle. If you have to manually install it, I recommend taking a look at Mozilla’s SSL Configurator to get you started with the correct settings.

Note: Don’t let SSL Certificate Providers rip you off though. Services like sslcertificaten.nl or startssl.com offer them really cheap or even for free! Starting mid 2015 Let’s Encrypt will open it’s doors, offering free certificates for everyone.

You can safely install the certificate as your site will remain accessible over HTTP. In addition it’ll also be reachable over HTTPS. Verify this by manually changing the URL in the address bar from http://yoursite.com/ to https://yoursite.com/. Your website should appear, and the URL in the address bar should still hold the entered https:// URL.

Note that your browser will most likely not show the green lock icon next to the URL just yet, as your website will contain “Mixed Content”: the HTML of your site may be loaded over HTTPS, yet images contained may still be referred to using their http:// URL. We’ll fix this issue later on, in step 4.

https-mixed-content
Mixed Content warning in Chrome

Tip: Tools such as SSLLabs’ SSL Server Test come in handy to verify the proper installation of your certificate. Above that you’ll get a lovely report from it. Just one word of caution though: don’t run it on URLs used by your bank … those reports are mostly just depressing (sad but true)

Note: My first encounter with certificates back in the day had me confused about the CA-bundle. It took me a while to get to know that it’s no more than a text file containing all intermediate certificates, alle pasted one after the other.

2. Update the WordPress Settings

When having finished step 1 you’ll notice that next/prev links to blogposts and links to pages will still use the insecure http:// URLs. That’s because WordPress itself doesn’t know yet you’ll be using HTTPS from now on.

To let WordPress know you’re using HTTPS log in to wp-admin and under “Settings” → “General Settings” adjust the values for WordPress Address (URL) and Site Address (URL) to their HTTPS counterparts.

https-wordpress-settings
The WordPress Settings one needs to change

Once saved you should be able to start on your website over HTTPS and click through it – using the WordPress rendered links such as next/prev blogpost, pages, etc. – whilst remaining over HTTPS.

3. Force SSL for wp-admin

WordPress offers a setting to force the wp-admin only being accessible over HTTPS. To activate this edit your wp-config.php and add the following line:

define('FORCE_SSL_ADMIN', true);

Once adjusted you should be automatically redirected from http://yoursite.com/wp-admin/ to https://yoursite.com/wp-admin/. Whilst we later on (step 5) will redirect all traffic to HTTPS, it’s a good test before we change it all.

4. Fix those mixed content warnings

We’re almost to getting our site working entirely over HTTPS. What still remains is to update the content all our blogposts so that images referred, and blogposts linked (from within the content) use our https:// URLs instead of the http:// ones. Updating all blogposts manually would be a very time-consuming job. Luckily a single SQL query can this it for you.

Log on to PhpMyAdmin (or another tool which your hosting provider offers) and execute the queries below on your WordPress database.

Make sure you take a backup of your database first. If things were to go wrong you can always restore that backup.

Whilst at it I’ve also added a few extra queries to updating all internal pingbacks, and to updating all YouTube & Vimeo embeds.

# Update self-hosted embeds (images, iframes, scripts, etc.)
UPDATE wp_posts SET post_content = REPLACE(post_content, 'http://yoursite.com', 'https://yoursite.com');
UPDATE wp_posts SET post_content = REPLACE(post_content, 'http://www.yoursite.com', 'https://www.yoursite.com');

# Update internal pingbacks
UPDATE wp_comments SET comment_author_url = REPLACE(comment_author_url, 'http://yoursite.com', 'https://yoursite.com');
UPDATE wp_comments SET comment_author_url = REPLACE(comment_author_url, 'http://www.yoursite.com', 'https://www.yoursite.com');

# Update YouTube embeds
UPDATE wp_posts SET post_content = REPLACE(post_content, 'http://www.youtube.com', 'https://www.youtube.com');
UPDATE wp_posts SET post_content = REPLACE(post_content, 'http://img.youtube.com', 'https://img.youtube.com');

# Update Vimeo embeds
UPDATE wp_posts SET post_content = REPLACE(post_content, 'http://player.vimeo.com/', 'https://player.vimeo.com/');

# Update Flickr embeds
UPDATE wp_posts SET post_content = REPLACE(post_content, 'http://farm', 'https://farm');

# Update Slideshare embeds
UPDATE wp_posts SET post_content = REPLACE(post_content, 'http://www.slideshare.net', 'https://www.slideshare.net');

[UPDATE] Note: Whilst the internal pingbacks don’t trigger any Mixed Content warnings themselves, I thought of the update as being a bit of forward thinking: that way you don’t bounce back from HTTPS to HTTP. And whilst we’re in PhpMyAdmin anyway, I’ve added them right here.

Once executed the mixed-content warnings should be gone and the green lock should appear. Clicking from blogpost to blogpost should work just fine, all whilst remaining on HTTPS.

https-green-lock
No more mixed content: the green lock icon appears in the address bar

[UPDATE] To automatically scan one’s website for Mixed Content I’ve created a PHP CLI script that does the job for you. Check it out here.

If you do still get mixed content warnings (check for a little shield in the Chrome Address Bar) open the DevTools Console for more information: it could be – and I had this – that a CSS file still references an http:// URL. Manually fix all those errors one by one until you get the green lock icon.

https-green-lock-shield
A green lock icon is present, yet a shield (on the right) also appears. DevTools has more information needed for pinpointing the problem.

Do note that if you’ve included external resources (images/scripts/css/…) you may not be able to fix them all: if they’re not accessible over HTTPS, you won’t be able to fix that error.

5. Redirect all HTTP requests to HTTPS

Now that your site works fine over both HTTP and HTTPS, it’s time to flip the switch and make it no longer accessible over HTTP. Add these rules to your .htaccess:

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

The rules are written in such a way that “old” http:// URLs will transparently redirect to their https:// counterparts. That way incoming links that still use http:// will keep on working just fine, but will get a security upgrade along the way.

6. Tweak your SSL Settings

If you want you can tweak some of your SSL settings, such as enabling only a strict set of ciphers. Whilst this is entirely optional, one tweak is recommended though: enabling HTTP Strict Transport Security (thanks for the tip @mathias!)

To enable HSTS, add this line to your .htaccess:

# Enable HSTS
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains" env=HTTPS

~

Following these steps you should be able to migrate your WordPress website from HTTP to HTTPS. It looks quite cumbersome, yet it’s done in no time (writing this blogpost took more time than actually rolling it out). Above that you only gain benefits from embracing HTTPS. And yes, TLS is fast.

Did this help you out? Like what you see?
Consider donating.

I don’t run ads on my blog nor do I do this for profit. A donation however would always put a smile on my face though. Thanks!

☕️ Buy me a Coffee ($3)

SSL Config Generator

Just choose the web server / web front you’re using (Apache, Nginx, HAProxy) + whether you want to support only modern, intermediate, or old versions of browsers and a proper configuration will be generated.

<VirtualHost *:443>
    ...
    SSLEngine on
    SSLCertificateFile      /path/to/signed_certificate
    SSLCertificateChainFile /path/to/intermediate_certificate
    SSLCertificateKeyFile   /path/to/private/key
    SSLCACertificateFile    /path/to/all_ca_certs

    # modern configuration, tweak to your needs
    SSLProtocol             all -SSLv2 -SSLv3 -TLSv1
    SSLCipherSuite          ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK
    SSLHonorCipherOrder     on



    # HSTS
    Header add Strict-Transport-Security "max-age=15768000"
    ...
</VirtualHost>

Choosing modern will result in an A+ on SSLLabs. Of course a stuff like POODLE will be prevented.

Mozilla Security Recommended Web Server Configuration Files →