Laptop with Kubernetes, WordPress and Cloudflare logo on the screen

SSL secured WordPress website on Kubernetes


Make sure that you've taken a look at the following tutorial(s) before you continue:

In this tutorial example.com will be used for project's domain name and example for project name and some prefixes. Commands presented in this tutorial should be executed from the root of tutorial's git repository.

By now you should have your MySQL and WordPress Deployments up and running. MySQL Deployment should have a corresponding Service. WordPress Deployment should have no Service. If Load Balancer Service from the previous part of this tutorial series still exists then delete it now.

You are going to need a domain. There are ways to get a free one  

You are going to learn

  • how to point your domain to a Service inside your cluster
  • how to setup DNS records using Cloudflare or Google Cloud DNS API
  • how to secure your website with free Let's Encrypt SSL/TLS certificate with auto-renewal using kube-lego
  • how to create your own nginx based LoadBalancer
  • how to create an Ingress
  • how to install WordPress on Kubernetes and have a SSL/TLS secured site up and running
  • how to create persistent disk snapshot

Serve SSL/TLS secured web application from your Kubernetes cluster

This tutorial will teach you how to secure your application and host it in your cluster. You are going to create a Load Balancer and learn how to point your domain to your cluster with Domain Name Server. If you choose you can use Cloudflare which can increase security and reduce you server’s load thus saving you even more money in the long run.  Backup strategies are also mentioned along the way.

1
Create Service for WordPress

Reliability: HighUpdated: 1 October 2017CopiedBookmarkedBookmark removed

This Service has no type declaration so it will have default type assigned which is NodePort. Creating it will not provision Google's Load Balancer unlike the one from the previous part of the tutorial. Modify k8s/wordpress/service.yaml. Find and replace example with your project name.

kubectl apply -f k8s/wordpress/service.yaml
kubectl describe service SERVICE_NAME

2
Controll the cost of Load Balancing with nginx Ingress controller

Reliability: HighUpdated: 1 October 2017CopiedBookmarkedBookmark removed

I will repeat here what was said about Load Balancers in the earlier part of the tutorial series. When a Load Balancer is created additional charges start applying. Google charges you for something called Network Load Balancing: Forwarding Rule Minimum Service Charge. Monthly cost of this is about the same as the cost of one g1-small instance. Take notice of the word "Minumum" there. Basically it means that if you have five forwarding rules or less you pay about $20 a month (quite steep in my opinion). If you have more than five then additional charges kick in.

You can avoid exceeding five forwarding rules limit by setting up your own Load Balancer. Thanks to nginx Ingresss controller you can have many different domains pointing to various Services in your cluster while still using only one forwarding rule.

Create nginx Ingress controller resources

kubectl apply -f k8s/nginx-ingress/namespace.yaml
kubectl apply -f k8s/nginx-ingress/configmap.yaml
kubectl apply -f k8s/nginx-ingress/default-deployment.yaml
kubectl apply -f k8s/nginx-ingress/default-service.yaml
kubectl apply -f k8s/nginx-ingress/deployment.yaml
kubectl apply -f k8s/nginx-ingress/service.yaml

Now get the IP of your Load Balancer. We will use it inside A type record on the DNS of your choice. Cloudflare and Google Cloud DNS based solutions are presented below.

kubectl describe svc nginx --namespace nginx-ingress

LoadBalancer Ingress: 111.111.111.11

3
Configure DNS records for your domain

Reliability: MediumUpdated: 1 October 2017CopiedBookmarkedBookmark removed

You can use Google Cloud DNS API for it or alternatively Cloudflare DNS. If you decide to use Google Cloud DNS API then you will have to create a Managed Zone. There is some little cost connected to setting up and maintaining your own Managed Zone. In my case it's been about 20 cents per month per domain. If you do not want to pay for Google's DNS services you can use Cloudflare's services instead but you will have to sign up for it first. Cloudflare brings some other extremely useful functionalities to the table so I suggest signing up for it.

Configure Cloudflare DNS

Configuring Cloudflare's DNS is as easy as falling off a log. Once you sign up for the account you can add your site. Cloudflare will ask you to configure your domain provider to use external DNS servers which addresses Cloudflare will provide to you. Once it's done go to CloudflareDNS.

Add A type record for your domain. Use the LoadBalancer Ingress IP address that you retrieved using

kubectl describe svc nginx --namespace nginx-ingress

Add two CNAME type records for sub-domains www and dev.

