{"id":2026,"date":"2021-06-30T23:53:47","date_gmt":"2021-06-30T23:53:47","guid":{"rendered":"http:\/\/10.0.0.14:5557\/?p=2026"},"modified":"2021-07-01T03:10:47","modified_gmt":"2021-07-01T03:10:47","slug":"installing-lets-encrypt-on-a-reverse-proxy-for-multiple-dockerized-wordpress-sites","status":"publish","type":"post","link":"https:\/\/tutorials.leesonresearch.com\/tutorials\/2021\/06\/30\/installing-lets-encrypt-on-a-reverse-proxy-for-multiple-dockerized-wordpress-sites\/","title":{"rendered":"Installing Let&#8217;s Encrypt on a Reverse Proxy for Multiple Dockerized WordPress Sites"},"content":{"rendered":"\n<h3 class=\"wp-block-heading\">Why Bother with HTTPS?<\/h3>\n\n\n\n<p>In the not to distant future the web will require a SSL certificate on all websites. As it is now Google penalizes websites for using HTTP and plans to flag websites that are not using HTTPS as unsafe in Chrome. If that isn&#8217;t enough Google prioritizes websites that use HTTPS in Google search results. It&#8217;s the future. Fortunately we can acquire a free SSL certificate from the Let&#8217;s Encrypt certificate authority.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Let&#8217;s Encrypt &amp; Nginx Reverse Proxies<\/h3>\n\n\n\n<p>In order to install a Let&#8217;s Encrypt certificate on our Dockerized WordPress we need to use a reverse proxy which, if you are familiar with Antsle and running Docker apps on Antsle Antlets, then you probably are already familiar with serving Docker apps with the Antsle&#8217;s Nginx reverse proxy. <\/p>\n\n\n\n<p>We begin by creating an Antlet that can be cloned for this and future Dockerized projects using HTTPS.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Spin Up a New Ubuntu LXC<\/h2>\n\n\n\n<p>If you are not familiar with creating a new server on Antsle <a rel=\"noreferrer noopener\" href=\"https:\/\/docs.antsle.com\/antlets\/create-and-manage-antlet\" target=\"_blank\">the documentation for Antsle can walk you through the process<\/a>. What is important is that you spin up a Ubuntu 18 LXC bare metal server and not a KVM virtual machine. Since an LXC relies on the host machine&#8217;s kernel and operating system to function, the Antsle can access the ssl certificates that we will install on the new Ubuntu LXC server whereas KVM servers run their own operating system, with its own kernel, and virtual hardware separately from the Antsle so the Antsle does not have direct access to the Antlet&#8217;s SSL certificates. <\/p>\n\n\n\n<p>Okay, you have a new Ubuntu 18 LXC Antlet, <strong>before you start the Antlet<\/strong> we need to set up a network bridge with a virtual nic. This bridge will allow us to connect our Antlet to our local network which is useful for development and debugging.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Set Up a Bridged Network with Virtual NIC<\/h2>\n\n\n\n<p>Quoting from Antsle&#8217;s documentation:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>Bridged networking allows you to attach an additional virtual network interface to an antlet. This virtual NIC will connect to one of the physical ethernet ports on the rear of the Antsle by way of an internal bridge. Then the antlet can be accessed directly on the local LAN.<\/p><cite><a href=\"https:\/\/docs.antsle.com\/networking\/bridge\" target=\"_blank\" rel=\"noreferrer noopener\">Antsle<\/a><\/cite><\/blockquote>\n\n\n\n<p>Again, if you are not familiar with how to set a bridged network the <a rel=\"noreferrer noopener\" href=\"https:\/\/docs.antsle.com\/networking\/bridge\" target=\"_blank\">Antsle docs can walk you through the process<\/a>.<\/p>\n\n\n\n<p>Once the virtual nic is created you can start the server.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Using Netplan Configure a Static IP Address<\/h2>\n\n\n\n<p>Now we have a live Ubuntu Antlet but before we can access the Antlet on our local network we need to configure Netplan to configure the network interface which will give us an ip address for our browser to hit the server.<\/p>\n\n\n\n<p><strong>Configuring Netplan<\/strong><\/p>\n\n\n\n<p>SSH into the new Ubuntu Antlet and run the following:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ncd \/etc\/netplan\nls\n<\/pre><\/div>\n\n\n<p>You should see something like this where I have added an additional ethernet eth1 with a static ip:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: yaml; highlight: [5,6,7,8,9,10,11]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nnetwork:\n  version: 2\n  ethernets:\n    eth0: {dhcp4: true}\n    eth1:\n          dhcp4: no\n          addresses:\n            - 10.0.0.92\/24\n          gateway4: 10.0.0.1\n          nameservers:\n            addresses: &#x5B;8.8.8.8, 1.1.1.1]\n<\/pre><\/div>\n\n\n<p>Close the file and run:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nnetplan apply\n<\/pre><\/div>\n\n\n<p>Reboot the Antlet. Once we&#8217;re back up we can get the ip address for our local network by running:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nip a\n<\/pre><\/div>\n\n\n<p>Which should display something like the following. Scroll way down to find eth1 with our bridged local network ip address which in my case is 10.0.0.92 which will probably be different in your environment. If you are on a Mac like I am the ip will be 10.0.0.xx and on a PC something like 192.168.1.xx<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n79: eth1@if80: mtu 1500 qdisc noqueue state UP group default qlen 1000\nlink\/ether 52:54:00:b1:10:d0 brd ff:ff:ff:ff:ff:ff link-netnsid 0\ninet 10.0.0.92\/24 brd 10.0.0.255 scope global eth1\nvalid_lft forever preferred_lft forever\ninet6 fe80::5054:ff:feb1:10d0\/64 scope link dadfailed tentative\nvalid_lft forever preferred_lft forever\n<\/pre><\/div>\n\n\n<p>Now we install Docker and Docker Compose.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Install Docker Engine<\/h2>\n\n\n\n<p>Run the following to install Docker:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\napt-get update\n \napt install apt-transport-https ca-certificates curl software-properties-common\n \ncurl -fsSL https:\/\/download.docker.com\/linux\/ubuntu\/gpg | sudo apt-key add -\n \nadd-apt-repository &quot;deb &#x5B;arch=amd64] https:\/\/download.docker.com\/linux\/ubuntu bionic stable&quot;\n \napt update\n \napt-cache policy docker-ce\n# confirm Installed: (none)\n \napt install docker-ce\n \nsystemctl status docker\n<\/pre><\/div>\n\n\n<p>With docker installed we can move onto the installation of Docker Compose.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ncurl -L &quot;https:\/\/github.com\/docker\/compose\/releases\/download\/1.24.0\/docker-compose-$(uname -s)-$(uname -m)&quot; -o \/usr\/local\/bin\/docker-compose\n\nchmod +x \/usr\/local\/bin\/docker-compose\n<\/pre><\/div>\n\n\n<p>If using curl doesn\u2019t work, try pip.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# install python3\napt update\napt install python3\n \n# install pip3\napt update\napt install python3-pip\n \n# install docker-compose\npip3 install docker-compose\n \n# confirm that docker-compose is installed\ndocker-compose --version\n<\/pre><\/div>\n\n\n<p>Now that we have an Antlet with Nginx, Docker and Docker Compose installed we can clone this Antlet for future reverse proxied docker projects! This significantly streamlines our workflow. \ud83d\ude42<\/p>\n\n\n\n<p>Clone this Antlet for our reverse nginx\/docker project then the fun begins.<\/p>\n\n\n\n<p>These are our next steps in setting up our reverse proxy:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Create a domain or subdomain for our project<\/li><li>Clone two dockerized WordPress sites from my Gitlab repo<\/li><li>Test the WordPress installations using our subdomain<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Create &amp; Configure a Domain Name<\/h2>\n\n\n\n<p>Before we can continue you will have to have a registered domain name or subdomain available to use for this project. You can obtain a <a rel=\"noreferrer noopener\" href=\"https:\/\/www.freenom.com\" target=\"_blank\">free domain name from Freenom<\/a>. I haven&#8217;t used Freenom but I hear its pretty good.<\/p>\n\n\n\n<p>Once you have your domain you will need to setup a DNS A record for your domain. The Freenom the <a rel=\"noreferrer noopener\" href=\"https:\/\/my.freenom.com\/knowledgebase.php?action=displayarticle&amp;id=4\" target=\"_blank\">Freenom Knowlege base<\/a> can walk you through this.<\/p>\n\n\n\n<p>Personally, what I like to do is to group my Docker projects under one of my single domain names and assign each Docker app a unique subdomain. You can call these subdomains anything you want. <\/p>\n\n\n\n<p>To keep this tutorial simple but useful, we are going to set up two Dockerized WordPress installations that can run concurrently on our new Antlet. Eventually we will be installing Let&#8217;s Encrypt on each of these WordPress sites. Cool huh?<\/p>\n\n\n\n<p>For simplicity I will assign our WordPress apps:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>wp1.mydomainname.tld<\/li><li>wp2.mydomainname.tld<\/li><\/ul>\n\n\n\n<p>You can swap in your domain and tld that you will be using.<\/p>\n\n\n\n<p>Next step: add a dockerized WordPress app to the root of our Antlet.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"configure-wp1-site\">Install &amp; Configure WordPress<\/h2>\n\n\n\n<p>We are at the stage now where we need some apps to proxy. For the sake of expedience we are going to clone a Dockerized WordPress site from my public repository on Gitlab.<\/p>\n\n\n\n<p>If you are not already in the root directory on your Antlet change to the root directory now:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ncd ~\/\n<\/pre><\/div>\n\n\n<p>&#8230; and clone the Dockerized WordPress site:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ngit clone https:\/\/gitlab.com\/ronleeson\/dockerized-wordpress.git\n\n# List directory contents\nls\ndockerized-wordpress\n\n# Rename the directory to wp1\nmv dockerized-wordpress wp1\nls\nwp1\n\n# Clone a second dockerized-wordpress\ngit clone https:\/\/gitlab.com\/ronleeson\/dockerized-wordpress.git\n\n# List directory contents\nls\ndockerized-wordpress\n\n# Rename the directory to wp2\nmv dockerized-wordpress wp2\nls\nwp1\nwp2\n<\/pre><\/div>\n\n\n<p>Now we have two dockerized-wordpress sites: wp1 and wp2.<\/p>\n\n\n\n<p>Lets spin up wp1:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ncd ~\/wp1\n\n# Boot up the site\ndocker-compose up -d\n<\/pre><\/div>\n\n\n<p>The wp1 WordPress installation should be up and running. We can confirm this by running:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ndocker-compose ps\n            Name                           Command              State              Ports            \n----------------------------------------------------------------------------------------------------\nwp1_db_1           docker-entrypoint.sh mysqld      Up      3306\/tcp, 33060\/tcp                 \nwp1_phpmyadmin_1   \/docker-entrypoint.sh apac ...   Up      0.0.0.0:8182-&gt;80\/tcp,:::8182-&gt;80\/tcp\nwp1_wordpress_1    docker-entrypoint.sh apach ...   Up      0.0.0.0:8080-&gt;80\/tcp,:::8080-&gt;80\/tcp\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Test WP1 WordPress Installation<\/h2>\n\n\n\n<p>Before we can hit the wp1 home page we need to update two records in the wp1 database using phpMyAdmin.<\/p>\n\n\n\n<p>First: grab our static ip for this server which we setup earlier using netplan:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nip a\n<\/pre><\/div>\n\n\n<p>&#8230; scroll down your results to eth1 and note your static ip which in my case is 10.0.0.92.<\/p>\n\n\n\n<p>Second: note that phpMyAdmin is using 8182 so point your browser to http:\/\/10.0.0.92:8182 which should display phpMyAdmin&#8217;s home page:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"774\" src=\"http:\/\/10.0.0.14:5557\/wp-content\/uploads\/2021\/06\/phpMyAdmin_LP-1024x774.png\" alt=\"\" class=\"wp-image-1788\" srcset=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/phpMyAdmin_LP-1024x774.png 1024w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/phpMyAdmin_LP-300x227.png 300w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/phpMyAdmin_LP-768x580.png 768w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/phpMyAdmin_LP-600x453.png 600w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/phpMyAdmin_LP-945x714.png 945w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/phpMyAdmin_LP.png 1167w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>&#8230; click on options:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"774\" src=\"http:\/\/10.0.0.14:5557\/wp-content\/uploads\/2021\/06\/phpMyAdmin_wpdb-1024x774.png\" alt=\"\" class=\"wp-image-1791\" srcset=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/phpMyAdmin_wpdb-1024x774.png 1024w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/phpMyAdmin_wpdb-300x227.png 300w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/phpMyAdmin_wpdb-768x580.png 768w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/phpMyAdmin_wpdb-600x453.png 600w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/phpMyAdmin_wpdb-945x714.png 945w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/phpMyAdmin_wpdb.png 1167w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>&#8230; update the siteurl and home records with your server static ip, in my case http:\/\/10.0.0.92:8080. Note the port that wordpress service is running on. You can always view the wp1&#8217;s ports by running <strong>docker-compose ps<\/strong>.<br><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"774\" src=\"http:\/\/10.0.0.14:5557\/wp-content\/uploads\/2021\/06\/phpMyAdmin_siteurl_home-1024x774.png\" alt=\"\" class=\"wp-image-1793\" srcset=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/phpMyAdmin_siteurl_home-1024x774.png 1024w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/phpMyAdmin_siteurl_home-300x227.png 300w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/phpMyAdmin_siteurl_home-768x580.png 768w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/phpMyAdmin_siteurl_home-600x453.png 600w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/phpMyAdmin_siteurl_home-945x714.png 945w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/phpMyAdmin_siteurl_home.png 1167w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Now we can hit the wp1&#8217;s WordPress home page via: <strong>http:\/\/10.0.0.92:8080<\/strong>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"774\" src=\"http:\/\/10.0.0.14:5557\/wp-content\/uploads\/2021\/06\/wp1_home_page-1024x774.png\" alt=\"\" class=\"wp-image-1796\" srcset=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/wp1_home_page-1024x774.png 1024w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/wp1_home_page-300x227.png 300w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/wp1_home_page-768x580.png 768w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/wp1_home_page-600x453.png 600w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/wp1_home_page-945x714.png 945w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/wp1_home_page.png 1167w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>http:\/\/10.0.0.92:8080<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Access WP1 with Subdomain<\/h2>\n\n\n\n<h2 class=\"wp-block-heading\">Configure the Antsle Nginx Reverse Proxy<\/h2>\n\n\n\n<p>We are close. We need only to update our Antsle Nginx config to point to wp1.yourdomainname.tld.<\/p>\n\n\n\n<p>We begin: ssh into your Antsle and create a conf file for your domain name.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ncd \/etc\/nginx\/virtualhosts\nvi yourdomainname.tld.conf\n<\/pre><\/div>\n\n\n<p>Add the following config code. Remember to swap in your domain name and Antlet ip address on the highlighted lines. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; highlight: [3,16,23,36]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nserver {\n    listen 80;\n    server_name wp1.yourdomainname.tld;\n    location \/ {\n        proxy_read_timeout 300;\n        proxy_connect_timeout 300;\n        proxy_redirect off;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n        proxy_set_header X-Forwarded-Protocol $scheme;\n        proxy_set_header X-Forwarded-Ssl off;\n        proxy_set_header X-Url-Scheme $scheme;\n        proxy_set_header X-Frame-Options SAMEORIGIN;\n        proxy_pass http:\/\/10.1.1.24:8080; # Replace with your Antlet ip\n    }\n}\n\n\nserver {\n    listen 80;\n    server_name wp2.yourdomainname.tld;\n    location \/ {\n        proxy_read_timeout 300;\n        proxy_connect_timeout 300;\n        proxy_redirect off;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n        proxy_set_header X-Forwarded-Protocol $scheme;\n        proxy_set_header X-Forwarded-Ssl off;\n        proxy_set_header X-Url-Scheme $scheme;\n        proxy_set_header X-Frame-Options SAMEORIGIN;\n        proxy_pass http:\/\/10.1.1.24:8081;  # Replace with your Antlet ip\n    }\n} \n<\/pre><\/div>\n\n\n<p>Note that we added the server config for wp2 since we are here. After all, the point of this tutorial is to be able to run multiple Docker projects on one Antlet.<\/p>\n\n\n\n<p>Save the file and restart Nginx<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nservice nginx restart\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Update wp1 Site to Point to Subdomain<\/h2>\n\n\n\n<p>Go back into phpMyAdmin: http:\/\/10.0.0.92:8181 and update the <strong>siteurl<\/strong> and <strong>home<\/strong> records in the options table to point to http:\/\/wp1.yourdomainname.tld.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Test the WordPress Installation<\/h2>\n\n\n\n<p>At this point when you hit wp1.mydomainname.tld you should see the WordPress installation screen.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"774\" src=\"http:\/\/10.0.0.14:5557\/wp-content\/uploads\/2021\/06\/wp1_home_page_by_domain-1024x774.png\" alt=\"\" class=\"wp-image-1832\" srcset=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/wp1_home_page_by_domain-1024x774.png 1024w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/wp1_home_page_by_domain-300x227.png 300w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/wp1_home_page_by_domain-768x580.png 768w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/wp1_home_page_by_domain-600x453.png 600w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/wp1_home_page_by_domain-945x714.png 945w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/wp1_home_page_by_domain.png 1167w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Huzzah! Our wp1.yourdomainname.tld is now live.<\/p>\n\n\n\n<p>Now, catch your breath&#8230; breath&#8230; good. Go get a cup of tea, come back and we get to do whole thing over again for the <strong>wp2<\/strong> WordPress site. Well&#8230; not the whole thing. \ud83d\ude42 <a href=\"#configure-wp1-site\">Start from <strong>Configure wp1 Site<\/strong> swapping in wp2 for wp1.<\/a><\/p>\n\n\n\n<p><strong>In a nutshell, here&#8217;s the sequence for configuring wp2:<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>cd into wp2<\/li><li>change the port on <em>phpmyadmin<\/em> service to <strong>8183<\/strong> so we don&#8217;t get a port conflict with wp1&#8217;s <em>phpmyadmin<\/em> service which is already running on 8182<\/li><li>boot up wp2<\/li><li>update siteurl and home records using phpMyAdmin to point to static ip on <strong>port 8183<\/strong><\/li><li>confirm wp2 is running by hitting http:\/\/10.0.0.92:8080 (your ip will be different the port will be the same)<\/li><li>you might want to go into the wp2 admin to change the site&#8217;s title to differentiate the two WordPress sites wp1 and wp2 from each other<\/li><li>power up wp2<\/li><li>point your browser to http:\/\/wp2.yourdomainname.tld<\/li><li>wp2 site comes up: jump for joy<\/li><\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">What&#8217;s Next?<\/h2>\n\n\n\n<p>We now have two Dockerized WordPress installations running on one server. We could easily add a third, fourth, ad infinitum&#8230; So, now our revels have ended&#8230; not!<\/p>\n\n\n\n<p>Both our websites are running on http where all our requests and responses are sent in plain text which susceptible to interception or eavesdropping. <a rel=\"noreferrer noopener\" href=\"https:\/\/us.norton.com\/internetsecurity-wifi-what-is-a-man-in-the-middle-attack.html\" target=\"_blank\">Here&#8217;s a great resource<\/a> for detailing all the mischief that can be perpetrated over http.  \ud83d\ude41<\/p>\n\n\n\n<p>In the very near future all websites will need to move to https protocol where all traffic between the user and server is encrypted protecting the user. As well, TLS also authenticates the server you are connecting to and protects that transmitted data from tampering. To quote from the linked resource above:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>It helps me to think about it like this &#8211; HTTP in HTTPS is the equivalent of a destination, while SSL is the equivalent of a journey. The first is responsible for getting the data to your screen, and the second manages the way it gets there. With joint forces, they move data in a safe fashion.<\/p><cite><a href=\"https:\/\/www.globalsign.com\/en\/blog\/the-difference-between-http-and-https\" target=\"_blank\" rel=\"noreferrer noopener\">Globalsign<\/a><\/cite><\/blockquote>\n\n\n\n<p>Not to worry, the last steps of this tutorial will detail how to install a free SSL certificate using Let&#8217;s Encrypt.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Implementing SSL on the Antlet<\/h2>\n\n\n\n<p>The tricky part about implementing Let&#8217;s Encrypt on an Antlet is that the SSL server configuration will reside on the Antsle whereas the actual SSL certificates will reside on the Antlet. The Antlet we created is a LXC container. What does this mean? To quote from Wikipedia:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p><strong>LXC<\/strong> (<strong>Linux Containers<\/strong>) is an <a href=\"https:\/\/en.wikipedia.org\/wiki\/Operating-system-level_virtualization\">operating-system-level virtualization<\/a> method for running multiple isolated <a href=\"https:\/\/en.wikipedia.org\/wiki\/Linux\">Linux<\/a> systems (containers) on a control host using a single Linux kernel.<\/p><cite><a href=\"https:\/\/en.wikipedia.org\/wiki\/LXC\" target=\"_blank\" rel=\"noreferrer noopener\">Wikipedia<\/a><\/cite><\/blockquote>\n\n\n\n<p>What does that mean to us? Since our LXC server is running on the Antsle host, Antsle can access files from within our LXC container. So, when we get to updating the server config on the Antsle for SSL, Antsle can access the ssl certs that we will install on our running Antlet. <\/p>\n\n\n\n<p>Essentially what we have set up now serving over http are two reverse proxies, one proxy on the Antsle, the second proxy on the Antlet which then directs traffic to each of our Docker WordPress sites. This will become clear as we move along.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Manual Installation of Let&#8217;s Encrypt on the Antlet<\/h2>\n\n\n\n<p>First step, install certbot on the Antlet:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\napt-get update\napt-get install certbot\n<\/pre><\/div>\n\n\n<p>&#8230; using certbot we generate the certificates:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ncertbot certonly --manual --preferred-challenges dns\n<\/pre><\/div>\n\n\n<p>Let&#8217;s Encrypt will walk us through the process of providing your email address and the names of your subdomains that we want certificates for after which Let&#8217;s Encrypt is going to ask us to deploy a DNS TXT record under the names of our subdomains:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nPlease deploy a DNS TXT record under the name\n_acme-challenge.wp1.leesonresearch.com with the following value:\n\nsgU6O0ugaY0Grn93uRJ053gop3Wx6uktiJCfoM02a40\n\nBefore continuing, verify the record is deployed.\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nPress Enter to Continue\n<\/pre><\/div>\n\n\n<p>Now you will have to go to your domain&#8217;s registrar to add the TXT record for wp1.yourdomainname.tld which will lood something like this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"108\" src=\"http:\/\/10.0.0.14:5557\/wp-content\/uploads\/2021\/06\/dns_txt_record-1-1024x108.png\" alt=\"\" class=\"wp-image-1890\" srcset=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/dns_txt_record-1-1024x108.png 1024w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/dns_txt_record-1-300x32.png 300w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/dns_txt_record-1-768x81.png 768w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/dns_txt_record-1-600x63.png 600w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/dns_txt_record-1-945x100.png 945w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/dns_txt_record-1.png 1281w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>After entering and submitting the txt record for wp1, go get a cup of tea and chill because it will take some time for the update to propagate through the internet before Let&#8217;s Encrypt can shake hands with your TXT record for wp1.<\/p>\n\n\n\n<p>Once Let&#8217;s Encrypt verifies your TXT record for wp1 you can hit enter to continue with setting the text record for wp2.<\/p>\n\n\n\n<p>After continuing with the verification process on our subdomains, Let&#8217;s Encrypt will confirm success and tell us where the certs live on our Antlet. We will need the paths to our certs in order to update the Nginx config on our Antsle. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nIMPORTANT NOTES:\n - Congratulations! Your certificate and chain have been saved at:\n   \/etc\/letsencrypt\/live\/wp2.yourdomainname.tld\/fullchain.pem\n   Your key file has been saved at:\n   \/etc\/letsencrypt\/live\/wp2.yourdomainname.tld\/privkey.pem\n   Your cert will expire on 2021-09-07. To obtain a new or tweaked\n   version of this certificate in the future, simply run certbot\n   again. To non-interactively renew *all* of your certificates, run\n   &quot;certbot renew&quot;\n<\/pre><\/div>\n\n\n<p>Currently the Nginx server configuration for our subdomains on our Antsle is serving over http protocol.<\/p>\n\n\n\n<p>SSH into your Antsle:<\/p>\n\n\n\n<p>Now we have to update these server configs to switch over from http to https protocol. We&#8217;ll start with wp1. My LXC Ubuntu server is running on my Antsle as 10.1.1.24. Your LXC server ip will be different.<\/p>\n\n\n\n<p>Might be easiest to delete the config for http above and replace with https server config below.<\/p>\n\n\n\n<p>Update the highlighted rows with your subdomain, Antlet ip and Antlet name with yours.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; highlight: [7,8,12,18,19,20,40]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# THE FOLLOWING SERVER CONF RESIDES ON ANTSLE WHICH POINTS TO ANTLET\n# \/antlets\/your-antlet-name\/etc\/letsencrypt is the path to certs on Antlet\n# So \/antlets\/your-antlet-name is the path to the Antlet\n\nserver {\n  listen 80;\n  server_name wp1.yourdomainname.tld;\n  rewrite ^ $scheme:\/\/wp1.yourdomainname.tld$request_uri redirect;\n}\n\nupstream sslwp1.yourdomainname.tld {\n  server 10.1.1.14:8080;\n}\n\nserver {\n  listen 443 ssl;\n  ssl on;\n  ssl_certificate \/antlets\/your-antlet-name\/etc\/letsencrypt\/live\/wp1.yourdomainname.tld\/fullchain.pem;\n  ssl_certificate_key \/antlets\/your-antlet-name\/etc\/letsencrypt\/live\/wp1.yourdomainname.tld\/privkey.pem;\n  server_name wp1.yourdomainname.tld;\n  root \/var\/www;\n \n  location \/ {\n  proxy_read_timeout 300;\n  proxy_connect_timeout 300;\n  proxy_redirect off;\n  proxy_buffers 4 256k;\n  proxy_buffer_size 128k;\n  proxy_busy_buffers_size 256k;\n \n  proxy_set_header Host $host;\n  proxy_set_header X-Real_IP $remote_addr;\n  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n  proxy_set_header X-Forwarded-Proto $scheme;\n  proxy_set_header X-Forwarded-Protocol $scheme;\n  proxy_set_header X-Forwarded-Ssl off;\n  proxy_set_header X-Url-Scheme $scheme;\n  proxy_set_header X-Frame-Options SAMEORIGIN;\n \n  proxy_pass http:\/\/sslwp1.yourdomainname.tld;\n  }\n}\n<\/pre><\/div>\n\n\n<p>Save your file and test your new configuration:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nnginx -t\n<\/pre><\/div>\n\n\n<p>If Nginx configuration is successful, restart the Antsle&#8217;s Nginx:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nservice nginx restart\n<\/pre><\/div>\n\n\n<p>Before testing https:\/\/wp1.yourdomainname.tld you might want to clear your browser cache otherwise your browser may automatically kick over to http:\/\/wp1.yourdomainname.tld.<\/p>\n\n\n\n<p>Cache cleared? Hit https:\/\/wp1.yourdomainname.tld and you should see something like this in Google Chrome:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1006\" height=\"586\" src=\"http:\/\/10.0.0.14:5557\/wp-content\/uploads\/2021\/06\/secure_icon_url-1.png\" alt=\"\" class=\"wp-image-1918\" srcset=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/secure_icon_url-1.png 1006w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/secure_icon_url-1-300x175.png 300w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/secure_icon_url-1-768x447.png 768w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/secure_icon_url-1-600x350.png 600w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/06\/secure_icon_url-1-945x550.png 945w\" sizes=\"auto, (max-width: 1006px) 100vw, 1006px\" \/><\/figure>\n\n\n\n<p>Repeat the same process above to configure Nginx server for wp2.<\/p>\n\n\n\n<p>If all went well we should have to SSL secured Dockerized WordPress sites running on our Antlet.<\/p>\n\n\n\n<p>We are not quite done because these certificates will expire in ninety days so we need to setup a cron job that will try to renew the certificates and if the certs have yet to expire nothing happens. If the certificates are up for expiration then the certificates on both wp1 and wp2 will automatically renewed.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nvi \/etc\/cron.daily\/renewcerts\n<\/pre><\/div>\n\n\n<p>Add the following to the renewcerts shell script:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n#!\/bin\/bash\ncertbot renew\nservice nginx reload\n<\/pre><\/div>\n\n\n<p>&#8230; make our shell script executable:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nchmod a+x \/etc\/cron.daily\/renewcerts\n<\/pre><\/div>\n\n\n<p>We now have both WordPress sites secured with https protocol. Congratulations!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Building an Nginx reverse proxy to redirect to multiple docker apps.<\/p>\n","protected":false},"author":1,"featured_media":1684,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[49,13,11],"tags":[3,21,25,24],"class_list":["post-2026","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-antsle","category-docker","category-server-ops","tag-docker","tag-docker-compose","tag-lets-encrypt","tag-wordpress"],"_links":{"self":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts\/2026","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/comments?post=2026"}],"version-history":[{"count":8,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts\/2026\/revisions"}],"predecessor-version":[{"id":2038,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts\/2026\/revisions\/2038"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/media\/1684"}],"wp:attachment":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/media?parent=2026"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/categories?post=2026"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/tags?post=2026"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}