One place for hosting & domains

      How To Install and Configure LXD on Ubuntu 20.04


      The author selected the Free and Open Source Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      A Linux container is a set of processes that is separated from the rest of the system. To the end-user, a Linux container functions as a virtual machine, but it’s much more light-weight. You don’t have the overhead of running an additional Linux kernel, and the containers don’t require any CPU hardware virtualization support. This means you can create more containers than virtual machines on the same server.

      Imagine that you have a server that should run multiple web sites for your customers. On the one hand, each web site could be a virtual host/server block of the same instance of the Apache or Nginx web server. On the other hand, when using virtual machines, you would create a separate nested virtual machine for each website. Linux containers sit somewhere between virtual hosts and virtual machines.

      LXD lets you create and manage these containers. LXD provides a hypervisor service to manage the entire life cycle of containers. In this tutorial, you’ll configure LXD and use it to run Nginx in a container. You’ll then route traffic from the internet to the container to make a sample web page accessible.

      Prerequisites

      To complete this tutorial, you’ll need the following:

      Note: Starting from Ubuntu 20.04, LXD is available officially as a snap package. This is a new package format and it has several advantages. A snap package can be installed in any Linux distribution that supports snap packages. It is suggested to use a server with at least 2GB RAM when running the LXD snap package. The following table summarizes the features of the LXD snap package:

      Feature snap package
      available LXD versions 2.0, 3.0, 4.0, 4.x
      memory requirements moderate, for snapd service. Suggested server with 2GB RAM
      upgrade considerations can defer LXD upgrade up to 60 days
      ability to upgrade from the other package format can upgrade from deb to snap

      Follow the rest of this tutorial to use LXD from the snap package in Ubuntu 20.04. If, however, you want to use the LXD deb package, see our tutorial How To Install and Use LXD on Ubuntu 18.04.

      Step 1 — Preparing Your Environment for LXD

      Before you configure and run LXD, you will prepare your server’s environment. This involves adding your sudo user to the lxd group and configuring your storage backend.

      Adding your non-root account to the lxd Unix group

      When setting up your non-root account, add them to the lxd group using the following command. The adduser command takes as arguments the user account and the Unix group in order to add the user account into the existing Unix group:

      Now apply the new membership:

      Enter your password and press ENTER.

      Finally, confirm that your user is now added to the lxd group:

      You will receive an output like this:

      Now you are ready to continue configuring LXD.

      Preparing the storage backend

      To begin, you will configure the storage backend.

      The recommended storage backend for LXD when you run it on Ubuntu is the ZFS filesystem. ZFS also works very well with DigitalOcean Block Storage. To enable ZFS support in LXD, first update your package list and then install the zfsutils-linux auxiliary package:

      • sudo apt update
      • sudo apt install -y zfsutils-linux

      We are almost ready to run the LXD initialization script.

      Before you do, you must identify and take a note of the device name for your block storage.

      To do so, use ls to check the /dev/disk/by-id/ directory:

      In this specific example, the full path of the device name is /dev/disk/by-id/scsi-0DO_Volume_volume-fra1-0:

      Output

      total 0 lrwxrwxrwx 1 root root 9 Sep 16 20:30 scsi-0DO_Volume_volume-fra1-0 -> ../../sda

      Note down the full file path for your storage device. You will use it in the following step when you configure LXD.

      Step 2 — Initializing and Configuring LXD

      LXD is available as a snap package in Ubuntu 20.04. It comes pre-installed, but you must configure it.

      First, verify that the LXD snap package is installed. The command snap list shows installed snap packages:

      Ubuntu 20.04 preinstalls LXD 4.0.3, and it is tracking the 4.0/stable channel. LXD 4.0 is supported for five years (until the year 2025). It will only receive security updates:

      Output of the "snap list" command — Listing the installed snap packages

      Name Version Rev Tracking Publisher Notes core18 20200724 1885 latest/stable canonical✓ base lxd 4.0.3 16922 4.0/stable/… canonical✓ - snapd 2.45.3.1 8790 latest/stable canonical✓ snapd

      To find more information about the LXD installed snap package, run snap info lxd. You will be able to see the available versions, including when the package was last updated.

      You will now configure LXD.

      Configuring Storage Options for LXD

      Start the LXD initialization process using the sudo lxd init command:

      First, the program will ask if you want to enable LXD clustering. For the purposes of this tutorial, press ENTER to accept the default no, or type no and then press ENTER. LXD clustering is an advanced topic that enables high availability for your LXD setup and requires at least three LXD servers running in a cluster:

      Output

      Would you like to use LXD clustering? (yes/no) [default=no]: no

      The next six prompts deal with the storage pool. Give the following responses:

      • Press ENTER to configure a new storage pool.
      • Press ENTER to accept the default storage pool name.
      • Press ENTER to accept the default zfs storage backend.
      • Press ENTER to create a new ZFS pool.
      • Type yes to use an existing block device.
      • Lastly, type the full path to the block storage device name (This is what you recorded earlier. It should be something like: /dev/disk/by-id/device_name).

      Your answers will look like the following:

      Output

      Do you want to configure a new storage pool? (yes/no) [default=yes]: yes Name of the new storage pool [default=default]: default Name of the storage backend to use (btrfs, dir, lvm, zfs) [default=zfs]: zfs Create a new ZFS pool? (yes/no) [default=yes]: yes Would you like to use an existing block device? (yes/no) [default=no]: yes Path to the existing block device: /dev/disk/by-id/scsi-0DO_Volume_volume-fra1-01

      You have now configured the storage backend for LXD. Continuing with LXD’s init script, you will now configure some networking options.

      Configuring Networking Options for LXD

      LXD now asks whether you want to connect to a MAAS (Metal As A Server) server. MAAS is software that makes a bare-metal server appear as, and be handled as if, a virtual machine.

      We are running LXD in standalone mode, therefore accept the default and answer no:

      Output

      Would you like to connect to a MAAS server? (yes/no) [default=no]: no

      You are then asked to configure a network bridge for LXD containers. This enables the following features:

      • Each container automatically gets a private IP address.
      • Each container can communicate with each other over the private network.
      • Each container can initiate connections to the internet.
      • Each container remains inaccessible from the internet by default; you cannot initiate a connection from the internet and reach a container unless you explicitly enable it. You’ll learn how to allow access to a specific container in the next step.

      When asked to create a new local network bridge, choose yes:

      Output

      Would you like to create a new local network bridge? (yes/no) [default=yes]: yes

      Then accept the default name, lxdbr0:

      Output

      What should the new bridge be called? [default=lxdbr0]: lxdbr0

      Accept the automated selection of private IP address range for the bridge:

      Output

      What IPv4 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: auto What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: auto

      Finally, LXD asks the following miscellaneous questions:

      When asked if you want to manage LXD over the network, press ENTER or answer no:

      Output

      Would you like LXD to be available over the network? (yes/no) [default=no]: no

      When asked if you want to update stale container images automatically, press ENTER or answer yes:

      Output

      Would you like stale cached images to be updated automatically? (yes/no) [default=yes] yes

      When asked if you want to view and keep the YAML configuration you just created, answer yes if you do. Otherwise, you press ENTER or answer no:

      Output

      Would you like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]: no

      A script will run in the background. It is normal no to receive any output.

      You have now configured your network and storage options for LXD. Next you will create your first LXD container.

      Step 2 — Creating and Configuring an LXD Container

      Now that you have successfully configured LXD, you are ready to create and manage your first container. In LXD, you manage containers using the lxc command followed by an action, such as list, launch, start, stop and delete.

      Use lxc list to view the available installed containers:

      Since this is the first time that the lxc command communicates with the LXD hypervisor, it shows some information about how to launch a container. Finally, the command shows an empty list of containers. This is expected because we haven’t created any yet:

      Output of the "lxd list" command

      To start your first container, try: lxc launch ubuntu:18.04 +------+-------+------+------+------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +------+-------+------+------+------+-----------+

      Now create a container that runs Nginx. To do so, first use the lxc launch command to create and start an Ubuntu 18.04 container named webserver.

      Create the webserver container. The 18.04 in ubuntu:18.04 is a shortcut for Ubuntu 18.04. ubuntu: is the identifier for the preconfigured repository of LXD images. You could also use ubuntu:bionic for the image name:

      • lxc launch ubuntu:20.04 webserver

      Note: You can find the full list of all available Ubuntu images by running lxc image list ubuntu: and other Linux distributions by running lxc image list images:. Both ubuntu: and images: are repositories of container images. For each container image, you can get more information with the command lxc image info ubuntu:20.04.

      Because this is the first time you’ve created a container, this command downloads the container image from the internet and caches it. You’ll see this output once your new container finishes downloading:

      Output

      Creating webserver Starting webserver

      With the webserver container started, use the lxc list command to show information about it. We added --columns ns4 in order to show only the columns for name, state and IPv4 address. The default lxc list command shows three more columns: the IPv6 address, whether the container is persistent or ephemeral, and whether there are snapshots available for each container:

      The output shows a table with the name of each container, its current state, its IP address, and its type:

      Output

      +-----------+---------+------------------------------------+ | NAME | STATE | IPV4 | +-----------+---------+------------------------------------+ | webserver | RUNNING | your_webserver_container_ip (eth0) | +-----------+---------+------------------------------------+

      LXD’s DHCP server provides this IP address and in most cases it will remain the same even if the server is rebooted. However, in the following steps you will create iptables rules to forward connections from the internet to the container. Therefore, you should instruct LXD’s DHCP server to always give the same IP address to the container.

      The following set of commands will configure the container to obtain a static IP assignment. First, you will override the network configuration for the eth0 device that is inherited from the default LXD profile. This allows you to set a static IP address, which ensures proper communication of web traffic into and out of the container.

      Specifically, lxc config device is a command that performs the config action to configure a device. The first line has the sub-action override to override the device eth0 from the container webserver. The second line has the sub-action to set the ipv4.address field of the eth0 device of the webserver container to the IP address that was given by the DHCP server in the beginning.

      Run the first config command:

      • lxc config device override webserver eth0

      You will receive an output like this:

      Output

      Device eth0 overridden for webserver

      Now set the static IP:

      • lxc config device set webserver eth0 ipv4.address your_webserver_container_ip

      If the command is successful, you will receive no output.

      Restart the container:

      Now check the status of the container:

      You should see that the container is RUNNING and the IPV4 address is your static address.

      You are ready to install and configure Nginx inside the container.

      Step 3 — Configuring Nginx Inside an LXD Container

      In this step you will connect to the webserver container and configure the web server.

      Connect to the container with lxc shell command, which takes the name of the container and starts a shell inside the container:

      Once inside the container, your shell prompt will look like the following:

      This shell, even if it is a root shell, is limited to the container. Anything that you run in this shell stays in the container and cannot escape to the host server.

      Note: When getting a shell into a container, you may see a warning such as mesg: ttyname failed: No such device. This message is produced when the shell in the container tries to run the command mesg from the configuration file /root/.profile. You can safely ignore it. To avoid seeing it, you may remove the command mesg n || true from /root/.profile.

      Once inside your container, update the package list and install Nginx:

      • apt update
      • apt install nginx

      With Nginx installed, you will now edit the default Nginx web page. Specifically, you will add two lines of text so that it is clear that this site is hosted inside the webserver container.

      Using nano or your preferred editor, open the file /var/www/html/index.nginx-debian.html:

      • nano /var/www/html/index.nginx-debian.html

      Add the two highlighted phrases to the file:

      /var/www/html/index.nginx-debian.html

      <!DOCTYPE html> <html> <head> <title>Welcome to nginx on LXD container webserver!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx on LXD container webserver!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> ...

      You have edited the file in two places and specifically added the text on LXD container webserver. Save the file and exit your text editor.

      Now log out of the container:

      Once the server’s default prompt returns, use curl to test that the web server in the container is working. To do this, you’ll need the IP address of the web container, which you found using the lxc list command earlier.

      Use curl to test your web server:

      • curl http://your_webserver_container_ip

      You will receive the Nginx default HTML welcome page as output. Note that it includes your edits:

      Output

      <!DOCTYPE html> <html> <head> <title>Welcome to nginx on LXD container webserver!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx on LXD container webserver!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> ...

      The web server is working but you can only access it while on the host using the private IP. In the next step, you will route external requests to this container so the world can access your web site through the internet.

      Step 4 — Forwarding Incoming Connections to the Nginx Container Using LXD

      Now that you have configured Nginx, it’s time to connect the webserver container to the internet. To begin, you need to set up the server to forward any connections that it may receive on port 80 to the webserver container. To do this, you’ll create an iptables rule to forward network connections. You can learn more about IPTables in our tutorials, How the IPtables Firewall Works and IPtables Essentials: Common Firewall Rules and Commands.

      This iptables command requires two IP addresses: the public IP address of the server (your_server_ip) and the private IP address of the webserver container (your_webserver_container_ip), which you can obtain using the lxc list command.

      Execute this command to create a new IPtables rule:

      • PORT=80 PUBLIC_IP=your_server_ip CONTAINER_IP=your_container_ip IFACE=eth0 sudo -E bash -c 'iptables -t nat -I PREROUTING -i $IFACE -p TCP -d $PUBLIC_IP --dport $PORT -j DNAT --to-destination $CONTAINER_IP:$PORT -m comment --comment "forward to the Nginx container"'

      Let’s study that command:

      • -t nat specifies that we’re using the nat table for address translation.
      • -I PREROUTING specifies that we’re adding the rule to the PREROUTING chain.
      • -i $IFACE specifies the interface eth0, which is the default public network interface on the host for Droplets.
      • -p TCP says we’re using the TCP protocol.
      • -d $PUBLIC_IP specifies the destination IP address for the rule.
      • --dport $PORT: specifies the destination port (such as 80).
      • -j DNAT says that we want to perform a jump to Destination NAT (DNAT).
      • --to-destination $CONTAINER_IP:$PORT says that we want the request to go to the IP address of the specific container and the destination port.

      Note: You can reuse this command to set up forwarding rules. Reset the variables PORT, PUBLIC_IP, CONTAINER_IP and IFACE at the start of the line. Just change the highlighted values.

      Now list your IPTables rules:

      • sudo iptables -t nat -L PREROUTING

      You’ll see output like this:

      Output

      Chain PREROUTING (policy ACCEPT) target prot opt source destination DNAT tcp -- anywhere your_server_ip tcp dpt:http /* forward to this container */ to:your_container_ip:80 ...

      Now test that the webserver is accessible from the internet

      Use the curl command from your local machine to test the connections:

      • curl --verbose 'http://your_server_ip'

      You’ll see the headers followed by the contents of the web page you created in the container:

      Output

      * Trying your_server_ip... * Connected to your_server_ip (your_server_ip) port 80 (#0) > GET / HTTP/1.1 > User-Agent: curl/7.47.0 > Accept: */* > < HTTP/1.1 200 OK < Server: nginx/1.10.0 (Ubuntu) ... <!DOCTYPE html> <html> <head> <title>Welcome to nginx on LXD container webserver!</title> <style> body { ...

      This confirms that the requests are going to the container.

      Finally, you will save the firewall rule so that it reapplies after a reboot.

      To do so, first install the iptables-persistent package:

      • sudo apt install iptables-persistent

      When installing the package, the application will prompt you to save the current firewall rules. Accept and save all current rules.

      When you reboot your machine, the firewall rule will load. In addition, the Nginx service in your LXD container will automatically restart.

      You’ve successfully configured LXD. In the final step you will learn how to stop and destroy the service.

      Step 5 — Stopping and Removing Containers Using LXD

      You may decide that you want to take down the container and delete it. In this step you will stop and remove your container.

      First, stop the container:

      Use the lxc list command to verify the status:

      You will see that the container’s state reads STOPPED:

      Output

      +-----------+---------+------+------+------------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +-----------+---------+------+------+------------+-----------+ | webserver | STOPPED | | | PERSISTENT | 0 | +-----------+---------+------+------+------------+-----------+

      To remove the container, use lxc delete:

      Running lxc list again shows that there’s no container running:

      The command will output the following:

      +------+-------+------+------+------+-----------+
      | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
      +------+-------+------+------+------+-----------+
      

      Use the lxc help command to see additional options.

      To remove the firewall rule that routes traffic to the container, first locate the rule in the list of rules with this command, which associates a line number with each rule:

      • sudo iptables -t nat -L PREROUTING --line-numbers

      You’ll see your rule, prefixed with a line number, like this:

      Output

      Chain PREROUTING (policy ACCEPT) num target prot opt source destination 1 DNAT tcp -- anywhere your_server_ip tcp dpt:http /* forward to the Nginx container */ to:your_container_ip

      Use that line number to remove the rule:

      • sudo iptables -t nat -D PREROUTING 1

      List the rules again to ensure removal:

      • sudo iptables -t nat -L PREROUTING --line-numbers

      The rule is removed:

      Output

      Chain PREROUTING (policy ACCEPT) num target prot opt source destination

      Now save the changes so that the rule doesn’t come back when you restart your server:

      • sudo netfilter-persistent save

      You can now bring up another container with your own settings and add a new firewall rule to forward traffic to it.

      Conclusion

      In this tutorial, you installed and configured LXD. You then created a website using Nginx running inside an LXD container and made it publicly available us IPtables.

      From here, you could configure more websites, each confined to its own container, and use a reverse proxy to direct traffic to the appropriate container. The tutorial How to Host Multiple Web Sites with Nginx and HAProxy Using LXD on Ubuntu 16.04 walks you through that setup.

      See the LXD reference documentation for more information on how to use LXD.

      To practice with LXD, you can try LXD online and follow the web-based tutorial.

      To get user support on LXD, visit the LXD discussion forum.



      Source link

      How To Set Up and Use LXD on Ubuntu 18.04


      The author selected the Free and Open Source Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      A Linux container is a set of processes that is separated from the rest of the system. To the end-user, a Linux container functions as a virtual machine, but it’s much more light-weight. You don’t have the overhead of running an additional Linux kernel, and the containers don’t require any CPU hardware virtualization support. This means you can create more containers than virtual machines on the same server.

      Imagine that you have a server that should run multiple web sites for your customers. On the one hand, each web site could be a virtual host/server block of the same instance of the Apache or Nginx web server. On the other hand, when using virtual machines, you would create a separate nested virtual machine for each website. Linux containers sit somewhere between virtual hosts and virtual machines.

      LXD lets you create and manage these containers. LXD provides a hypervisor service to manage the entire life cycle of containers. In this tutorial, you’ll configure LXD and use it to run Nginx in a container. You’ll then route traffic from the internet to the container to make a sample web page accessible.

      Prerequisites

      To complete this tutorial, you’ll need the following:

      Note: LXD is pre-installed in Ubuntu 18.04, and the installed LXD package is a deb package. But beginning with Ubuntu 20.04, newer versions of LXD are now only available as snap packages.

      Therefore, Ubuntu 18.04 is the last Ubuntu version that has LXD as a deb package. This LXD deb package has standard support until 2023 and End of Life in 2028. See the table below to help you decide on the package format.

      Feature deb package snap package
      available LXD versions 3.0 2.0, 3.0, 4.0, 4.x
      memory requirements minimal moderate, for snapd service
      upgrade considerations you can decide not to upgrade LXD can defer LXD upgrade up to 60 days
      ability to switch from the other package format not supported can switch from deb to snap

      Follow the rest of this tutorial to use LXD from the deb package in Ubuntu 18.04. If, however, you want to use the LXD snap package in Ubuntu 18.04, see [TODO-TUTORIAL-FOR-LXD-IN-UBUNTU-20.04].

      Step 1 — Configuring LXD

      LXD is available as a deb package in Ubuntu 18.04. It comes pre-installed, but you must configure it before you can use it. LXD is composed of the LXD service and the default client utility that helps you configure the service. This client utility is lxc. The client utility can access the LXD service if you either run it as root, or if your non-root account is a member of the lxd Unix group. In the following, we show how to add your non-root user account to the lxd Unix group and then continue with the configuration of the storage backend.

      Adding your non-root account to the lxd Unix group

      When setting up your non-root account, add them to the lxd group using the following command. The adduser command takes as arguments the user account and the Unix group in order to add the user account into the existing Unix group:

      Now apply the new membership:

      Enter your password and press ENTER.

      Finally, confirm that your user is now added to the lxd group:

      You will receive an output like this:

      Now you are ready to continue configuring LXD.

      Preparing the storage backend

      To begin, you will configure the storage backend.

      The recommended storage backend for LXD when you run it on Ubuntu is the ZFS filesystem. ZFS also works very well with DigitalOcean Block Storage. To enable ZFS support in LXD, first update your package list and then install the zfsutils-linux auxiliary package:

      • sudo apt update
      • sudo apt install -y zfsutils-linux

      We are almost ready to run the LXD initialization script.

      Before you do, you must identify and take a note of the device name for your block storage.

      To do so, use ls to check the /dev/disk/by-id/ directory:

      In this specific example, the full path of the device name is /dev/disk/by-id/scsi-0DO_Volume_volume-fra1-0:

      Output

      total 0 lrwxrwxrwx 1 root root 9 Sep 16 20:30 scsi-0DO_Volume_volume-fra1-0 -> ../../sda

      Note down the full file path for your storage device. You will use it in the following subsection.

      You are now ready to initialize LXD. Initialize LXD using the sudo lxd init command:

      A prompt will appear. The next two sections will walk you through the appropriate answers to each question.

      Configuring Storage Options for LXD

      First, the program will ask if you want to enable LXD clustering. For the purposes of this tutorial, press ENTER to accept the default no, or type no and then press ENTER. LXD clustering is an advanced topic that enables high availability for your LXD setup and requires at least three LXD servers running in a cluster:

      Output

      Would you like to use LXD clustering? (yes/no) [default=no]: no

      The next six prompts deal with the storage pool. Give the following responses:

      • Press ENTER to configure a new storage pool.
      • Press ENTER to accept the default storage pool name.
      • Press ENTER to accept the default zfs storage backend.
      • Press ENTER to create a new ZFS pool.
      • Type yes to use an existing block device.
      • Lastly, type the full path to the block storage device name (This is what you recorded earlier. It should be something like: /dev/disk/by-id/device_name).

      Your answers will look like the following:

      Output

      Do you want to configure a new storage pool? (yes/no) [default=yes]: yes Name of the new storage pool [default=default]: default Name of the storage backend to use (btrfs, dir, lvm, zfs) [default=zfs]: zfs Create a new ZFS pool? (yes/no) [default=yes]: yes Would you like to use an existing block device? (yes/no) [default=no]: yes Path to the existing block device: /dev/disk/by-id/scsi-0DO_Volume_volume-fra1-01

      You have now configured the storage backend for LXD. Continuing with LXD’s init script, you will now configure some networking options.

      Configuring Networking Options for LXD

      LXD now asks whether you want to connect to a MAAS (Metal As A Server) server. MAAS is software that makes a bare-metal server appear as, and be handled as if, a virtual machine.

      We are running LXD in standalone mode, therefore accept the default and answer no:

      Output

      Would you like to connect to a MAAS server? (yes/no) [default=no]: no

      You are then asked to configure a network bridge for LXD containers. This enables the following features:

      • Each container automatically gets a private IP address.
      • Each container can communicate with each other over the private network.
      • Each container can initiate connections to the internet.
      • Each container remains inaccessible from the internet by default; you cannot initiate a connection from the internet and reach a container unless you explicitly enable it. You’ll learn how to allow access to a specific container in the next step.

      When asked to create a new local network bridge, choose yes:

      Output

      Would you like to create a new local network bridge? (yes/no) [default=yes]: yes

      Then accept the default name, lxdbr0:

      Output

      What should the new bridge be called? [default=lxdbr0]: lxdbr0

      Accept the automated selection of private IP address range for the bridge:

      Output

      What IPv4 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: auto What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]: auto

      Finally, LXD asks the following miscellaneous questions:

      When asked if you want to manage LXD over the network, press ENTER or answer no:

      Output

      Would you like LXD to be available over the network? (yes/no) [default=no]: no

      When asked if you want to update stale container images automatically, press ENTER or answer yes:

      Output

      Would you like stale cached images to be updated automatically? (yes/no) [default=yes] yes

      When asked if you want to view and keep the YAML configuration you just created, answer yes if you do. Otherwise, you press ENTER or answer no:

      Output

      Would you like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]: no

      You have now configured your network and storage options for LXD. Next you will create your first LXD container.

      Step 2 — Creating and Configuring an LXD Container

      Now that you have successfully configured LXD, you are ready to create and manage your first container. In LXD, you manage containers using the lxc command followed by an action, such as list, launch, start, stop and delete.

      Use lxc list to view the available installed containers:

      Since this is the first time that the lxc command communicates with the LXD hypervisor, it shows some information about how to launch a container. Finally, the command shows an empty list of containers. This is expected because we haven’t created any yet:

      Output of the "lxd list" command

      To start your first container, try: lxc launch ubuntu:18.04 +------+-------+------+------+------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +------+-------+------+------+------+-----------+

      Now create a container that runs Nginx. To do so, first use the lxc launch command to create and start an Ubuntu 18.04 container named webserver.

      Create the webserver container. The 18.04 in ubuntu:18.04 is a shortcut for Ubuntu 18.04. ubuntu: is the identifier for the preconfigured repository of LXD images. You could also use ubuntu:bionic for the image name:

      • lxc launch ubuntu:18.04 webserver

      Note: You can find the full list of all available Ubuntu images by running lxc image list ubuntu: and other Linux distributions by running lxc image list images:. Both ubuntu: and images: are repositories of container images. For each container image, you can get more information with the command lxc image info ubuntu:18.04. While we launch a container with Ubuntu 18.04 for the purposes of this tutorial, you may select any available Ubuntu version for your own projects.

      Because this is the first time you’ve created a container, this command downloads the container image from the internet and caches it. You’ll see this output once your new container finishes downloading:

      Output

      Creating webserver Starting webserver

      With the webserver container started, use the lxc list command to show information about it. We added --columns ns4 in order to show only the columns for name, state and IPv4 address. The default lxc list command shows three more columns: the IPv6 address, whether the container is persistent or ephemeral, and whether there are snapshots available for each container:

      The output shows a table with the name of each container, its current state, its IP address, and its type:

      Output

      +-----------+---------+------------------------------------+ | NAME | STATE | IPV4 | +-----------+---------+------------------------------------+ | webserver | RUNNING | your_webserver_container_ip (eth0) | +-----------+---------+------------------------------------+

      LXD’s DHCP server provides this IP address and in most cases it will remain the same even if the server is rebooted. However, in the following steps you will create iptables rules to forward connections from the internet to the container. Therefore, you should instruct LXD’s DHCP server to always give the same IP address to the container.

      The following set of commands will configure the container to obtain a static IP assignment. First, you will override the network configuration for the eth0 device that is inherited from the default LXD profile. This allows you to set a static IP address, which ensures proper communication of web traffic into and out of the container.

      Specifically, lxc config device is a command that performs the config action to configure a device. The first line has the sub-action override to override the device eth0 from the container webserver. The second line has the sub-action to set the ipv4.address field of the eth0 device of the webserver container to the IP address that was given by the DHCP server in the beginning.

      Run the first config command:

      • lxc config device override webserver eth0

      You will receive an output like this:

      Output

      Device eth0 overridden for webserver

      Now set the static IP:

      • lxc config device set webserver eth0 ipv4.address your_webserver_container_ip

      If the command is successful you will receive no output.

      Restart the container:

      Now check the status of the container:

      You should see that the container is RUNNING and the IPV4 address is your static address.

      You are ready to install and configure Nginx inside the container.

      Step 3 — Configuring Nginx Inside an LXD Container

      In this step you will connect to the webserver container and configure the web server.

      Connect to the container with lxc shell command, which takes the name of the container and starts a shell inside the container:

      Once inside the container, your shell prompt will look like the following:

      This shell, even if it is a root shell, is limited to the container. Anything that you run in this shell stays in the container and cannot escape to the host server.

      Note: When getting a shell into a container, you may see a warning such as mesg: ttyname failed: No such device. This message is produced when the shell in the container tries to run the command mesg from the configuration file /root/.profile. You can safely ignore it. To avoid seeing it, you may remove the command mesg n || true from /root/.profile.

      Once inside your container, update the package list and install Nginx:

      • apt update
      • apt install nginx

      With Nginx installed, you will now edit the default Nginx web page. Specifically, you will add two lines of text so that it is clear that this site is hosted inside the webserver container.

      Using nano or your preferred editor, open the file /var/www/html/index.nginx-debian.html:

      • nano /var/www/html/index.nginx-debian.html

      Add the two highlighted phrases to the file:

      /var/www/html/index.nginx-debian.html

      <!DOCTYPE html> <html> <head> <title>Welcome to nginx on LXD container webserver!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx on LXD container webserver!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> ...

      You have edited the file in two places and specifically added the text on LXD container webserver. Save the file and exit your text editor.

      Now log out of the container:

      Once the server’s default prompt returns, use curl to test that the web server in the container is working. To do this, you’ll need the IP address of the web container, which you found using the lxc list command earlier.

      Use curl to test your web server:

      • curl http://your_webserver_container_ip

      You will receive the Nginx default HTML welcome page as output. Note that it includes your edits:

      Output

      <!DOCTYPE html> <html> <head> <title>Welcome to nginx on LXD container webserver!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx on LXD container webserver!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> ...

      The web server is working but you can only access it while on the host using the private IP. In the next step, you will route external requests to this container so the world can access your web site through the internet.

      Step 4 — Forwarding Incoming Connections to the Nginx Container Using LXD

      Now that you have configured Nginx, it’s time to connect the webserver container to the internet. To begin, you need to set up the server to forward any connections that it may receive on port 80 to the webserver container. To do this, you’ll create an iptables rule to forward network connections. You can learn more about IPTables in our tutorials, How the IPtables Firewall Works and IPtables Essentials: Common Firewall Rules and Commands.

      This iptables command requires two IP addresses: the public IP address of the server (your_server_ip) and the private IP address of the webserver container (your_webserver_container_ip), which you can obtain using the lxc list command.

      Execute this command to create a new IPtables rule:

      • PORT=80 PUBLIC_IP=your_server_ip CONTAINER_IP=your_container_ip IFACE=eth0 sudo -E bash -c 'iptables -t nat -I PREROUTING -i $IFACE -p TCP -d $PUBLIC_IP --dport $PORT -j DNAT --to-destination $CONTAINER_IP:$PORT -m comment --comment "forward to the Nginx container"'

      Let’s study that command:

      • -t nat specifies that we’re using the nat table for address translation.
      • -I PREROUTING specifies that we’re adding the rule to the PREROUTING chain.
      • -i $IFACE specifies the interface eth0, which is the default public network interface on the host for Droplets.
      • -p TCP says we’re using the TCP protocol.
      • -d $PUBLIC_IP specifies the destination IP address for the rule.
      • --dport $PORT: specifies the destination port (such as 80).
      • -j DNAT says that we want to perform a jump to Destination NAT (DNAT).
      • --to-destination $CONTAINER_IP:$PORT says that we want the request to go to the IP address of the specific container and the destination port.

      Note: You can reuse this command to set up forwarding rules. Reset the variables PORT, PUBLIC_IP, CONTAINER_IP and IFACE at the start of the line. Just change the highlighted values.

      Now list your IPTables rules:

      • sudo iptables -t nat -L PREROUTING

      You’ll see output like this:

      Output

      Chain PREROUTING (policy ACCEPT) target prot opt source destination DNAT tcp -- anywhere your_server_ip tcp dpt:http /* forward to this container */ to:your_container_ip:80 ...

      Now test that the webserver is accessible from the internet

      Use the curl command from your local machine to test the connections:

      • curl --verbose 'http://your_server_ip'

      You’ll see the headers followed by the contents of the web page you created in the container:

      Output

      * Trying your_server_ip... * Connected to your_server_ip (your_server_ip) port 80 (#0) > GET / HTTP/1.1 > User-Agent: curl/7.47.0 > Accept: */* > < HTTP/1.1 200 OK < Server: nginx/1.10.0 (Ubuntu) ... <!DOCTYPE html> <html> <head> <title>Welcome to nginx on LXD container webserver!</title> <style> body { ...

      This confirms that the requests are going to the container.

      Finally, you will save the firewall rule so that it reapplies after a reboot.

      To do so, first install the iptables-persistent package:

      • sudo apt install iptables-persistent

      When installing the package, the application will prompt you to save the current firewall rules. Accept and save all current rules.

      When you reboot your machine, the firewall rule will load. In addition, the Nginx service in your LXD container will automatically restart.

      You’ve successfully configured LXD. In the final step you will learn how to stop and destroy the service.

      Step 5 — Stopping and Removing Containers Using LXD

      You may decide that you want to take down the container and delete it. In this step you will stop and remove your container.

      First, stop the container:

      Use the lxc list command to verify the status:

      You will see that the container’s state reads STOPPED:

      Output

      +-----------+---------+------+------+------------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +-----------+---------+------+------+------------+-----------+ | webserver | STOPPED | | | PERSISTENT | 0 | +-----------+---------+------+------+------------+-----------+

      To remove the container, use lxc delete:

      Running lxc list again shows that there’s no container running:

      The command will output the following:

      Output

      +------+-------+------+------+------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +------+-------+------+------+------+-----------+

      Use the lxc help command to see additional options.

      To remove the firewall rule that routes traffic to the container, first locate the rule in the list of rules with this command, which associates a line number with each rule:

      • sudo iptables -t nat -L PREROUTING --line-numbers

      You’ll see your rule, prefixed with a line number, like this:

      Output

      Chain PREROUTING (policy ACCEPT) num target prot opt source destination 1 DNAT tcp -- anywhere your_server_ip tcp dpt:http /* forward to the Nginx container */ to:your_container_ip

      Use that line number to remove the rule:

      • sudo iptables -t nat -D PREROUTING 1

      List the rules again to ensure removal:

      • sudo iptables -t nat -L PREROUTING --line-numbers

      The rule is removed:

      Output

      Chain PREROUTING (policy ACCEPT) num target prot opt source destination

      Now save the changes so that the rule doesn’t come back when you restart your server:

      • sudo netfilter-persistent save

      You can now bring up another container with your own settings and add a new firewall rule to forward traffic to it.

      Conclusion

      In this tutorial, you installed and configured LXD. You then created a website using Nginx running inside an LXD container and made it publicly available us IPtables.

      From here, you could configure more websites, each confined to its own container, and use a reverse proxy to direct traffic to the appropriate container. The tutorial How to Host Multiple Web Sites with Nginx and HAProxy Using LXD on Ubuntu 16.04 walks you through that setup.

      See the LXD reference documentation for more information on how to use LXD.

      To practice with LXD, you can try LXD online and follow the web-based tutorial.

      To get user support on LXD, visit the LXD discussion forum.



      Source link

      Set Up a Reverse Proxy in an LXD Container to Host Multiple Websites


      Updated by Linode Contributed by Simos Xenitellis

      Introduction

      LXD (pronounced “Lex-Dee”) is a system container manager build on top of Linux Containers (LXC) supported by Canonical. The goal of LXD is to provide an experience similar to a virtual machine but through containerization rather than hardware virtualization. Compared to Docker for delivering applications, LXD offers nearly full operating-system functionality with additional features such as snapshots, live migrations, and storage management.

      A reverse proxy is a server that sits between internal applications and external clients, forwarding client requests to the appropriate server. While many common applications, such as Node.js, are able to function as servers on their own, they may lack a number of advanced load balancing, security, and acceleration features.

      This guide explains the creation of a reverse proxy in an LXD container in order to host multiple websites, each in their own additional containers. You will utilize NGINX and Apache web servers, while also relying on NGINX as a reverse proxy.

      Please refer to the following diagram to understand the reverse proxy created in this guide.

      Diagram of LXD reverse proxy and web servers

      In this guide you will:

      Note

      For simplicity, the term container is used throughout this guide to describe the LXD system containers.

      Before You Begin

      1. Complete A Beginner’s Guide to LXD: Setting Up an Apache Web Server In a Container. The guide instructs you to create a container called web with the Apache web server for testing purposes. Remove this container by running the following commands.

        lxc stop web
        lxc delete web
        

        Note

      2. This guide will use the hostnames apache1.example.com and nginx1.example.com for the two example websites. Replace these names with hostnames you own and setup their DNS entries to point them to the IP address of the server you created. For help with DNS see our DNS Manager Guide.

      Creating the Containers

      1. Create two containers called apache1 and nginx1, one with the Apache web server and another with the NGINX web server, respectively. For any additional websites, you may create new containers with your chosen web server software.

        lxc launch ubuntu:18.04 apache1
        lxc launch ubuntu:18.04 nginx1
        
      2. Create the proxy container for the reverse proxy.

        lxc launch ubuntu:18.04 proxy
        
      3. List the containers with the list command.

        lxc list
        
      4. The output will look similar to the following.

          
        +---------+---------+---------------------+-----------------------------------------------+------------+-----------+
        |  NAME   |  STATE  |        IPV4         |                     IPV6                      |    TYPE    | SNAPSHOTS |
        +---------+---------+---------------------+-----------------------------------------------+------------+-----------+
        | apache1 | RUNNING | 10.10.10.204 (eth0) | fd42:67a4:b462:6ae2:216:3eff:fe01:1a4e (eth0) | PERSISTENT |           |
        +---------+---------+---------------------+-----------------------------------------------+------------+-----------+
        | nginx1  | RUNNING | 10.10.10.251 (eth0) | fd42:67a4:b462:6ae2:216:3eff:febd:67e3 (eth0) | PERSISTENT |           |
        +---------+---------+---------------------+-----------------------------------------------+------------+-----------+
        | proxy   | RUNNING | 10.10.10.28 (eth0)  | fd42:67a4:b462:6ae2:216:3eff:fe00:252e (eth0) | PERSISTENT |           |
        +---------+---------+---------------------+-----------------------------------------------+------------+-----------+
        
        

        There are three containers, all in the RUNNING state – each with their own private IP address. Take note of the IP addresses (both IPv4 and IPv6) for the container proxy. You will need them to configure the proxy container in a later section.

        Now that the containers have been created, the following steps will detail how to set up the web server software in the apache1 and nginx1 containers, and the proxy container so that the web servers are accessible from the internet.

      Configuring the Apache Web Server Container

      When using a reverse proxy in front of a web server, the web server does not know the IP addresses of visitors. The web server only sees the IP address of the reverse proxy. However, each web server has a way to identify the real remote IP address of a visitor. For Apache, this is performed with the Remote IP Apache module. For the module to work, the reverse proxy must be configured to pass the remote IP address’ information.

      1. Start a shell in the apache1 container.

        lxc exec apache1 -- sudo --user ubuntu --login
        
      2. Update the package list in the apache1 container.

        sudo apt update
        
      3. Install the package apache2 in the container.

        sudo apt install -y apache2
        
      4. Create the file /etc/apache2/conf-available/remoteip.conf.

        remoteip.conf
        1
        2
        
        RemoteIPHeader X-Real-IP
        RemoteIPTrustedProxy 10.10.10.28 fd42:67a4:b462:6ae2:216:3eff:fe00:252e

        You can use the nano text editor by running the command sudo nano /etc/apache2/conf-available/remoteip.conf. Note, these are the IP addresses of the proxy container shown earlier, for both IPv4 and IPv6. Replace these with the IPs from your lxc list output.

        Note

        Instead of specifying the IP addresses, you can also use the hostname proxy.lxd. However, the RemoteIP Apache module is peculiar when using the hostname and will use only one of the two IP addresses (either IPv4 or IPv6), which means the Apache web server will not know the real source IP address for some connections. By listing explicitly both IPv4 and IPv6 addresses, you can be certain that RemoteIP will successfully accept the source IP information from all connections of the reverse proxy.

      5. Enable the new remoteip.conf configuration.

        sudo a2enconf remoteip
        
          
        Enabling conf remoteip.
        To activate the new configuration, you need to run:
        systemctl reload apache2
        
        
      6. Enable the remoteip Apache module.

        sudo a2enmod remoteip
        
          
        Enabling module remoteip.
        To activate the new configuration, you need to run:
        systemctl restart apache2
        
        
      7. Edit the default web page for Apache to make a reference that it runs inside a LXD container.

        sudo nano /var/www/html/index.html
        

        Change the line “It works!” (line number 224) to “It works inside a LXD container!” Save and exit.

      8. Restart the Apache web server.

        sudo systemctl reload apache2
        
      9. Exit back to the host.

        exit
        

      You have created and configured the Apache web server, but the server is not yet accessible from the Internet. It will become accessible after you configure the proxy container in a later section.

      Creating the NGINX Web Server Container

      Like Apache, NGINX does not know the IP addresses of visitors when using a reverse proxy in front of a web server. It only sees the IP address of the reverse proxy instead. Each NGINX web server software can identify the real remote IP address of a visitor with the Real IP module. For the module to work, the reverse proxy must be configured accordingly to pass the information regarding the remote IP addresses.

      1. Start a shell in the nginx1 container.

        lxc exec nginx1 -- sudo --user ubuntu --login
        
      2. Update the package list in the nginx1 container.

        sudo apt update
        
      3. Install NGINX in the container.

        sudo apt install -y nginx
        
      4. Create the file /etc/nginx/conf.d/real-ip.conf.

        real-ip.conf
        1
        2
        
        real_ip_header    X-Real-IP;
        set_real_ip_from  proxy.lxd;

        You can use the nano text editor by running the command sudo nano /etc/nginx/conf.d/real-ip.conf.

        Note

        You have specified the hostname of the reverse proxy, proxy.lxd. Each LXD container gets automatically a hostname, which is the name of the container plus the suffix .lxd. By specifying the set_real_ip_from field with proxy.lxd, you are instructing the NGINX web server to accept the real IP address information for each connection, as long as that connection originates from proxy.lxd. The real IP address information will be found in the HTTP header X-Real-IP in each connection.

      5. Edit the default web page for NGINX to make a reference that it runs inside a LXD container.

        sudo nano /var/www/html/index.nginx-debian.html
        

        Change the line “Welcome to nginx!” (line number 14) to “Welcome to nginx running in a LXD system container!”. Save and exit.

      6. Restart the NGINX web server.

        sudo systemctl reload nginx
        
      7. Exit back to the host.

        exit
        

      You have created and configured the NGINX web server, but the server is not accessible yet from the Internet. It will become accessible after you configure the proxy container in the next section.

      Setting up the Reverse Proxy

      In this section you will configure the container proxy. You will install NGINX and set it up as a reverse proxy, then add the appropriate LXD proxy device in order to expose both ports 80 and 443 to the internet.

      1. Add LXD proxy devices to redirect connections from the internet to ports 80 (HTTP) and 443 (HTTPS) on the server to the respective ports at the proxy container.

        lxc config device add proxy myport80 proxy listen=tcp:0.0.0.0:80 connect=tcp:127.0.0.1:80 proxy_protocol=true
        lxc config device add proxy myport443 proxy listen=tcp:0.0.0.0:443 connect=tcp:127.0.0.1:443 proxy_protocol=true
        
          
        Device myport80 added to proxy
        Device myport443 added to proxy
        
        

        The lxc config device add command takes as arguments:

        Argument Explanation
        proxy The name of the container.
        myport80 A name for this proxy device.
        proxy The type of the LXD device (LXD proxy device).
        listen=tcp:0.0.0.0:80 The proxy device will listen on the host (default) on port 80, protocol TCP, on all interfaces.
        connect=tcp:127.0.0.1:80 The proxy device will connect to the container on port 80, protocol TCP, on the loopback interface. In previous versions of LXD you could have specified localhost here. However, in LXD 3.13 or newer, you can only specify IP addresses.
        proxy_protocol=true Request to enable the PROXY protocol so that the reverse proxy will get the originating IP address from the proxy device.

        Note

        If you want to remove a proxy device, use lxc config device remove. If you want to remove the above device myport80, run the following command:

        lxc config device remove proxy myport80
        

        Where proxy is the name of the container, and myport80 is the name of the device.

      2. Start a shell in the proxy container.

        lxc exec proxy -- sudo --user ubuntu --login
        
      3. Update the package list.

        sudo apt update
        
      4. Install NGINX in the container.

        sudo apt install -y nginx
        
      5. Logout from the container.

        logout
        

      Direct Traffic to the Apache Web Server From the Reverse Proxy

      The reverse proxy container is running and the NGINX package has been installed. To work as a reverse proxy, add the appropriate website configuration so that NGINX can identify (with server_name below) the appropriate hostname, and then pass (with proxy_pass below) the connection to the appropriate LXD container.

      1. Start a shell in the proxy container.

        lxc exec proxy -- sudo --user ubuntu --login
        
      2. Create the file apache1.example.com in /etc/nginx/sites-available/ for the configuration of your first website.

        apache1.example.com
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        
        server {
                listen 80 proxy_protocol;
                listen [::]:80 proxy_protocol;
        
                server_name apache1.example.com;
        
                location / {
                        proxy_set_header Host $host;
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_pass http://apache1.lxd;
                }
        
                real_ip_header proxy_protocol;
                set_real_ip_from 127.0.0.1;
        }

        You can run sudo nano /etc/nginx/sites-available/apache1.example.com to open up a text editor and add the configuration. Note, in this case you only need to edit the server_name to be the hostname of the website.

      3. Enable the website.

        sudo ln -s /etc/nginx/sites-available/apache1.example.com /etc/nginx/sites-enabled/
        
      4. Restart the NGINX reverse proxy. By restarting the service, NGINX will read and apply the new site instructions just added to /etc/nginx/sites-enabled.

        sudo systemctl reload nginx
        
      5. Exit the proxy container and return back to the host.

        logout
        
      6. From your local computer, visit the URL of your website with your web browser. You should see the default Apache page:

        Web page of Apache server running in a container

        Note

        If you look at the Apache access.log file (default file /var/log/apache2/access.log), it will still show the private IP address of the proxy container instead of the real IP address. This issue is specific to the Apache web server and has to do with how the server prints the logs. Other software on the web server will be able to use the real IP. To fix this through the Apache logs, see the section Troubleshooting.

      Direct Traffic to the NGINX Web Server From the Reverse Proxy

      The reverse proxy container is running and the NGINX package has been installed. To work as a reverse proxy, you will add the appropriate website configuration so NGINX can identify (with server_name below) the appropriate hostname, and then pass (with proxy_pass below) the connection to the appropriate LXD container with the actual web server software.

      1. Start a shell in the proxy container.

        lxc exec proxy -- sudo --user ubuntu --login
        
      2. Create the file nginx1.example.com in /etc/nginx/sites-available/ for the configuration of your second website.

        nginx1.example.com
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        
        server {
                listen 80 proxy_protocol;
                listen [::]:80 proxy_protocol;
        
                server_name nginx1.example.com;
        
                location / {
                        proxy_set_header Host $host;
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_pass http://nginx1.lxd;
                }
        
                real_ip_header proxy_protocol;
                set_real_ip_from 127.0.0.1;
        }

        You can run sudo nano /etc/nginx/sites-available/nginx1.example.com to create the configuration. Note, you only need to edit the fields server_name to be the hostname of the website.

      3. Enable the website.

        sudo ln -s /etc/nginx/sites-available/nginx1.example.com /etc/nginx/sites-enabled/
        
      4. Restart the NGINX reverse proxy service.

        sudo systemctl reload nginx
        
      5. Exit the proxy container and return back to the host.

        logout
        
      6. From your local computer, visit the URL of your website with your web browser. You should see the following default NGINX page.

        Web page of the nginx server running in a container

      Adding Support for HTTPS with Let’s Encrypt

      1. Start a shell in the proxy container.

        lxc exec proxy -- sudo --user ubuntu --login
        
      2. Add the repository ppa:certbot/certbot by running the following command.

        sudo add-apt-repository ppa:certbot/certbot
        
      3. Output will look similar to the following.

          
              This is the PPA for packages prepared by Debian Let's Encrypt Team and backported for Ubuntu(s).
              More info: https://launchpad.net/~certbot/+archive/ubuntu/certbot
             Press [ENTER] to continue or Ctrl-c to cancel adding it.
        
             Get:1 http://security.ubuntu.com/ubuntu bionic-security InRelease [88.7 kB]
             ...
             Fetched 3360 kB in 2s (2018 kB/s)
             Reading package lists... Done
        
        
      4. Install the following two packages to a) support the creation of Let’s Encrypt certificates; and b) auto-configure the NGINX reverse proxy to use Let’s Encrypt certificates. The packages are pulled from the newly-created repository.

        sudo apt-get install certbot python-certbot-nginx
        

        Note

        This configures the reverse proxy to also act as a TLS Termination Proxy. Any HTTPS configuration is only found in the proxy container. By doing so, it is not necessary to perform any tasks inside the web server containers relating to certificates and Let’s Encrypt.

      5. Run certbot as root with the --nginx parameter in order to perform the auto-configuration of Let’s Encrypt for the first website. You will be asked to supply a valid email address for urgent renewal and security notices. You will then be asked to accept the Terms of Service and whether you would like to be contacted by the Electronic Frontier Foundation in the future. Next, you will provide the website for which you are activating HTTPS. Finally, you can choose to set up a facility that automatically redirects HTTP connections to HTTPS connections.

        sudo certbot --nginx
        
          
        Saving debug log to /var/log/letsencrypt/letsencrypt.log
        Plugins selected: Authenticator nginx, Installer nginx
        Enter email address (used for urgent renewal and security notices) (Enter 'c' to
        cancel): myemail@example.com
        
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Please read the Terms of Service at
        https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
        agree in order to register with the ACME server at
        https://acme-v02.api.letsencrypt.org/directory
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        (A)gree/(C)ancel: A
        
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Would you be willing to share your email address with the Electronic Frontier
        Foundation, a founding partner of the Let's Encrypt project and the non-profit
        organization that develops Certbot? We'd like to send you email about our work
        encrypting the web, EFF news, campaigns, and ways to support digital freedom.
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        (Y)es/(N)o: N
        
        Which names would you like to activate HTTPS for?
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        1: apache1.example.com
        2: nginx1.example.com
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Select the appropriate numbers separated by commas and/or spaces, or leave input
        blank to select all options shown (Enter 'c' to cancel): 1
        Obtaining a new certificate
        Performing the following challenges:
        http-01 challenge for apache1.example.com
        Waiting for verification...
        Cleaning up challenges
        Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/apache1.example.com
        
        Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        1: No redirect - Make no further changes to the webserver configuration.
        2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
        new sites, or if you're confident your site works on HTTPS. You can undo this
        change by editing your web server's configuration.
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
        Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/apache1.example.com
        
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Congratulations! You have successfully enabled https://apache1.example.com
        
        You should test your configuration at:
        https://www.ssllabs.com/ssltest/analyze.html?d=apache1.example.com
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        
        IMPORTANT NOTES:
         - Congratulations! Your certificate and chain have been saved at:
           /etc/letsencrypt/live/apache1.example.com/fullchain.pem
           Your key file has been saved at:
           /etc/letsencrypt/live/apache1.example.com/privkey.pem
           Your cert will expire on 2019-10-07. To obtain a new or tweaked
           version of this certificate in the future, simply run certbot again
           with the "certonly" option. To non-interactively renew *all* of
           your certificates, run "certbot renew"
         - Your account credentials have been saved in your Certbot
           configuration directory at /etc/letsencrypt. You should make a
           secure backup of this folder now. This configuration directory will
           also contain certificates and private keys obtained by Certbot so
           making regular backups of this folder is ideal.
         - If you like Certbot, please consider supporting our work by:
        
           Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
           Donating to EFF:                    https://eff.org/donate-le
        
        
      6. Run certbot as root with the --nginx parameter in order to perform the auto-configuration of Let’s Encrypt for the second website. This is the second time we run certbot, therefore we are asked directly to select the website to configure.

        sudo certbot --nginx
        
          
        Saving debug log to /var/log/letsencrypt/letsencrypt.log
        Plugins selected: Authenticator nginx, Installer nginx
        
        Which names would you like to activate HTTPS for?
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        1: apache1.example.com
        2: nginx1.example.com
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Select the appropriate numbers separated by commas and/or spaces, or leave input
        blank to select all options shown (Enter 'c' to cancel): 2
        Obtaining a new certificate
        Performing the following challenges:
        http-01 challenge for nginx1.example.com
        Waiting for verification...
        Cleaning up challenges
        Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/nginx1.example.com
        
        Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        1: No redirect - Make no further changes to the webserver configuration.
        2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
        new sites, or if you're confident your site works on HTTPS. You can undo this
        change by editing your web server's configuration.
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
        Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/nginx1.example.com
        
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Congratulations! You have successfully enabled https://nginx1.example.com
        
        You should test your configuration at:
        https://www.ssllabs.com/ssltest/analyze.html?d=nginx1.example.com
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        
        IMPORTANT NOTES:
         - Congratulations! Your certificate and chain have been saved at:
           /etc/letsencrypt/live/nginx1.example.com/fullchain.pem
           Your key file has been saved at:
           /etc/letsencrypt/live/nginx1.example.com/privkey.pem
           Your cert will expire on 2019-10-07. To obtain a new or tweaked
           version of this certificate in the future, simply run certbot again
           with the "certonly" option. To non-interactively renew *all* of
           your certificates, run "certbot renew"
         - If you like Certbot, please consider supporting our work by:
        
           Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
           Donating to EFF:                    https://eff.org/donate-le
        
        
      7. After adding all websites, perform a dry run in order to test the renewal of the certificates. Check that all websites are updating successfully to ensure the automated facility will update the certificates without further effort.

        sudo certbot renew --dry-run
        
          
        Saving debug log to /var/log/letsencrypt/letsencrypt.log
        
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Processing /etc/letsencrypt/renewal/apache1.example.com.conf
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Cert not due for renewal, but simulating renewal for dry run
        Plugins selected: Authenticator nginx, Installer nginx
        Renewing an existing certificate
        Performing the following challenges:
        http-01 challenge for apache1.example.com
        Waiting for verification...
        Cleaning up challenges
        
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        new certificate deployed with reload of nginx server; fullchain is
        /etc/letsencrypt/live/apache1.example.com/fullchain.pem
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Processing /etc/letsencrypt/renewal/nginx1.example.com.conf
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Cert not due for renewal, but simulating renewal for dry run
        Plugins selected: Authenticator nginx, Installer nginx
        Renewing an existing certificate
        Performing the following challenges:
        http-01 challenge for nginx1.example.com
        Waiting for verification...
        Cleaning up challenges
        
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        new certificate deployed with reload of nginx server; fullchain is
        /etc/letsencrypt/live/nginx1.example.com/fullchain.pem
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        ** DRY RUN: simulating 'certbot renew' close to cert expiry
        **          (The test certificates below have not been saved.)
        
        Congratulations, all renewals succeeded. The following certs have been renewed:
          /etc/letsencrypt/live/apache1.example.com/fullchain.pem (success)
          /etc/letsencrypt/live/nginx1.example.com/fullchain.pem (success)
        ** DRY RUN: simulating 'certbot renew' close to cert expiry
        **          (The test certificates above have not been saved.)
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        
        IMPORTANT NOTES:
         - Your account credentials have been saved in your Certbot
           configuration directory at /etc/letsencrypt. You should make a
           secure backup of this folder now. This configuration directory will
           also contain certificates and private keys obtained by Certbot so
           making regular backups of this folder is ideal.
        
        

        Note

        The certbot package adds a systemd timer in order to activate the automated renewal of Let’s Encrypt certificates. You can view the details of this timer by running systemctl list-timers.

      8. The certbot tool edits and changes the NGINX configuration files of your websites. In doing so, certbot does not obey initial listen directive (listen 80 proxy_protocol;) and does not add the proxy_protocol parameter to the newly added listen 443 ssl; lines. You must edit the configuration files for each website and append “proxy_protocol” to each “listen 443 ssl;” line.

        sudo nano /etc/nginx/sites-enabled/apache1.example.com
        sudo nano /etc/nginx/sites-enabled/nginx1.example.com
        
          
        listen 443 ssl proxy_protocol; # managed by Certbot
        listen [::]:443 ssl proxy_protocol; # managed by Certbot
        
        

        Note

        Each website configuration file has two pairs of listen directives: HTTP and HTTPS, respectively. The first is the original pair for HTTP that was added in a previous section. The second pair was added by certbot for HTTPS. These are pairs because they they cover both IPv4 and IPv6. The notation [::] refers to IPv6. When adding the parameter proxy_protocol, add it before the ; on each line as shown above.

      9. Restart NGINX.

        sudo systemctl restart nginx
        

      Troubleshooting

      Browser Error “SSL_ERROR_RX_RECORD_TOO_LONG”

      You have configured Certbot and created the appropriate Let’s Encrypt configuration for each website. But when you access the website from your browser, you get the following error.

        
      Secure Connection Failed
      
      An error occurred during a connection to apache1.example.com. SSL received a record that exceeded the maximum permissible length. Error code: SSL_ERROR_RX_RECORD_TOO_LONG
      
          The page you are trying to view cannot be shown because the authenticity of the received data could not be verified.
          Please contact the website owners to inform them of this problem.
      
      

      This error is caused when the NGINX reverse proxy in the proxy container does not have the proxy_protocol parameter in the listen 443 directives. Without the parameter, the reverse proxy does not consume the PROXY protocol information before it performs the HTTPS work. It mistakenly passes the PROXY protocol information to the HTTPS module, hence the record too long error.

      Follow the instructions in the previous section and add proxy_protocol to all listen 443 directives. Finally, restart NGINX.

      Error “Unable to connect” or “This site can’t be reached”

      When you attempt to connect to the website from your local computer and receive Unable to connect or This site can’t be reached errors, it is likely the proxy devices have not been configured.

      Run the following command on the host to verify whether LXD is listening and is able to accept connections to ports 80 (HTTP) and 443 (HTTPS).

      sudo ss -ltp '( sport = :http || sport = :https )'
      

      Note

      The ss command is similar to netstat and lsof. It shows information about network connections. In this case, we use it to verify whether there is a service on ports 80 and 443, and which service it is. * -l, to display the listening sockets, * -t, to display only TCP sockets, * -p, to show which processes use those sockets, * ( sport = :http || sport = :https ), to show only ports 80 and 443 (HTTP and HTTPS, respectively).

      In the following output we can verify that both ports 80 and 443 (HTTP and HTTPS, respectively) are in the LISTEN state. In the last column we verify that the process listening is lxd itself.

        
      State     Recv-Q  Send-Q   Local Address:Port   Peer Address:Port
      LISTEN    0       128                  *:http              *:*       users:(("lxd",pid=1301,fd=7),("lxd",pid=1301,fd=5))
      LISTEN    0       128                  *:https             *:*       users:(("lxd",pid=1349,fd=7),("lxd",pid=1349,fd=5))
      
      

      If you see a process listed other than lxd, stop that service and restart the proxy container. By restarting the proxy container, LXD will apply the proxy devices again.

      The Apache access.log Shows the IP Address of the Proxy Container

      You have set up the apache1 container and verified that it is accessible from the internet. But the logs at /var/log/apache2/access.log still show the private IP address of the proxy container, either the private IPv4 (10.x.x.x) or the private IPv6 addresses. What went wrong?

      The default log formats for printing access logs in Apache only print the IP address of the host of the last hop (i.e. the proxy server). This is the %h format specifier as shown below.

        
      LogFormat "%v:%p %h %l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i"" vhost_combined
      LogFormat "%h %l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i"" combined
      LogFormat "%h %l %u %t "%r" %>s %O" common
      
      

      The %h must be manually replaced with the %a format specifier, which prints the value as returned by the real RemoteIP Apache module.

        
      LogFormat "%v:%p %a %l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i"" vhost_combined
      LogFormat "%a %l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i"" combined
      LogFormat "%a %l %u %t "%r" %>s %O" common
      
      
      1. Run the following command in the apache1 container to edit the configuration file httpd.conf and perform the change from %h to %a.

        sudo nano /etc/apache2/apache2.conf
        
      2. Reload the Apache web server service.

        sudo systemctl reload apache2
        

      Next Steps

      You have set up a reverse proxy to host many websites on the same server and installed each website in a separate container. You can install static or dynamic websites in the containers. For dynamic websites, you may need additional configuration; check the respective documentation for setup using a reverse proxy. In addition, you may also use NGINX as a reverse proxy for non-HTTP(S) services.

      More Information

      You may wish to consult the following resources for additional information on this topic. While these are provided in the hope that they will be useful, please note that we cannot vouch for the accuracy or timeliness of externally hosted materials.

      Find answers, ask questions, and help others.

      This guide is published under a CC BY-ND 4.0 license.



      Source link