There is a button next to the records (orange or grey cloud) under Status column. It changes the settings of Cloudflare's HTTP proxy. We can decide if we want to use only the Cloudflare's DNS or if we want to use their HTTP proxy (CDN) as well.

In order to make it work:

  • with HTTP proxy turned Off change CloudflareCryptoSSL setting to Off
  • with HTTP proxy turned On change CloudflareCryptoSSL setting to Full

For now you should probably turn off Cloudflare's HTTP proxy for all A nad CNAME records you've added and set SSL to Off. You will turn it on after everything else is set up.

Configure Google Cloud DNS API

Alternatively to Cloudflare you can use Google Cloud DNS. Go to Google Cloud PlatformAPI Manager and enable Google Cloud DNS API

You can use Google Cloud PlatformNetwork ServicesCloud DNS to create a Managed Zone. You can also use the command line

gcloud dns managed-zones create --dns-name="example.com." --description="example zone" "example-zone"
gcloud dns managed-zones list
gcloud dns managed-zones describe example-zone

Configure your domain provider to use external DNS servers. Use name server addresses returned by the command above.

Verify if you've been redirected (should happen within 120 sec). Verification for ns-cloud-d2.googledomains.com would look like this:

watch dig example.com @ns-cloud-d2.googledomains.com.

Ctrl+C to stop watching dig.

dig +short NS example.com

Step notes

If you wanted to verify that your Service is up and reachable using your domain then you could create an Ingress without TLS but you would have to delete it afterwards. If you ever need to do that use k8s/wordpress/ingress-no-tls.yaml file.

Reliability of this step is Medium because with DNS things sometimes get a bit complicated. When something did not work for me I tried different settings, clicked here, changed something there. In the end I got it working twice so step instructions should be accurate.

4
Free self-renewing SSL certificate with kube-lego

Reliability: MediumUpdated: 1 October 2017CopiedBookmarkedBookmark removed

kube-lego is a nice piece of software that simplifies developer's life. It will connect to Let's Encrypt and automatically requests certificates domain defined in your Ingress. It only takes into account an Ingress which has kubernetes.io/tls-acme: "true" annotation. kube-lego checks all namespaces in the cluster.

kubectl create -f k8s/kube-lego/namespace.yaml
kubectl create -f k8s/kube-lego/configmap.yaml
kubectl create -f k8s/kube-lego/deployment.yaml

kube-lego Deployment additionally creates a Service on its own.

Check if kube-lego Pod is present

kubectl get pods --all-namespaces

Step notes

Notice that in k8s/kube-lego/deployment.yaml version 0.1.3 of kube-lego is used. I've tried 0.1.5 and encountered some problems. I did not have time to investigate. This is why reliability of this step is Medium

5
Create an Ingress and install WordPress

Reliability: HighUpdated: 1 October 2017CopiedBookmarkedBookmark removed

Now a new type of resource called Ingress needs to be created. Ingress is best described as a collection of rules that allow inbound connections to reach the Services in your cluster. Service we want to expose has to be of NodePort type. WordPress Service is exactly that type.

You can tell Kubernetes which type of Ingress should be created using annotations. Annotations are just another way of attaching metadata to objects. Similar in their role to labels but with a different purpose.

Ingress that will direct connections to your Service has to have annotation kubernetes.io/ingress.class: "nginx" This annotation will tell nginx Ingress controller to take care of that Ingress.

You also want kube-lego to notice that Ingress and do some work on it. Second annotation that the Ingress has to have is kubernetes.io/tls-acme: "true".

It always takes some time after Ingress creation for things to start working.

In one terminal view the logs of kube-lego Pod with

kubectl logs --namespace=kube-lego POD_NAME -f

-f stands for follow - Logs are streamed and output is appended as files grow.

Edit k8s/wordpress/ingress.yaml and adjust it to fit your needs. First find and replace example.com with your domain name and then find and replace example with your project name.

kubectl apply -f k8s/wordpress/ingress.yaml

After a while a Secret with the name specified inside k8s/wordpress/ingress.yaml should be created by kube-lego. By viewing logs of kube-lego you can examine if there are any problems with getting the certificate from Let's Encrypt.

Now finally go to type your domain name in your browser and press Enter. You should get WordPress installation page. CSS styles are probably gone but do not worry about it at all. Mixed-Content warning is fixed in next steps.

Just install WordPress as you would normally do it. First choose your language. On the next screen enter the name of the site, your admin username and password and you're done. Visit your domain again to see a clean WordPress site.

You now have a SSL secured WordPress website that is running on your Kubernetes controlled cluster in the cloud.

Congratulations !

Step notes

Remember that adding new (sub)domains to Let's Encrypt should never ever be done in a batch.
Let's Encrypt has a Rate Limits. If you exceed them it will be a huge pain in the ...

In my experience it's very easy to exceed Failed Validation limit and you get an error like this:

level=warning msg="authorization failed after 1m0s: getting authorization failed: 429 urn:acme:error:rateLimited: Error creating new authz :: Too many invalid authorizations recently."

If you wish to avoid this error you should add/uncomment domain entries inside your k8s/wordpress/ingress.yaml no more than two or three at a time. Failed Validation limit should reset after (about) an hour. You may have to delete kube-lego Deployment to stop hitting Let's Encrypt servers. Check kube-lego logs to examine what is going on.

There is another Rate Limit which when exceeded may have some nasty consequences. I have in mind the main limit which is Certificates per Registered Domain limit of 20 per week.
Do not exceed it. Ever ! You can view the status of this limit using lectl script. lectl on GitHub

Just download the lectl file and run it

chmod +x lectl
./lectl example.com

You can also use Cloudflare's PKI/TLS toolkit to view certificate information. cfssl on GitHub

cfssl certinfo -domain example.com

If you use Cloudflare and your SSL is turned on then you might see a shared certificate as the result of the last command instead of the one from Let’s Encrypt. You will probably see a lot of other domain names next to your own in such case. Don’t worry about it. Cloudflare has to decrypt the data at the edge in order to cache and filter any bad traffic. Then it re-encrypts the traffic if SSL settings say so. Cloudflare's free plan does not allow you to upload your own certificate. You will get a free shared certificate instead. It somehow works in the end.

6
Backup strategies for your WordPress website

Reliability: HighUpdated: 1 October 2017CopiedBookmarkedBookmark removed

There are two strategies for backup presented here. You can use one of them or both at the same time.

Persistent disk snapshots

You can create a snapshot of the disk by using Google Cloud Platform ConsoleCompute EngineSnapshots. Click Create snapshot at the top of the page. Make one for both of the persistent drives. Snapshots are differential. Learn more about persistent disk snapshots

Disk snapshot creation is also possible using the command line.

gcloud compute --project "PROJECT_NAME" disks snapshot "DISK_NAME" --zone "ZONE_NAME" --snapshot-names "SNAPSHOT_NAME"

Export the database and copy the files

Use WP CLI tool to export the database. Login shell does not work correctly with that custom wp-config.php so use su deployer instead of su -l deployer if you intend to use WP CLI commands.

su deployer
cd /usr/src/wordpress
wp db export

Copy the resulting file somewhere safe using following command

kubectl cp POD_NAME:/path/to/file /path/to/local/file

Archive the content of /var/www/example.com and copy it from the Pod as well.

7
Remove Mixed-Content warning

Reliability: HighUpdated: 1 October 2017CopiedBookmarkedBookmark removed

Having even a single element on your site that is served via HTTP and not HTTPS will make you site appear not secure for the browsers. Depending on the browser the padlock icon will disappear or your site will be marked as not safe in some way.

Replacing http:// with https://  in your WP_HOME and WP_SITEURL along with search and replace done on your WordPress database will fix it.

Replace http://  with https://  in k8s/wordpress/configmap.yaml for WP_HOME and WP_SITEURL and run following commands using your domain name

su deployer
cd /usr/src/wordpress
wp search-replace http://example.com https://example.com --dry-run
wp search-replace http://example.com https://example.com
kubectl apply -f k8s/wordpress/configmap.yaml
kubectl delete deployment DEPLOYMENT NAME
kubectl apply -f k8s/wordpress/deployment.yaml

If you ever decide to drop HTTPS support then you will have to change all of this back

8
Turn on Cloudflare's HTTP proxy(Optional)

Reliability: HighUpdated: 1 October 2017CopiedBookmarkedBookmark removed

Go to CloudflareDNS. Click button next to each of the A and CNAME records under Status column to turn on HTTP proxy. It should turn orange.

Go to CloudflareCrypto. Set SSL to Full. Enable Automatic HTTPS rewrites

Good job. You deserve a bottle of Grog. Arrrr !
Success ! Let's open a case of Rum. Arrrr !
New skills gained. Beers all around! Arrrr !
Thanks for reading ! Please
  •  
  •  
  • 1
  •  
  •  
  •  
  •  
  •  

Leave a comment

Your email address will not be published. Required fields are marked *

2 thoughts on “SSL secured WordPress website on Kubernetes