Using Ansible, Terraform, KVM, and Kubernetes to create an on-premises Kubernetes cluster

In this post, we use the server automation tools Ansible, Terraform, Docker, and Kubernetes to create and configure virtual machines (VMs) to host an on-premises Kubernetes cluster.

Understanding infrastructure

Infrastructure refers to computing resources used to store, transform, and exchange data. A new approach to software development called DevOps deploys applications across distributed systems consisting of multiple physical and virtual machines.

Understanding DevOps

DevOps is an approach to software development and system administration that views system administration as a task to be automated, so that software developers are not dependent on the services of a system administrator when they deploy software to server infrastructure.

DevOps tools automate the creation and deployment of servers to create a distributed software infrastructure on which software can be deployed and run on multiple computers whether physical or virtual.

Understanding the difference between cloud and on-premises (“onprem”) virtualization servers

The term “cloud” refers to computing services that are offsite, outsourced, and virtualized. These cloud services are provided by companies including Amazon AWS, Google GCP, Microsoft Azure, and Digital Ocean.

The term “On-premises” (“onprem”) refers to computing services that are onsite, in-house, and virtualized. Onprem virtualization servers can provide the same software environment as cloud providers. Onprem server infrastructure can be used to develop and test new software before it is deployed to a public cloud.

Overview of the system to be constructed

Understanding the virtualization server

Virtual machines require a physical machine containing processors, memory, and storage. For this exercise, we will reformat a circa 2015 laptop (i7-4712HQ, 16GB RAM, 1TB SSD) with Ubuntu Linux 22.04 LTS as an on-premises virtualization host.

Understanding KVM virtualization

KVM creates a hypervisor virtualization host on a Linux server. A KVM hypervisor can host multiple virtual machine (VM) guests.

Understanding Terraform

Terraform can run scripts that automate the creation of virtual machines, on public clouds such as Amazon AWS, Google GCP, and Digital Ocean, as well as on on-premises (“onprem”) virtualization hosts including KVM.

Understanding the libvirt provider software

The libvirt provider software enables Terraform to automate the creation of virtual machines on KVM hypervisor hosts.

Understanding Ansible

Ansible can run scripts that automate server administration tasks. In this exercise, multiple Ansible scripts will be executed to use Terraform to create virtual machine (VM) servers, on which software will be deployed and configured, creating a Kubernetes cluster.

Understanding virtual machine (VM) guests

A virtual machine (VM) guest is a server that emulates hardware as a software image, using a subset of the hypervisor host’s processor cores, memory, and storage to create a distinct computer environment, with its own software libraries, network address, and password or key entry system.

Understanding Docker software containers

Docker containers are software containers created by the docker-compose command. A Docker container has its own software libraries, network address, and password or key entry system, but comparisons between Docker containers and virtual machines (VMs) are discouraged.

Understanding Kubernetes

Kubernetes is software that allows you to create a high-availability cluster consisting of a control plane server and one or more worker servers. Kubernetes allows for software to be deployed as containers stored in pods, running on clusters, running on nodes.

Containers

Software is organized within Docker containers. A Docker container is a self-contained computing environment with its own libraries, IP address, and SSH password or key.

Pods

Pods are a unit of computing that contain one or more containers. Pods execute on clusters, which are intermediate interfaces that distribute computing tasks across control plane and worker nodes, running on virtual machine servers.

Clusters

A cluster is an addressable interface that allows for the execution of Kubernetes Pods across a distributed system of control plane and worker nodes.

Nodes

In Kubernetes, a node is the physical or virtual machine that hosts the control plane role or worker role in a distributed Kubernetes cluster. In this exercise the nodes will be hosted on KVM virtual machines (VMs).

Understanding the automation server

The automation server is a Linux server separate from the virtualization server. The automation server can be a physical or virtual machine.

Tip: avoid running operations like this from your baremetal desktop. These operations involve hosts files and SSH keys for server access, and should be isolated if possible. Consider creating a virtual machine for this role using a hypervisor such as KVM on Linux, VirtualBox on Windows, or Parallels on MacOS. Use Ubuntu Linux 22.04 LTS.

Entering commands as root

This procedure assumes that you are entering commands as root. Escalate to the root user:

sudo su

Preparing the virtualization server 1/3

The virtualization server should be a minimal build: do a fresh format of Ubuntu Linux 22.04 LTS.

Using a wired network connection

If possible, use a wired Ethernet connection for the network connection on the hypervisor. This simplifies advanced operations like iptables forwarding and makes possible the later use of macvtap adapters for connecting in the hypervisor host networking space.

Setting a static IP address

Set a static IP address for the network connection of the virtualization server. Reboot.

Installing software on the virtualization server

From a root shell on the virtualization server, enter the following command:

apt install ifconfig net-tools iptraf-ng openssh-server

Configuring the SSH server on the virtualization server

From a root shell on the virtualization server, enter the following commands:

cd /etc/ssh

Use the nano editor to create the following text file:

nano sshd_config

uncomment and replace the following line:

PermitRootLogin yes

Creating a root password

sudo su
passwd

Preparing the automation server 1/2

Configure a virtual machine on a different physical machine than the virtualization server. Use Ubuntu Linux 22.04 LTS.

Adding a macvtap or bridge mode network adapter

For KVM, add a macvtap network adapter to the automation server. For VirtualBox or Parallels, add a bridge mode network adapter. This will allow the automation server to access internal subnets on the virtualization server via an ip route command.

Installing software and downloading Ansible scripts on the automation server

From a root shell on the automation server, enter the following commands:

apt install ansible git openssh-server net-tools iptraf-ng
cd /root
mkdir tmpops
cd tmpops
git clone https://github.com/kubealex/libvirt-k8s-provisioner.git

Creating the Ansible hosts file

cd /etc
mkdir ansible
cd ansible

Use the nano editor to create the following text file (use the IP address of the virtualization server in your setup):

nano hosts

contents:

[vm_host]
192.168.56.60

Creating an SSH key pair

From a root shell on the automation server, enter the following command:

ssh-keygen -f /root/.ssh/id_rsa -q -N ""

When prompted for a passphrase, press Enter and provide a blank value.

Copying the SSH public key to the virtualization server

From a root shell on the automation server, enter the following commands (substitute the IP address of the virtualization server in your setup):

cd /root/.ssh
rsync -e ssh -raz id_rsa.pub root@192.168.56.60:/root/.ssh/authorized_keys

Preparing the virtualization server 2/3

Verifying the automation server’s public key on the virtualization server

From a root shell on the virtualization server, enter the following commands:

cd /root/.ssh
ls -la

Verify that the file authorized_keys is listed:

root@henderson:/home/desktop# cd /root/.ssh
root@henderson:~/.ssh# ls -la
total 20
drwx------ 2 root root 4096 Jul 29 05:51 .
drwx------ 11 root root 4096 Jul 29 06:25 ..
-rw-r--r-- 1 root root 565 Jul 28 08:18 authorized_keys
-rw------- 1 root root 978 Jul 29 05:50 known_hosts
-rw-r--r-- 1 root root 142 Jul 29 05:50 known_hosts.old

Testing that the automation server can connect to the virtualization server using a public SSH key

From the automation server, enter the folowing command (substitute the IP address of your virtualization server):

ssh root@192.168.56.60

Note: If you are able to login without supplying a password, you have succeeded.

Using Ansible to automate operations

Ansible can run scripts called playbooks to perform automated server administration tasks. Ansible playbook scripts will use Terraform to create and configure virtual machines (VMs) on which a Kubernetes cluster will be installed.

A note about the libvirt-k8s-provisioner project

The libvirt-k8s-provisioner project provides a set of scripts that use Ansible and Terraform to create virtual machines (VMs) and to deploy a Kubernetes cluster.

Modifying the libvirt-k8s-provisioner vars file

cd /root/tmpops/libvirt-k8s-provisioner/vars
nano k8s_cluster.yml
k8s:
  cluster_name: k8s-test
  cluster_os: Ubuntu
  cluster_version: 1.24
  container_runtime: crio
  master_schedulable: false

# Nodes configuration

  control_plane:
    vcpu: 2
    mem: 2 
    vms: 3
    disk: 30

  worker_nodes:
    vcpu: 2
    mem: 2
    vms: 1
    disk: 30

# Network configuration

  network:
    network_cidr: 192.168.200.0/24
    domain: k8s.test
    additional_san: ""
    pod_cidr: 10.20.0.0/16
    service_cidr: 10.110.0.0/16
    cni_plugin: cilium

rook_ceph:
  install_rook: true
  volume_size: 50
      rook_cluster_size: 1

# Ingress controller configuration [nginx/haproxy]

ingress_controller:
  install_ingress_controller: true
  type: haproxy
      node_port:
        http: 31080
        https: 31443    

# Section for metalLB setup

metallb:
  install_metallb: true
  l2:
    iprange: 192.168.200.210-192.168.200.250

Installing the collection requirements for Ansible operations

From a root shell on the virtualization server, enter the following commands:

cd /root/tmpops/libvirt-k8s-provisioner
ansible-galaxy collection install -r requirements.yml

Running the Ansible playbook to create and configure virtual machines on the virtualization host 1/2

From a root shell on the virtualization server, enter the following commands:

ansible-playbook main.yml

The task sequence will end with this error:

fatal: [k8s-test-worker-0.k8s.test]: FAILED! => {"changed": false, "elapsed": 600, "msg": "timed out waiting for ping module test: Failed to connect to the host via ssh: ssh: Could not resolve hostname k8s-test-worker-0.k8s.test: Name or service not known"}
fatal: [k8s-test-master-0.k8s.test]: FAILED! => {"changed": false, "elapsed": 600, "msg": "timed out waiting for ping module test: Failed to connect to the host via ssh: ssh: Could not resolve hostname k8s-test-master-0.k8s.test: Name or service not known"}

Note: we will recover from this error in a later step.

Preparing the virtualization server 3/3

From a root shell on the virtualization server, enter the following command:

virsh net-dhcp-leases k8s-test

Information about the virtual machines in the k8s-test network will be displayed:

root@henderson:/home/desktop# virsh net-dhcp-leases k8s-test
Expiry Time MAC address Protocol IP address Hostname Client ID or DUID
--------------------------------------------------------------------------------------------------------------------------------------------------------
2022-07-29 07:21:42 52:54:00:4a:20:99 ipv4 192.168.200.99/24 k8s-test-master-0 ff:b5:5e:67:ff:00:02:00:00:ab:11:28:1f:a1:fb:24:5c:f5:70
2022-07-29 07:21:42 52:54:00:86:29:8f ipv4 192.168.200.28/24 k8s-test-worker-0 ff:b5:5e:67:ff:00:02:00:00:ab:11:9e:22:e1:40:72:21:cf:9d

Take note of the IP addresses starting with 192.168.200, these values will be needed in a later configuration step.

Understanding the need for IP forwarding on the virtualization server

By default, virtual machines are created with IP addresses in the 192.168.200.x subnet. This subnet is accessible within the virtualization server.

In order to make the 192.168.200.x subnet accessible to the automation server, we need to create a gateway router using iptables directives on the virtualization server.

In a later step, we will add a default route for the 192.168.200.x subnet on the automation server, allowing it to resolve IP addresses in that subnet.

Enabling IP forwarding for the 192.168.200.x subnet

From a root shell on the virtualization server, enter the following commands:

Use the nano editor to create the following text file:

cd /etc
nano sysctl.conf

Add the following line to the end of the sysctl.conf file:

net.ipv4.ip_forward = 1

Enter this command:

sysctl -p

Use the nano editor to create the following text file (substitute the wanadaptername and wanadapterip for those of the virtualization server in your setup):

nano forward.sh

contents:

#!/usr/bin/bash
# values
kvmsubnet="192.168.200.0/24"
wanadaptername="eno1"
wanadapterip="192.168.56.60"
kvmadaptername="k8s-test"
kvmadapterip="192.168.200.1"
# allow virtual adapter to accept packets from outside the host
iptables -I FORWARD -i $wanadaptername -o $kvmadaptername -d $kvmsubnet -j ACCEPT
iptables -I FORWARD -i $kvmadapterip -o $wanadaptername -s $kvmsubnet -j ACCEPT
iptables --table nat --append POSTROUTING --out-interface eth1 -j MASQUERADE

Enter the following commands:

chmod 755 forward.sh
bash forward.sh

Note: add invocation to /etc/rc.local for persistence.

Preparing the automation server 2/2

Adding IP addresses for the VM hosts created in “Preparing the virtualization server 3/3”

From a root shell on the automation server, enter the following commands:

Use the nano editor to create the following text file:

cd /etc
nano sysctl.conf

Add the following line to the end of the sysctl.conf file:

net.ipv4.ip_forward = 1

Enter this command:

sysctl -p

Use the nano editor to modify the /etc/hosts file:

nano hosts

Add the following lines (substitute the IP addresses observed earlier in “Preparing the virtualization server 3/3”):

192.168.200.99 k8s-test-master-0.k8s.test
192.168.200.28 k8s-test-worker-0.k8s.test

Adding a route for for the 192.168.200.x subnet:

From a root shell on the automation server, enter the following command (substitute the wanadaptername (dev) and wanadapterip for those of the virtualization server in your setup):

ip route add 192.168.200.0/24 via 192.168.56.60 dev enp0s3

Note: add invocation to /etc/rc.local for persistence.

Testing the IP routing from the automation server to the 192.168.200.x subnet

Ping one of the IP addresses you observed in the preceding step “Preparing the virtualization server 3/3” (Substitute one of the IP addresses in your setup):

ping 192.168.200.99

Running the Ansible playbook to create and configure virtual machines on the virtualization host 2/2

From a root shell on the automation server, enter the following commands:

cd /root/tmpops/libvirt-k8s-provisioner
ansible-playbook main.yml

Verifying that the Ansible task sequence has completed without errors

Sample output:

PLAY RECAP *******************************************************************************************************************************************************************************************************************************
192.168.56.60 : ok=76 changed=24 unreachable=0 failed=0 skipped=32 rescued=0 ignored=0
k8s-test-master-0.k8s.test : ok=49 changed=28 unreachable=0 failed=0 skipped=28 rescued=0 ignored=0
k8s-test-worker-0.k8s.test : ok=38 changed=24 unreachable=0 failed=0 skipped=28 rescued=0 ignored=0

Making a test connection to a VM

From a root shell on the automation server, enter the following command (substitute the IP address of a VM in your setup):

ssh kube@192.168.200.99

When prompted, enter the password: “kuberocks”

Listing all container images running in the clusters

From a root shell on the control plane (obsolete term “master”) VM server, enter the following command:

kubectl get pods –all-namespaces

Output:

Pending 0 113m
rook-ceph rook-ceph-mds-ceph-filesystem-a-868694c95d-85r54 1/1 Running 0 113m
rook-ceph rook-ceph-mds-ceph-filesystem-b-748dc85c96-qktmb 1/1 Running 0 113m
rook-ceph rook-ceph-mgr-a-7f6784d748-cg9v8 1/1 Running 0 115m
rook-ceph rook-ceph-mon-a-6f9d4bc99b-fgc2g 1/1 Running 0 116m
rook-ceph rook-ceph-operator-f4ccf8fc-f5rcl 1/1 Running 0 119m
rook-ceph rook-ceph-osd-0-7b6fbf8657-lktsx 0/1 CrashLoopBackOff 27 (38s ago) 114m
rook-ceph rook-ceph-osd-prepare-k8s-test-worker-0.k8s.test-j79rg 0/1 Completed 0 114m
rook-ceph rook-ceph-rgw-ceph-objectstore-a-64b5fd4d9b-77krx 0/1 Running 20 (7m43s ago) 110m

Appendix: Full output from the Ansible playbook runs

root@austin:/root/tmpops/libvirt-k8s-provisioner# ansible-playbook main.yml
[WARNING]: Collection community.general does not support Ansible version 2.10.8

PLAY [Pre-flight checklist before installing k8s] ****************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Check if distribution is supported] ************************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Check if target distribution is correct] *******************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Check if Docker is selected on 1.24] ***********************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Check at least one vm for control plane is defined] ********************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Check if master is schedulable in case of clusters composed by control plane VMs] **************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Fail fast if Rook cluster size exceeds worker nodes.] ******************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Fail fast if no container runtime is defined] **************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Fail fast if no ingress controller is selected] ************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Fail fast if no container plugin selected] *****************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

PLAY [This play installs needed tools to provision infrastructure VMs] *******************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Upgrade all packages] **************************************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Install required packages] *********************************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Install required packages] *********************************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Upgrade all packages] **************************************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Install required packages] *********************************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Download and provision Terraform] **************************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Virtualization services are enabled] ***********************************************************************************************************************************************************************************************
ok: [192.168.56.60]

PLAY [This play ensures environment is set up for cluster creation] **********************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [ansible.builtin.set_fact] **********************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Ensure needed pip packages are present] ********************************************************************************************************************************************************************************************
ok: [192.168.56.60] => (item=PyYAML)
changed: [192.168.56.60] => (item=kubernetes)
changed: [192.168.56.60] => (item=jsonpatch)
ok: [192.168.56.60] => (item=netaddr)

TASK [Ensure workspace directory exists] *************************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Ensure cluster folder exists] ******************************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Ensure pivot tmp folder exists] ****************************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Populate cluster folder with terraform files] **************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Snapshot cluster configuration for further use] ************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Ensure helm is installed] **********************************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Install helm in PATH] **************************************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Install Helm Diff] *****************************************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [remove directory] ******************************************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Retrieve the minor version] ********************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Create ssh keypair] ****************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Copy SSH keys in working directory] ************************************************************************************************************************************************************************************************
changed: [192.168.56.60] => (item=id_rsa_k8s-test)
changed: [192.168.56.60] => (item=id_rsa_k8s-test.pub)

TASK [Getting ssh private key] ***********************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Getting ssh public key] ************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [ansible.builtin.set_fact] **********************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Download CentOS image] *************************************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Download Ubuntu image] *************************************************************************************************************************************************************************************************************
changed: [192.168.56.60]

PLAY [This play provisions libvirt resources with terraform] *****************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Use TF project to ensure pool and network are defined] *****************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Ensure security_driver is disabled] ************************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Restart libvirtd service] **********************************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Ensure systemd-resolved config dir is present] *************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Enable localdns if systemd-resolved is present] ************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Ensure NM configuration directory exists] ******************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Ensure NM dnsmasq directory exists] ************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Configure NetworkManager for local DNS] ********************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Configure NetworkManager for libvirt network] **************************************************************************************************************************************************************************************
changed: [192.168.56.60]

RUNNING HANDLER [Restart systemd-resolved] ***********************************************************************************************************************************************************************************************
changed: [192.168.56.60]

RUNNING HANDLER [Restart NetworkManager] *************************************************************************************************************************************************************************************************
changed: [192.168.56.60]

RUNNING HANDLER [Wait for local DNS resolver to be up] ***********************************************************************************************************************************************************************************
skipping: [192.168.56.60]

PLAY [This play provisions k8s VMs based on intial config] *******************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Ensure control plane VMs are in place] *********************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Ensure worker VMs are in place] ****************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Ensure Rook dedicated worker VMs are in place] *************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Add masters to given group] ********************************************************************************************************************************************************************************************************
ok: [192.168.56.60] => (item=0)

TASK [Add workers to given group] ********************************************************************************************************************************************************************************************************
ok: [192.168.56.60] => (item=0)

TASK [Ensure to clean known_hosts] *******************************************************************************************************************************************************************************************************
ok: [192.168.56.60] => (item=k8s-test-master-0.k8s.test)
ok: [192.168.56.60] => (item=k8s-test-worker-0.k8s.test)

PLAY [Check connection and set facts] ****************************************************************************************************************************************************************************************************

TASK [Wait 600 seconds for target connection to become reachable/usable] *****************************************************************************************************************************************************************
fatal: [k8s-test-worker-0.k8s.test]: FAILED! => {"changed": false, "elapsed": 600, "msg": "timed out waiting for ping module test: Failed to connect to the host via ssh: ssh: Could not resolve hostname k8s-test-worker-0.k8s.test: Name or service not known"}
fatal: [k8s-test-master-0.k8s.test]: FAILED! => {"changed": false, "elapsed": 600, "msg": "timed out waiting for ping module test: Failed to connect to the host via ssh: ssh: Could not resolve hostname k8s-test-master-0.k8s.test: Name or service not known"}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************
192.168.56.60              : ok=44   changed=26   unreachable=0    failed=0    skipped=14   rescued=0    ignored=0   
k8s-test-master-0.k8s.test : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   
k8s-test-worker-0.k8s.test : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

root@austin:/root/tmpops/libvirt-k8s-provisioner# ansible-playbook main.yml
[WARNING]: Collection community.general does not support Ansible version 2.10.8

PLAY [Pre-flight checklist before installing k8s] ****************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Check if distribution is supported] ************************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Check if target distribution is correct] *******************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Check if Docker is selected on 1.24] ***********************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Check at least one vm for control plane is defined] ********************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Check if master is schedulable in case of clusters composed by control plane VMs] **************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Fail fast if Rook cluster size exceeds worker nodes.] ******************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Fail fast if no container runtime is defined] **************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Fail fast if no ingress controller is selected] ************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Fail fast if no container plugin selected] *****************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

PLAY [This play installs needed tools to provision infrastructure VMs] *******************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Upgrade all packages] **************************************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Install required packages] *********************************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Install required packages] *********************************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Upgrade all packages] **************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Install required packages] *********************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Download and provision Terraform] **************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Virtualization services are enabled] ***********************************************************************************************************************************************************************************************
ok: [192.168.56.60]

PLAY [This play ensures environment is set up for cluster creation] **********************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [ansible.builtin.set_fact] **********************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Ensure needed pip packages are present] ********************************************************************************************************************************************************************************************
ok: [192.168.56.60] => (item=PyYAML)
ok: [192.168.56.60] => (item=kubernetes)
ok: [192.168.56.60] => (item=jsonpatch)
ok: [192.168.56.60] => (item=netaddr)

TASK [Ensure workspace directory exists] *************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Ensure cluster folder exists] ******************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Ensure pivot tmp folder exists] ****************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Populate cluster folder with terraform files] **************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Snapshot cluster configuration for further use] ************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Ensure helm is installed] **********************************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Install helm in PATH] **************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Install Helm Diff] *****************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [remove directory] ******************************************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Retrieve the minor version] ********************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Create ssh keypair] ****************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Copy SSH keys in working directory] ************************************************************************************************************************************************************************************************
ok: [192.168.56.60] => (item=id_rsa_k8s-test)
ok: [192.168.56.60] => (item=id_rsa_k8s-test.pub)

TASK [Getting ssh private key] ***********************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Getting ssh public key] ************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [ansible.builtin.set_fact] **********************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Download CentOS image] *************************************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Download Ubuntu image] *************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

PLAY [This play provisions libvirt resources with terraform] *****************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Use TF project to ensure pool and network are defined] *****************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Ensure security_driver is disabled] ************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Restart libvirtd service] **********************************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Ensure systemd-resolved config dir is present] *************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Enable localdns if systemd-resolved is present] ************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Ensure NM configuration directory exists] ******************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Ensure NM dnsmasq directory exists] ************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Configure NetworkManager for local DNS] ********************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Configure NetworkManager for libvirt network] **************************************************************************************************************************************************************************************
ok: [192.168.56.60]

PLAY [This play provisions k8s VMs based on intial config] *******************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Ensure control plane VMs are in place] *********************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Ensure worker VMs are in place] ****************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Ensure Rook dedicated worker VMs are in place] *************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Add masters to given group] ********************************************************************************************************************************************************************************************************
ok: [192.168.56.60] => (item=0)

TASK [Add workers to given group] ********************************************************************************************************************************************************************************************************
ok: [192.168.56.60] => (item=0)

TASK [Ensure to clean known_hosts] *******************************************************************************************************************************************************************************************************
ok: [192.168.56.60] => (item=k8s-test-master-0.k8s.test)
ok: [192.168.56.60] => (item=k8s-test-worker-0.k8s.test)

PLAY [Check connection and set facts] ****************************************************************************************************************************************************************************************************

TASK [Wait 600 seconds for target connection to become reachable/usable] *****************************************************************************************************************************************************************
ok: [k8s-test-master-0.k8s.test]
ok: [k8s-test-worker-0.k8s.test]

TASK [ansible.builtin.ping] **************************************************************************************************************************************************************************************************************
ok: [k8s-test-master-0.k8s.test]
ok: [k8s-test-worker-0.k8s.test]

TASK [Add hosts to correct groups] *******************************************************************************************************************************************************************************************************
changed: [k8s-test-master-0.k8s.test]
changed: [k8s-test-worker-0.k8s.test]

TASK [Extract facts from setup] **********************************************************************************************************************************************************************************************************
ok: [k8s-test-worker-0.k8s.test]
ok: [k8s-test-master-0.k8s.test]

TASK [ansible.builtin.set_fact] **********************************************************************************************************************************************************************************************************
ok: [k8s-test-master-0.k8s.test]
ok: [k8s-test-worker-0.k8s.test]

PLAY [This play provisions loadbalancer VM for control plane] ****************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Ensure Load Balancer VM is in place] ***********************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Add loadbalancer to inventory] *****************************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Ensure to clean known_hosts] *******************************************************************************************************************************************************************************************************
skipping: [192.168.56.60]
[WARNING]: Could not match supplied host pattern, ignoring: loadbalancer

PLAY [Check connection to loadbalancer] **************************************************************************************************************************************************************************************************
skipping: no hosts matched

PLAY [Loadbalancer configuration play] ***************************************************************************************************************************************************************************************************
skipping: no hosts matched

PLAY [Install container runtime] *********************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [k8s-test-master-0.k8s.test]
ok: [k8s-test-worker-0.k8s.test]

TASK [Retrieve the minor version] ********************************************************************************************************************************************************************************************************
ok: [k8s-test-master-0.k8s.test]
ok: [k8s-test-worker-0.k8s.test]

TASK [Upgrade all packages] **************************************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Upgrade all packages] **************************************************************************************************************************************************************************************************************
changed: [k8s-test-master-0.k8s.test]
changed: [k8s-test-worker-0.k8s.test]

TASK [Add modules to autostart] **********************************************************************************************************************************************************************************************************
changed: [k8s-test-worker-0.k8s.test]
changed: [k8s-test-master-0.k8s.test]

TASK [Enable br_netfilter] ***************************************************************************************************************************************************************************************************************
changed: [k8s-test-worker-0.k8s.test] => (item=br_netfilter)
changed: [k8s-test-master-0.k8s.test] => (item=br_netfilter)
changed: [k8s-test-master-0.k8s.test] => (item=overlay)
changed: [k8s-test-worker-0.k8s.test] => (item=overlay)

TASK [Enable sysctl values] **************************************************************************************************************************************************************************************************************
changed: [k8s-test-worker-0.k8s.test] => (item={'key': 'net.ipv4.ip_forward', 'value': 1})
changed: [k8s-test-master-0.k8s.test] => (item={'key': 'net.ipv4.ip_forward', 'value': 1})
changed: [k8s-test-master-0.k8s.test] => (item={'key': 'net.bridge.bridge-nf-call-ip6tables', 'value': 1})
changed: [k8s-test-worker-0.k8s.test] => (item={'key': 'net.bridge.bridge-nf-call-ip6tables', 'value': 1})
changed: [k8s-test-worker-0.k8s.test] => (item={'key': 'net.bridge.bridge-nf-call-iptables', 'value': 1})
changed: [k8s-test-master-0.k8s.test] => (item={'key': 'net.bridge.bridge-nf-call-iptables', 'value': 1})

TASK [Setup sysctl] **********************************************************************************************************************************************************************************************************************
changed: [k8s-test-master-0.k8s.test]
changed: [k8s-test-worker-0.k8s.test]

TASK [Ensure sysctl is restarted] ********************************************************************************************************************************************************************************************************
changed: [k8s-test-master-0.k8s.test]
changed: [k8s-test-worker-0.k8s.test]

TASK [Ensure required packages are present] **********************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Add docker repo key] ***************************************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Ensure the presence of apt-repo for docker] ****************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Ensure docker packages are installed] **********************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Ensure required packages are present] **********************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Add docker repository] *************************************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Ensure docker is installed] ********************************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Create /etc/docker] ****************************************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Copy docker configuration in /etc/docker] ******************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Create systemd folder for Docker service] ******************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Force systemd to reread configs] ***************************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Ensure docker is enabled and started] **********************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Add crio repo key] *****************************************************************************************************************************************************************************************************************
changed: [k8s-test-master-0.k8s.test] => (item={'key': 'https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_22.04/Release.key', 'keyring': '/etc/apt/trusted.gpg.d/libcontainers.gpg'})
changed: [k8s-test-worker-0.k8s.test] => (item={'key': 'https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_22.04/Release.key', 'keyring': '/etc/apt/trusted.gpg.d/libcontainers.gpg'})
changed: [k8s-test-master-0.k8s.test] => (item={'key': 'https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable:cri-o:/1.24/xUbuntu_22.04/Release.key', 'keyring': '/etc/apt/trusted.gpg.d/libcontainers-cri-o.gpg'})
changed: [k8s-test-worker-0.k8s.test] => (item={'key': 'https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable:cri-o:/1.24/xUbuntu_22.04/Release.key', 'keyring': '/etc/apt/trusted.gpg.d/libcontainers-cri-o.gpg'})

TASK [Ensure the presence of apt-repo for cri-o] *****************************************************************************************************************************************************************************************
changed: [k8s-test-worker-0.k8s.test] => (item={'repo': 'deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_22.04/ /', 'file': '/etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list'})
changed: [k8s-test-master-0.k8s.test] => (item={'repo': 'deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_22.04/ /', 'file': '/etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list'})
changed: [k8s-test-worker-0.k8s.test] => (item={'repo': 'deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/1.24/xUbuntu_22.04/ /', 'file': '/etc/apt/sources.list.d/devel:kubic:libcontainers:stable:cri-o:1.24.list'})
changed: [k8s-test-master-0.k8s.test] => (item={'repo': 'deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/1.24/xUbuntu_22.04/ /', 'file': '/etc/apt/sources.list.d/devel:kubic:libcontainers:stable:cri-o:1.24.list'})

TASK [Temporary fix for memory swap slices on 20.04] *************************************************************************************************************************************************************************************
changed: [k8s-test-master-0.k8s.test]
changed: [k8s-test-worker-0.k8s.test]

TASK [Add kubic CentOS repository] *******************************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test] => (item={'file': '/etc/yum.repos.d/devel:kubic:libcontainers:stable.repo', 'url': 'http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/CentOS_8_Stream/devel:kubic:libcontainers:stable.repo'}) 
skipping: [k8s-test-master-0.k8s.test] => (item={'file': '/etc/yum.repos.d/devel:kubic:libcontainers:stable:cri-o.repo', 'url': 'http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/1.24/CentOS_8_Stream/devel:kubic:libcontainers:stable:cri-o:1.24.repo'}) 
skipping: [k8s-test-worker-0.k8s.test] => (item={'file': '/etc/yum.repos.d/devel:kubic:libcontainers:stable.repo', 'url': 'http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/CentOS_8_Stream/devel:kubic:libcontainers:stable.repo'}) 
skipping: [k8s-test-worker-0.k8s.test] => (item={'file': '/etc/yum.repos.d/devel:kubic:libcontainers:stable:cri-o.repo', 'url': 'http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/1.24/CentOS_8_Stream/devel:kubic:libcontainers:stable:cri-o:1.24.repo'}) 

TASK [Ensure cri-o is installed - CentOS] ************************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Ensure cri-o is installed - Ubuntu] ************************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Ensure cri-o is installed - Ubuntu] ************************************************************************************************************************************************************************************************
changed: [k8s-test-master-0.k8s.test]
changed: [k8s-test-worker-0.k8s.test]

TASK [Fire crio-conf template] ***********************************************************************************************************************************************************************************************************
changed: [k8s-test-master-0.k8s.test]
changed: [k8s-test-worker-0.k8s.test]

TASK [Fire crio-conf template] ***********************************************************************************************************************************************************************************************************
changed: [k8s-test-master-0.k8s.test]
changed: [k8s-test-worker-0.k8s.test]

TASK [Remove example CNI configs] ********************************************************************************************************************************************************************************************************
changed: [k8s-test-master-0.k8s.test] => (item=100-crio-bridge.conf)
changed: [k8s-test-worker-0.k8s.test] => (item=100-crio-bridge.conf)
changed: [k8s-test-master-0.k8s.test] => (item=200-loopback.conf)
changed: [k8s-test-worker-0.k8s.test] => (item=200-loopback.conf)

TASK [Force systemd to reread configs] ***************************************************************************************************************************************************************************************************
ok: [k8s-test-worker-0.k8s.test]
ok: [k8s-test-master-0.k8s.test]

TASK [Ensure cri-o is enabled and started] ***********************************************************************************************************************************************************************************************
changed: [k8s-test-master-0.k8s.test]
changed: [k8s-test-worker-0.k8s.test]

TASK [Ensure required packages are present] **********************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Add containerd repository] *********************************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Ensure containerd is installed] ****************************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Ensure containerd is configured and installed on Ubuntu machine] *******************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Create /etc/containers] ************************************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Initialize config] *****************************************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Force systemd to reread configs] ***************************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Ensure containerd is enabled and started] ******************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Reboot nodes before proceeding] ****************************************************************************************************************************************************************************************************
changed: [k8s-test-worker-0.k8s.test]
changed: [k8s-test-master-0.k8s.test]

PLAY [Ensure kube packages are installed] ************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [k8s-test-worker-0.k8s.test]
ok: [k8s-test-master-0.k8s.test]

TASK [Add Kubernetes repository] *********************************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Ensure required packages for kubetools are installed] ******************************************************************************************************************************************************************************
changed: [k8s-test-master-0.k8s.test]
changed: [k8s-test-worker-0.k8s.test]

TASK [Add kube-repo key] *****************************************************************************************************************************************************************************************************************
changed: [k8s-test-master-0.k8s.test]
changed: [k8s-test-worker-0.k8s.test]

TASK [Ensure the presence of apt-repo for kubernetes packages] ***************************************************************************************************************************************************************************
changed: [k8s-test-master-0.k8s.test]
changed: [k8s-test-worker-0.k8s.test]

TASK [Ensure Kubernetes packages are installed] ******************************************************************************************************************************************************************************************
changed: [k8s-test-master-0.k8s.test]
changed: [k8s-test-worker-0.k8s.test]

TASK [Ensure kubelet, kubeadm, kubectl are on hold] **************************************************************************************************************************************************************************************
changed: [k8s-test-worker-0.k8s.test] => (item=kubectl)
changed: [k8s-test-master-0.k8s.test] => (item=kubectl)
changed: [k8s-test-master-0.k8s.test] => (item=kubeadm)
changed: [k8s-test-worker-0.k8s.test] => (item=kubeadm)
changed: [k8s-test-master-0.k8s.test] => (item=kubelet)
changed: [k8s-test-worker-0.k8s.test] => (item=kubelet)

TASK [Disable swap] **********************************************************************************************************************************************************************************************************************
changed: [k8s-test-master-0.k8s.test]
changed: [k8s-test-worker-0.k8s.test]

TASK [Remove swap entry from fstab] ******************************************************************************************************************************************************************************************************
ok: [k8s-test-master-0.k8s.test]
ok: [k8s-test-worker-0.k8s.test]

TASK [Disable SELinux] *******************************************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Install kubepackages] **************************************************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Install lvm2 for rook support and git for installing rook] *************************************************************************************************************************************************************************
skipping: [k8s-test-master-0.k8s.test]
skipping: [k8s-test-worker-0.k8s.test]

TASK [Install lvm2 for rook support and git for installing rook] *************************************************************************************************************************************************************************
ok: [k8s-test-master-0.k8s.test]
ok: [k8s-test-worker-0.k8s.test]

TASK [Enable kubelet] ********************************************************************************************************************************************************************************************************************
ok: [k8s-test-master-0.k8s.test]
ok: [k8s-test-worker-0.k8s.test]

PLAY [Prepare kubeadm-config for cluster setup] ******************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [k8s-test-master-0.k8s.test]

TASK [Generate cluster token] ************************************************************************************************************************************************************************************************************
changed: [k8s-test-master-0.k8s.test]

TASK [Set generated token as fact] *******************************************************************************************************************************************************************************************************
ok: [k8s-test-master-0.k8s.test]

TASK [Generate certificate key] **********************************************************************************************************************************************************************************************************
changed: [k8s-test-master-0.k8s.test]

TASK [Set generated token as fact] *******************************************************************************************************************************************************************************************************
ok: [k8s-test-master-0.k8s.test]

TASK [Add kubelet config for node] *******************************************************************************************************************************************************************************************************
changed: [k8s-test-master-0.k8s.test]

PLAY [Install cluster with kubeadm] ******************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [k8s-test-master-0.k8s.test]

TASK [Start kubeadm install] *************************************************************************************************************************************************************************************************************
changed: [k8s-test-master-0.k8s.test]

TASK [Get information on generated certificate] ******************************************************************************************************************************************************************************************
ok: [k8s-test-master-0.k8s.test]

TASK [Set fact for certificate hash] *****************************************************************************************************************************************************************************************************
ok: [k8s-test-master-0.k8s.test]

TASK [Create kube directory] *************************************************************************************************************************************************************************************************************
changed: [k8s-test-master-0.k8s.test]

TASK [Copy kubeconfig] *******************************************************************************************************************************************************************************************************************
changed: [k8s-test-master-0.k8s.test]

TASK [Fetch kubeconfig file] *************************************************************************************************************************************************************************************************************
ok: [k8s-test-master-0.k8s.test]

TASK [Set kubeconfig as fact] ************************************************************************************************************************************************************************************************************
ok: [k8s-test-master-0.k8s.test]

TASK [ansible.builtin.set_fact] **********************************************************************************************************************************************************************************************************
ok: [k8s-test-master-0.k8s.test]

PLAY [Verify cluster has been initialized] ***********************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Save kubeconfig as file] ***********************************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Wait for control-plane pods to be up and running] **********************************************************************************************************************************************************************************
skipping: [192.168.56.60]

PLAY [Play to join control plane nodes in the cluster] ***********************************************************************************************************************************************************************************
skipping: no hosts matched

PLAY [Play to join nodes in the cluster] *************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [k8s-test-worker-0.k8s.test]

TASK [Fire joinConfiguration template for worker nodes] **********************************************************************************************************************************************************************************
changed: [k8s-test-worker-0.k8s.test]

TASK [Join worker nodes in cluster] ******************************************************************************************************************************************************************************************************
changed: [k8s-test-worker-0.k8s.test]

TASK [Mark node as joined] ***************************************************************************************************************************************************************************************************************
ok: [k8s-test-worker-0.k8s.test]

PLAY [Apply network plugin] **************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Download Calico manifest.] *********************************************************************************************************************************************************************************************************
skipping: [192.168.56.60] => (item={'name': '/tmp/k8s-test/calico-operator.yaml', 'url': 'https://docs.projectcalico.org/manifests/tigera-operator.yaml'}) 
skipping: [192.168.56.60] => (item={'name': '/tmp/k8s-test/calico-crd.yaml', 'url': 'https://docs.projectcalico.org/manifests/custom-resources.yaml'}) 

TASK [Apply custom CIDR to calico installation manifest] *********************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Temporary fix for non ascii char in Calico CRD (https://github.com/projectcalico/api/pull/46)] *************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Apply calico manifests to the cluster.] ********************************************************************************************************************************************************************************************
skipping: [192.168.56.60] => (item=/tmp/k8s-test/calico-operator.yaml) 
skipping: [192.168.56.60] => (item=/tmp/k8s-test/calico-crd.yaml) 

TASK [Add helm chart repository for Cilium] **********************************************************************************************************************************************************************************************
changed: [192.168.56.60] => (item={'name': 'cilium', 'repo_url': 'https://helm.cilium.io/'})

TASK [Ensure Cilium helm chart is installed] *********************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Download flannel manifest] *********************************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Patch kube-flannel to use host-gw instead of vxlan] ********************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Apply flannel manifests to the cluster.] *******************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Wait for core-dns pods to be up and running] ***************************************************************************************************************************************************************************************
changed: [192.168.56.60]

PLAY [Complete cluster setup] ************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Prepare playbook for cluster deletion] *********************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Delete image file] *****************************************************************************************************************************************************************************************************************
changed: [192.168.56.60] => (item=/tmp/OS-GenericCloud.qcow2)

TASK [Label worker nodes] ****************************************************************************************************************************************************************************************************************
changed: [192.168.56.60] => (item=k8s-test-worker-0.k8s.test)

TASK [Remove taint from master nodes] ****************************************************************************************************************************************************************************************************
skipping: [192.168.56.60] => (item=k8s-test-master-0.k8s.test) 

PLAY [Save inventory to disk] ************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Gather facts from all servers] *****************************************************************************************************************************************************************************************************
ok: [192.168.56.60 -> k8s-test-master-0.k8s.test] => (item=k8s-test-master-0.k8s.test)
ok: [192.168.56.60 -> k8s-test-worker-0.k8s.test] => (item=k8s-test-worker-0.k8s.test)
ok: [192.168.56.60 -> 192.168.56.60] => (item=192.168.56.60)

TASK [Fire up inventory template] ********************************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [ansible.builtin.debug] *************************************************************************************************************************************************************************************************************
ok: [192.168.56.60] => {
    "msg": "Inventory is now saved as k8s-test-inventory-k8s, you can resume next steps by referencing it."
}

PLAY [Prepare cluster to install ingress controller] *************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Ensure the needed Namespaces exist.] ***********************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Add helm chart repository for Contour] *********************************************************************************************************************************************************************************************
skipping: [192.168.56.60] => (item={'name': 'bitnami', 'repo_url': 'https://charts.bitnami.com/bitnami'}) 

TASK [Ensure Contour helm chart is installed] ********************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Ensure the needed Namespaces exist.] ***********************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Add helm chart repository for haproxy] *********************************************************************************************************************************************************************************************
changed: [192.168.56.60] => (item={'name': 'haproxytech', 'repo_url': 'https://haproxytech.github.io/helm-charts'})

TASK [Ensure haproxy helm chart is installed] ********************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Ensure the needed Namespaces exist.] ***********************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

TASK [Add helm chart repository for Nginx] ***********************************************************************************************************************************************************************************************
skipping: [192.168.56.60] => (item={'name': 'ingress-nginx', 'repo_url': 'https://kubernetes.github.io/ingress-nginx'}) 

TASK [Ensure Nginx helm chart is installed] **********************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

PLAY [Refresh facts] *********************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [k8s-test-worker-0.k8s.test]
ok: [k8s-test-master-0.k8s.test]
ok: [192.168.56.60]

PLAY [Trigger new haproxy configuration] *************************************************************************************************************************************************************************************************
skipping: no hosts matched

PLAY [Save new haproxy configuration] ****************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Fire up new haproxy template] ******************************************************************************************************************************************************************************************************
skipping: [192.168.56.60]

PLAY [Prepare cluster to install rook] ***************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Ensure the needed Namespaces exist.] ***********************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Add helm chart repository for Rook] ************************************************************************************************************************************************************************************************
changed: [192.168.56.60] => (item={'name': 'rook-release', 'repo_url': 'https://charts.rook.io/release'})
ok: [192.168.56.60] => (item={'name': 'rook-release', 'repo_url': 'https://charts.rook.io/release'})

TASK [Ensure rook-operator helm chart is installed] **************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Trigger rook template] *************************************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Ensure rook-ceph-cluster helm chart is installed] **********************************************************************************************************************************************************************************
changed: [192.168.56.60]

PLAY [Prepare cluster to install metalLB] ************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Render template for L2 configuration] **********************************************************************************************************************************************************************************************
changed: [192.168.56.60]

TASK [Download metal-lb CR for later apply] **********************************************************************************************************************************************************************************************
changed: [192.168.56.60] => (item={'name': '/tmp/k8s-test/metallb-ns.yaml', 'url': 'https://raw.githubusercontent.com/metallb/metallb/v0.12.1/manifests/namespace.yaml'})
changed: [192.168.56.60] => (item={'name': '/tmp/k8s-test/metallb.yaml', 'url': 'https://raw.githubusercontent.com/metallb/metallb/v0.12.1/manifests/metallb.yaml'})

TASK [Ensure metal-lb namespace is present] **********************************************************************************************************************************************************************************************
changed: [192.168.56.60] => (item=/tmp/k8s-test/metallb-ns.yaml)

TASK [Ensure metal-lb is installed in your cluster] **************************************************************************************************************************************************************************************
changed: [192.168.56.60] => (item=/tmp/k8s-test/metallb.yaml)

TASK [Render template for L2 configuration] **********************************************************************************************************************************************************************************************
ok: [192.168.56.60]

TASK [Ensure metal-lb configmap is added in your cluster] ********************************************************************************************************************************************************************************
changed: [192.168.56.60]

PLAY RECAP *******************************************************************************************************************************************************************************************************************************
192.168.56.60              : ok=76   changed=24   unreachable=0    failed=0    skipped=32   rescued=0    ignored=0   
k8s-test-master-0.k8s.test : ok=49   changed=28   unreachable=0    failed=0    skipped=28   rescued=0    ignored=0   
k8s-test-worker-0.k8s.test : ok=38   changed=24   unreachable=0    failed=0    skipped=28   rescued=0    ignored=0   

References

https://serverfault.com/questions/627238/kvm-libvirt-how-to-configure-static-guest-ip-addresses-on-the-virtualisation-ho

https://www.digitalocean.com/community/tutorials/how-to-create-a-kubernetes-cluster-using-kubeadm-on-ubuntu-20-04

https://www.armosec.io/blog/setting-up-kubernetes-cluster/

https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/

https://www.cyberciti.biz/faq/linux-kvm-libvirt-dnsmasq-dhcp-static-ip-address-configuration-for-guest-os/

https://linuxconfig.org/how-to-install-kubernetes-on-ubuntu-22-04-jammy-jellyfish-linux

https://subok-tech.com/installing-kubernetes-using-ansible-on-ubuntu-20-04/

https://projectcalico.docs.tigera.io/getting-started/kubernetes/quickstart

https://projectcalico.docs.tigera.io/getting-started/kubernetes/hardway/

https://groups.google.com/g/ansible-project/c/YTl5g317SBg?pli=1

https://www.shellhacks.com/ansible-sudo-a-password-is-required/

https://fabianlee.org/2022/05/25/kvm-kubeadm-cluster-on-kvm-using-ansible/

https://fabianlee.org/2021/07/05/kvm-installing-terraform-and-the-libvirt-provider-for-local-kvm-resources/

https://thenewstack.io/install-ansible-on-ubuntu-server-to-automate-linux-server-deployments/

https://www.baeldung.com/linux/destination-source-routing

https://github.com/kubealex/libvirt-k8s-provisioner

Creating a network file share with Linux and Samba authenticating against Active Directory

In this procedure we create a network file share by integrating the open source program Samba running on Linux with Active Directory to authenticate access to the network file share.

Business case

A computer running Linux and Samba can create a network file share authenticating against a company’s Active Directory. This means that a Linux server and Samba network file share software can replace a Windows server for the network file share role in the enterprise, reducing software licensing costs and improving security and stability.

This procedure was tested on Ubuntu Linux 22.04 LTS

This procedure was tested on Ubuntu Linux 22.04 LTS

Understanding the test network

This procedure was tested on a network of 3 virtual machines, each running in bridge mode, on different hypervisor hosts.

sudburyWindows Server 2019 acting as Active Directory controller for the clarkcounty.gordonbuchan.com domain.
sandiegoUbuntu Linux 22.04LTS desktop joined to the clarkcounty.gordonbuchan.com domain, authenticating access to a network file share enabled by Samba and Winbind against the Active Directory controller for the domain clarkcounty.gordonbuchan.com on sudbury.
hamiltonWindows 10 Pro workstation joined to the clarkcounty.gordonbuchan.com domain.

Understanding Active Directory

Active Directory is commercial software developed by Microsoft that runs primarily on Windows Server. Active Directory can authenticate users and groups of users, and can control access to resources like network file shares and “Single Sign-On” (SSO) login to computers connected to the network.

Understanding Samba

Samba is open source free software that enables a Linux server to provide a network file share that can be accessed by Windows computers.

A note re Samba’s included Active Directory functionality

Samba itself is able to act as an Active Directory controller and can implement a subset of Active Directory’s features. This post assumes that you are authenticating against an Active Directory controller running on Windows Server.

Understanding Winbind

Winbind is software that enables Samba to integrate with Active Directory to authenticate access to a network file share.

Understanding System Security Services Daemon (SSSD)

SSSD is a technology that enables Active Directory integration for Linux workstations. In practice, it is difficult to integrate SSSD with Samba for Active Directory authentication in a stable fashion. There are some approaches to SSSD which incorporate Winbind for a hybrid approach. This procedure will focus on using Winbind, and without using SSSD.

Choosing Winbind over SSSD for a network file share authenticaticated against Active Directory

This procedure will use Winbind to enable Samba to integrate with Active Directory to create a network file share authenticated against Active Directory.

Objectives

  • Access to the network file share authenticated against Active Directory.
  • The network file share must be accessible to workstations with “Enable insecure guest logins” set to “Disabled.”
  • The network file share must observe ACL and allow overrides by Windows clients for ownership and permissions.

(Single-Sign-On (SSO) and SSSD will be addressed in a later procedure.)

Creating the Active Directory group example_group and adding members to the group

Entering commands as root

This procedure assumes that you are logged in as the root user of the Linux server.

Escalate to the root user:

sudo su

Replacing the example realm/domain name with your realm/domain name

Please replace the sample realm/domain name clarkcounty.gordonbuchan.com with your realm/domain name.

Setting the system hostname

hostnamectl set-hostname sandiego.clarkcounty.gordonbuchan.com

Configuring the /etc/hosts file

Associate the host name of your Linux server with its IP address:

cd /etc
nano hosts
192.168.33.110   sandiego
192.168.33.110   sandiego.clarkcounty.gordonbuchan.com

Setting DNS

Disable systemd-resolved service:

systemctl stop systemd-resolved
systemctl disable systemd-resolved

Unlink the symbolic link /etc/resolv.conf:

cd /etc
unlink resolv.conf

Creating a new /etc/resolv.conf file

Ensure that the first nameserver entry is the IP address of the Active Directory server.

nano resolv.conf
nameserver 192.168.33.80
nameserver 8.8.8.8
search clarkcounty.gordonbuchan.com
reboot

Installing software

apt install acl samba winbind libnss-winbind krb5-user

Note: for the files /etc/krb5.conf and /etc/samba/smb.conf, the realm/domain name must be in UPPERCASE letters

The realm/domain name must be in UPPERCASE letters. This includes the long version CLARKCOUNTY.GORDONBUCHAN.COM and short version CLARKCOUNTY of the realm/domain name.

Configuring Kerberos

cd /etc
cp krb5.conf krb5.conf.orig
nano krb5.conf
[libdefaults]
default_realm = CLARKCOUNTY.GORDONBUCHAN.COM
dns_lookup_realm = false
dns_lookup_kdc = true

Configuring Nsswitch

cd /etc
cp nsswitch.conf nsswitch.conf.orig
nano nsswitch.conf
passwd: files winbind
group: files winbind
hosts: files dns wins
chmod 550 nsswitch.conf

Configuring Samba (1/2)

cd /etc/samba
cp smb.conf smb.conf.orig
nano smb.conf
[global]
   realm = CLARKCOUNTY.GORDONBUCHAN.COM
   security = ADS
   workgroup = CLARKCOUNTY

   idmap config SAMDOM : range = 10000 - 999999
   idmap config SAMDOM : backend = rid
   idmap config * : range = 3000-7999
   idmap config * : backend = tdb

   map acl inherit = Yes
   vfs objects = acl_xattr

   dedicated keytab file = /etc/krb5.keytab
   kerberos method = secrets and keytab
   winbind refresh tickets = Yes

Obtaining a Kerberos ticket

kinit admingordon
klist

Joining the Active Directory domain

net ads info testjoin
net ads -v join -U admingordon
net ads info

Restarting services (1/2)

systemctl restart smbd nmbd winbind

Creating the share folder

cd /home
mkdir example_share
chmod -R 2770 example_share
chown -R "CLARKCOUNTY\admingordon":"CLARKCOUNTY\example_group" example_share

Configuring Samba (2/2)

cd /etc/samba
cp smb.conf smb.conf.orig
nano smb.conf
   [Share]
   acl_xattr:ignore system acl = Yes
   acl allow execute always = Yes
   acl group control = Yes
   inherit acls = Yes
   inherit owner = windows and unix
   inherit permissions = Yes
   path = /media/share
   read only = No

Restarting services (2/2)

systemctl restart smbd nmbd winbind

Mapping sid==5-1-5-32-546 to nogroup

This SID must be mapped to the UNIX group nogroup:

net groupmap add sid=S-1-5-32-546 unixgroup=nogroup type=builtin

Connecting to the network file share from a Windows computer joined to the domain

Applying Windows Access Control Lists (ACLs) from a Windows computer joined to the domain

References

http://blog.jrg.com.br/2021/02/01/ubuntu-focal-fossa-samba-domain-member-shares-1/
https://docs.vmware.com/en/VMware-Horizon-7/7.13/linux-desktops-setup/GUID-F8F0CFCF-C4D6-4784-85FF-E7C6DF575F49.html
https://ubuntu.com/server/docs/service-sssd-ad
https://wiki.samba.org/index.php/Setting_up_Samba_as_a_Domain_Member
https://www.jurisic.org/post/2021/11/24/SAMBA-Domain-Member-as-File-Server
https://www.moderndeployment.com/windows-server-2019-active-directory-installation-beginners-guide/
https://www.reddit.com/r/Ubuntu/comments/h01i2w/cheat_sheet_on_how_to_configure_a_smb_file_server/

Web presence step by step Chapter 18: Installing VirtualBox on a computer running Windows to host Linux as a virtual machine (VM) guest

Previous step Chapter 17: Using subdomains to host multiple websites under a single domain name

Web presence step by step is a series of posts that show you to how to build a web presence.

This post describes how to install Linux as a virtual machine (VM) guest on a Windows computer, by installing a program called VirtualBox.

Installing Linux as a VM guest allows you to explore Linux as a desktop or server operating system from an existing Windows computer, without reformatting.

VirtualBox is a hypervisor

The software that enables a computer to host a virtual machine as a guest is called a hypervisor. The hypervisor we will install in this blog post is called VirtualBox, an open source application available free of charge.

This image has an empty alt attribute; its file name is image-26.png

Using a virtual machine (VM) guest as a web, database, or file sharing server for local development and testing

A VM guest performs well in server roles likes web, database, and file sharing. VMs can be used for offline development and testing of websites and web-based applications, which are later deployed to public-facing web servers.

A VM guest can be used to deploy an internal or public-facing server with an application such as WordPress or Nextcloud.

Should you use a VirtualBox VM guest server for production?

Probably not. VirtualBox is great for testing, experiments, learning. However, if you are going into production, to run an essential task for a business, you should consider a more formal deployment using a platform like Linux KVM or Windows Server Hyper-V for an internal deployment, or to a public cloud like Digital Ocean or AWS for a public-facing deployment.

Who should consider using a VirtualBox VM guest server?

VirtualBox enables people to experiment with Linux, without making a commitment to modifying their existing Windows computer.

VirtualBox is great for students — anyone learning software development can host their own fully-functioning Linux server, allowing them to build prototype servers, containers, and software development environments.

Using a VM guest as a graphical user interface (GUI) desktop

A VM guest can function as a graphical desktop, allowing you to use the guest operating system in a sized window, or in full-screen mode. For example, you can connect to another network via VPN from a VM guest, while the host computer running Windows remains connected to its original network.

Limitations of a VM guest GUI desktop

VMs have some limitations: they are not well-suited to GPU-intensive tasks like video editing software or gaming, for example. YouTube video will be choppy.

Downloading VirtualBox

https://www.virtualbox.org/wiki/Downloads

Installing VirtualBox

Downloading Ubuntu desktop Linux

https://ubuntu.com/download/desktop

Installing Ubuntu desktop Linux as a guest VM under VirtualBox

Adjusting resolution

Adjusting window size

Switching to full screen mode

Enabling audio

Network considerations at the hypervisor level

Understanding communications between the host and the VM guest

The VM guest running under VirtualBox is a distinct and separate computer from the machine that hosts it. VirtualBox enables network communication between the VM guest and the Internet, via the network connection of the host.

VirtualBox also enables network communication between the VM guest and the host. For example, if you have a web server running on your VM guest, you can connect to it from your host’s desktop using a web browser or a terminal program like KiTTY for SSH.

Understanding the difference between network address translation (NAT) mode vs bridge mode

Network address translation (NAT) mode

By default, a VM guest is created with a network adapter configured to connect to the Internet via the Internet connection of its host.

A host can communicate with a VM guest via a NAT mode network connection

The host and the VM guest are able to communicate with each other. Computers other than the host cannot see or initiate a network connection with the VM guest.

Creating a NAT network to permit communications between the host and the VM guest

Making a VM guest visible and accessible to other computers on the network to which the host is connected

A VM guest can be made visible and accessible to other computers on the network via a bridge mode network connection, or via port forwarding.

Bridge mode

The network adapter for a VM guest can be configured in “bridge mode” which permits the VM to connect directly to the same network as the host. In this mode, a VM guest can request an IP address or use an IP address in the same subnet as the host and other computers connected to the same network.

A host cannot communicate with a VM guest via a bridge mode network connection

Due to the way that a bridge mode connection is configured to use the network connection of the host, the link permits communication between the VM guest and the outside world, but not between the VM guest and the host itself.

Adding an additional network adapter

(Note: the VM must be powered down in order to add a network adapter.)

Understanding port forwarding

VirtualBox will offer to open a hole on the host’s firewall. Click on “Allow access”:

Network considerations at the VM guest level

Allowing a port exception in the firewall

Setting a static IP address

Installing and starting the SSH terminal server

Enabling remote desktop access with x11vnc

To enable remote desktop access, please refer to this post: Installing x11vnc to replace broken screen sharing on Ubuntu 21.04.

Previous step Chapter 17: Using subdomains to host multiple websites under a single domain name

Installing x11vnc to replace broken screen sharing on Ubuntu 21.04

After upgrading from Ubuntu 20.10 to Ubuntu 21.04, screen sharing (VNC server) is no longer functioning correctly.

Update 2023/11/02: on Ubuntu 22.04, in some situations, the x11vnc server will start on port 5901/tcp, rather than the default 5900/tcp, even if legacy VNC support is disabled.

x11vnc is an effective replacement for Vino and gnome-remote-desktop

This post describes how to install x11vnc, and describes how to create a script that runs at Gnome desktop login that invokes x11vnc with the necessary command line options.

Vino was replaced by gnome-remote-desktop, but gnome-remote-desktop does not function correctly in Ubuntu 21.04

Vino, the VNC server previously used by Gnome, has been deprecated. Vino has been replaced by gnome-remote-desktop, but as currently integrated, gnome-remote-desktop does not function correctly in Ubuntu 21.04.

When configured using the standard Gnome control panel settings (Settings, Sharing, Screen Sharing):

This is the error that appears when trying to connect, in both xorg and wayland:

This is a good reminder of why one should use an LTS release for production servers

A good suggested practice is to run LTS releases on production servers, but run recent releases on workstation laptops. This allows for issues to be identified and workarounds devised before the next LTS release, in case the problem persists in the next LTS release.

Why would one want to access the Graphical User Interface (GUI) desktop of a Linux server?

For some server tasks, it is helpful to be able to access the GUI desktop of the server via a remote viewer. For example, you may need to install a GUI operating system as a guest, and access its GUI desktop from the console of your server’s GUI desktop.

Disabling Wayland

Wayland is an alternative to the xorg windows system. One day, it will be terrific. For now, it does not work with x11vnc or other important applications like TeamViewer.

sudo su
cd /etc/gdm3
nano custom.conf
# Uncomment the line below to force the login screen to use Xorg
WaylandEnable=false
reboot

A reminder about the firewall and opening port 5900/tcp

From a shell window, enter the following commands:

sudo su
ufw allow 5900/tcp
ufw allow 5901/tcp
exit

Disabling the existing (broken) screen sharing server

Settings, Sharing, Screen Sharing:

Installing the x11vnc package

From a shell window, enter the following commands:

sudo su
apt install x11vnc
exit

Creating a password for x11vnc

From a shell as the user that owns the Gnome desktop session, enter these commands. When prompted, supply a password:

whoami
x11vnc -storepasswd

A friendly warning about security

Different situations can accept different levels of risk. The VNC protocol as implemented, sends data as cleartext over a network connection. This may be acceptable over a local area network, particularly if you have VLAN segmentation and good wifi encryption enabled on your house Local Area Network (LAN).

Do not even consider sending this kind of unencrypted traffic over the public Internet. Use a VPN, or redirect the connection via SSH tunnelling.

Starting the server manually

From a shell as the user that owns the Gnome desktop session, enter these commands. When prompted, supply a password:

whoami
x11vnc -display :0 -forever -shared -rfbauth /home/desktop/.vnc/passwd

Testing the x11vnc screen sharing server

Determining the IP address of your computer

From a shell window, enter the following commands:

sudo su
apt install net-tools
ifconfig
exit

(Note: the command:

ip a

provides an equivalent result. But ifconfig is easier to read.)

Connecting to the server from a client

From another computer on the same local area network, connect to the IP address of the machine on which x11vnc is running. Attempt to connect using a VNC client such as RealVNC, tightvnc, or remmina:

Creating a script that contains the x11vnc command line options

From a shell as the user that owns the Gnome desktop session, enter these commands:

cd ~
whoami
nano x11vncstartup.sh

Enter this text. Press Control-X to save and exit:

#!/usr/bin/bash
x11vnc -display :0 -forever -shared -rfbauth /home/desktop/.vnc/passwd

Enter this command:

chmod 755 x11vncstartup.sh

Testing the script manually

From a shell as the user that owns the Gnome desktop session, enter these commands:

cd ~
./x11vncstartup.sh

Test as described earlier in the section “Testing the x11vnc screen sharing server.”

Adding the script to Startup Applications

Go to Show Applications. search for “startup applications,” click on its icon:

Click Add:

Name: x11vncstartup.sh

Click the “Browse…” button. Select the file x11vncstartup.sh, Click “Open”:

Comment: x11vncstartup.sh

Click on “Add”:

Click on “Close”:

Testing that x11vnc was started as a script in “Startup Applications”

Reboot the computer. Log into the computer’s desktop.

Test as described earlier in the section “Testing the x11vnc screen sharing server.”

Web presence step by step Chapter 17: Using subdomains to host multiple websites under a single domain name

Previous step Chapter 16: Using a script to automate the creation of a virtual host on an Apache web server
Next step: Chapter 18: Installing VirtualBox on a computer running Windows to host Linux as a virtual machine (VM) guest

Web presence step by step is a series of posts that show you to how to build a web presence.

In this post, we learn how to declare multiple subdomain names under a single domain name for use as virtual host names, for multiple separate websites.

Understanding virtual hosts and their relation to domain and subdomain names

A shared LAMP web server can host multiple websites, or “virtual hosts,” named after domain and subdomain names.

You may wish to host a web-based application like MyBB or Nextcloud as its own website, rather than as a subdirectory of an existing website, without purchasing an additional domain name.

Multiple subdomains can be hosted on the same web server, or on different web servers.

The bare domain and the www subdomain

A virtual host can be identified by a “bare” domain name like “webpresencestepbystep.com,” or by a subdomain name, like “www.webpresencestepbystep.com” — by convention, the www subdomain prefix points to the same content as the “bare” domain name.

Additional subdomains like “community” and “media”

Additional subdomain names can be declared like “community.webpresencestepbystep.com” and “media.webpresencestepbystep.com” – these subdomains can point to separate websites on the same web server, or on different web servers.

Understanding domain and subdomain names and their relation to Domain Name System (DNS) zone files

As we can see in the DNS zone for the domain, the host names “@” (“bare domain”) and “community” are A declarations associated with the IP address of web server A.

The host name “www” is a CNAME declaration associated with the host name “@” so implicitly is associated with the IP address of web server A.

The host name “media” is an A declaration associated with the IP address of web server B.

2 websites on web server A, 1 website on web server B, all as subdomains of a single domain name

By using multiple subdomains of the same domain name, 3 separate websites can be declared, with 2 websites hosted on web server A, and 1 website hosted on web server B, without the need to purchase additional domain names.

Bare domain and subdomain www on web server A

webpresencestepbystep.com and www.webpresencestepbystep.com on web server A

A note about www and CNAME

the subdomain “www” host name is a canononical name (“CNAME”) of the @ host name, which identifies the “bare” domain. This means that www.webpresencestepbystep.com will resolve to the same IP address as webpresencestepbystep.com

Virtual host profiles

/etc/apache2/sites-available/webpresencestepbystep.com.conf:

# generated 2021/05/18 19:42:53 EDT by addvhost.php
<VirtualHost *:80>
<IfModule mpm_itk_module>
AssignUserID webpresencestepbystep_com webpresencestepbystep_com
</IfModule>
ServerName webpresencestepbystep.com
ServerAlias www.webpresencestepbystep.com
DocumentRoot /usr/web/webpresencestepbystep_com/webpresencestepbystep.com
ServerAdmin info@yourdomain.com
CustomLog /var/log/apache2/webpresencestepbystep.com-access_log combined
ErrorLog /var/log/apache2/webpresencestepbystep.com-error_log
RewriteEngine on
RewriteCond %{SERVER_NAME} =webpresencestepbystep.com [OR]
RewriteCond %{SERVER_NAME} =www.webpresencestepbystep.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>

/etc/apache2/sites-available/webpresencestepbystep.com-le-ssl.conf:

<IfModule mod_ssl.c>
<VirtualHost *:443>
<IfModule mpm_itk_module>
AssignUserID webpresencestepbystep_com webpresencestepbystep_com </IfModule>
ServerName webpresencestepbystep.com
ServerAlias www.webpresencestepbystep.com
DocumentRoot /usr/web/webpresencestepbystep_com/webpresencestepbystep.com ServerAdmin info@yourdomain.com CustomLog /var/log/apache2/webpresencestepbystep.com-access_log combined ErrorLog /var/log/apache2/webpresencestepbystep.com-error_log Include /etc/letsencrypt/options-ssl-apache.conf SSLCertificateFile /etc/letsencrypt/live/linuxstepbystep.com/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/linuxstepbystep.com/privkey.pem
</VirtualHost>
</IfModule> 

Website

Subdomain community on web server A

community.webpresencestepbystep.com on the same IP address, on web server A

Virtual host profiles

/etc/apache2/sites-available/community.webpresencestepbystep.com.conf:

# generated 2021/05/29 12:45:14 EDT by addvhost.php
<VirtualHost *:80>
<IfModule mpm_itk_module>
AssignUserID community_webpresencestepbystep_ community_webpresencestepbystep_
</IfModule>
ServerName community.webpresencestepbystep.com
DocumentRoot /usr/web/community_webpresencestepbystep_/community.webpresencestepbystep.com
ServerAdmin info@yourdomain.com
CustomLog /var/log/apache2/community.webpresencestepbystep.com-access_log combined
ErrorLog /var/log/apache2/community.webpresencestepbystep.com-error_log
RewriteEngine on
RewriteCond %{SERVER_NAME} =community.webpresencestepbystep.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>

/etc/apache2/sites-available/community.webpresencestepbystep.com-le-ssl.conf

<IfModule mod_ssl.c>
<VirtualHost *:443>
<IfModule mpm_itk_module>
AssignUserID community_webpresencestepbystep_ community_webpresencestepbystep_
</IfModule>
ServerName community.webpresencestepbystep.com
DocumentRoot /usr/web/community_webpresencestepbystep_/community.webpresencestepbystep.com
ServerAdmin info@yourdomain.com
CustomLog /var/log/apache2/community.webpresencestepbystep.com-access_log combined
ErrorLog /var/log/apache2/community.webpresencestepbystep.com-error_log
SSLCertificateFile /etc/letsencrypt/live/linuxstepbystep.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/linuxstepbystep.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
</IfModule>

Website

Subdomain media on web server B

media.webpresencestepbystep.com on a different IP address, on web server B

Virtual host profiles

/etc/apache2/sites-available/media.webpresencestepbystep.com.conf:

# generated 2021/05/29 17:12:33 UTC by addvhost.php
<VirtualHost *:80>
<IfModule mpm_itk_module>
AssignUserID media_webpresencestepbystep_com media_webpresencestepbystep_com
</IfModule>
ServerName media.webpresencestepbystep.com
DocumentRoot /usr/web/media_webpresencestepbystep_com/media.webpresencestepbystep.com
ServerAdmin info@yourdomain.com
CustomLog /var/log/apache2/media.webpresencestepbystep.com-access_log combined
ErrorLog /var/log/apache2/media.webpresencestepbystep.com-error_log
RewriteEngine on
RewriteCond %{SERVER_NAME} =media.webpresencestepbystep.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>

/etc/apache2/sites-available/media.webpresencestepbystep.com-le-ssl.conf

<IfModule mod_ssl.c>
<VirtualHost *:443>
<IfModule mpm_itk_module>
AssignUserID media_webpresencestepbystep_com media_webpresencestepbystep_com
</IfModule>
ServerName media.webpresencestepbystep.com
DocumentRoot /usr/web/media_webpresencestepbystep_com/media.webpresencestepbystep.com
ServerAdmin info@yourdomain.com
CustomLog /var/log/apache2/media.webpresencestepbystep.com-access_log combined
ErrorLog /var/log/apache2/media.webpresencestepbystep.com-error_log
SSLCertificateFile /etc/letsencrypt/live/media.webpresencestepbystep.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/media.webpresencestepbystep.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
</IfModule>

Website

Previous step Chapter 16: Using a script to automate the creation of a virtual host on an Apache web server
Next step: Chapter 18: Installing VirtualBox on a computer running Windows to host Linux as a virtual machine (VM) guest

Web presence step by step Chapter 16: Using a script to automate the creation of a virtual host on an Apache web server

Previous step: Chapter 15: Using dwservice.net to provide remote technical support as an alternative to TeamViewer
Next step: Chapter 17: Using subdomains to host multiple websites under a single domain name

Web presence step by step is a series of posts that show you to how to build a web presence.

In this chapter we install and use a script to automate the creation of a virtual host on an Apache web server.

A PHP script that automates the creation of a virtual host under Apache

This script collects and validates inputs, then executes the commands to create a virtual host under Apache.

A note about the source code view below

For formatting reasons, the text is limited to a fixed width. To fully view the text, you can scroll to the right to see the ends of lines, or use the print view for this blog post.

To view the source code in an another text editor, download and uncompress the zip file described below, or select and copy the text from the source code example below, and paste the text into a file on your computer called “addvhost.php”

Consider copying the file to your Apache web server’s /usr/bin directory with a chmod of 755 so that it can be executed from the system path. Steps to do so are included in the procedure below.

Saving the PHP script to a file called addvhost.php

Download this zip file:

https://blog.gordonbuchan.com/files/addvhost.zip

Uncompress the zip file to extract the file “addvhost.php” then copy the file to your Apache web server.

Source code of the script

Scroll right to see the ends of lines.

#!/usr/bin/php
<?PHP
// addvhost.php
// v0102
// updated to variable-ize vhostip as a base setting
// creates a virtual host under Apache
// Gordon Buchan 20210512 https://gordonbuchan.com
// MIT License https://mit-license.org
// tested on Ubuntu 20.04, may work on Debian
// directory structure allows for chroot jails for SFTP:
// in a jail you do not own your home directory, only your webdir
// tip: apt install finger whois
// ////////////////////////////////////////////////////////////////
// start summary
// initialize base settings in variables ie bvhwb
// ask for vhostsubdomain, vhostusername, vhostpassword
// infer vhosthomedir, vhostwebdir by convention
// create user, home directory, password
// create directory
// create index.php document
// chown vhosthomedir as root:root
// chown vhostwebdir as vhostusername:vhostusername
// chmod vhostwebdir
// create virtual host file
// enable virtual host
// echo suggestion that client restart apache, run certbot --apache, restart apache
// end summary

// ////////////////////////////////////////////////////////////////
// start base settings

$bvhostconfdir = "/etc/apache2/sites-available";
$bvhwb = "/usr/web";
$restartcommandstr = "systemctl apache2 restart";
$vhostenablecommandstr = "a2enmod";
$echoplaintextpasswords = TRUE;
$logplaintextpassword = TRUE;
$vhostserveradmin = "info@yourdomain.com";
// tip: could be "xxx.xxx.xxx.xxx"
$vhostip = "*";

// ////////////////////////////////////////////////////////////////
// end base settings

// ////////////////////////////////////////////////////////////////
// start function sink
 
// start polyfill
// str_contains() polyfill for pre PHP8: code for this function taken from php.net
if (!function_exists('str_contains')) {
	function str_contains(string $haystack, string $needle): bool 
	{
	return '' === $needle || false !== strpos($haystack, $needle);
	} // end function str_contains()
}
// ////////////////////////////////////////////////////////////////
// end polyfill

// validate functions
// We will be using the readline() function to ask questions on the command line.
// These functions allow us to do rich validation within a while statement to trap
// the readline in a loop until our conditions are satisfied.
// We will also echo text to the console with reasons for rejection to assist the client.
// For example: bad string format, vhost appears to exist already, etc.

// ////////////////////////////////////////////////////////////////
function vhostsubdomainverify($vhostsubdomainstr) {
global $bvhwb;
global $bvhostconfdir;

//assume true until proven false
$returnval = TRUE;

// is the string clean?
// note that "-" hyphen character is permitted, not part of symbol sieve
if ( preg_match('/[\'^£$%&*()}{@#~?><>,|=_+¬]/', $vhostsubdomainstr) ) {
	$returnval = FALSE;
	echo "string has special character that is not permitted\n";
}

// string does not contain a period symbol
if (!str_contains($vhostsubdomainstr,".") ) {
	$returnval = FALSE;
	echo "string does not contain a \".\" period symbol.\n";
}

// string contains two period symbols in a row
if (str_contains($vhostsubdomainstr,"..") ) {
	$returnval = FALSE;
	echo "string contain two \"..\" period symbols in a row.\n";
}

// string contains leading period symbol
$strlen = strlen($vhostsubdomainstr);
$begsample = substr($vhostsubdomainstr,0,1);
if ($begsample == ".") {
	$returnval = FALSE;
	echo "string begins with a \".\" period symbol.\n";
}

// string contains trailing period symbol
$endlen = strlen($vhostsubdomainstr);
$endsample = substr($vhostsubdomainstr,($endlen - 1),1);
if ($endsample == ".") {
	$returnval = FALSE;
	echo "string ends with a \".\" period symbol.\n";
}

// does the vhostsubdomain already exist?
$vhostsubdomainstrund = str_replace(".","_",$vhostsubdomainstr);
clearstatcache();
if (is_dir("$bvhwb/$vhostsubdomainstrund") ) {
	$returnval = FALSE;
	echo "webdir for proposed vhost already exists.\n";
} else {
} // end if (is_dir()

$grepforvhost1str = "grep -i 'ServerName $vhostsubdomainstr' $bvhostconfdir/*";
$grepforvhost2str = "grep -i 'ServerAlias $vhostsubdomainstr' $bvhostconfdir/*";
$grepforvhost1res = shell_exec($grepforvhost1str);
$grepforvhost2res = shell_exec($grepforvhost2str);

// if the string has contents something was there for the grep to find
if ($grepforvhost1res || $grepforvhost2res) {
	echo "subdomain appears to be part of an existing virtual host\n";
	$returnval = FALSE;
}

return $returnval;
} // end function vhostsubdomainverify()

// ////////////////////////////////////////////////////////////////
function prependverify($prependverify) {

// let us make our tests and comparisons case-insensitive
$lowerpv = strtolower($prependverify);

if ( ( $lowerpv == "n") || ($lowerpv == "no") || ($lowerpv == "y") || ($lowerpv == "yes") ) {
	$returnval = TRUE;
} else {
	echo "please indicate n or no, y or yes\n";
	$returnval = FALSE;
}

return $returnval;
} // end function prependverify()

// ////////////////////////////////////////////////////////////////
function usernameverify($vhostusername) {

// force to lower-case
$vhostusername = strtolower($vhostusername);

// assume TRUE until proven false
$returnval = TRUE;

// is the string clean?
// note that "-" hyphen character is permitted, as is the "_" underscore character, not part of symbol sieve
if ( preg_match('/[\'^£$%&*()}{@#~?><>,|=+¬]/', $vhostusername) ) {
	$returnval = FALSE;
	echo "string has special character that is not permitted\n";
}

$vhunstrlen = strlen($vhostusername);

if ($vhunstrlen < 2) {
	echo "username should be minimum 2 characters\n";
	$returnval = FALSE;
}
if ($vhunstrlen > 32) {
	echo "username should be maximum 32 characters\n";
	$returnval = FALSE;
}

// what does finger return?
$fingerstr = shell_exec("finger $vhostusername 2>&1");

if (!str_contains("$fingerstr","no such user") ) {
	echo "finger found this username to already be in use\n";
	$returnval = FALSE;
}

return $returnval;
} // end function usernameverify()

// ////////////////////////////////////////////////////////////////
function passwordplainverify($passwordplain) {

// assume TRUE until proven false
$returnval = TRUE;

// we should do some tests here
// but mostly just for length, not all that fancy stuff.
// but: we will want to offer to auto-generate a plaintext password

$ppstrlen = strlen($passwordplain);

if ($ppstrlen < 8) {
	echo "password should be at least 8 characters\n";
	$returnval = FALSE;
}

return $returnval;
} // end function passwordplainverify()

// ////////////////////////////////////////////////////////////////
function genpassverify($genpassverify) {

// let us make our tests and comparisons case-insensitive
$lowergpv = strtolower($genpassverify);

if ( ( $lowergpv == "n") || ($lowergpv == "no") || ($lowergpv == "y") || ($lowergpv == "yes") ) {
	$returnval = TRUE;
} else {
	echo "please indicate n or no, y or yes\n";
	$returnval = FALSE;
}

return $returnval;
} // end function genpassverify()

// ////////////////////////////////////////////////////////////////
function genuserverify($genuserverify) {

// let us make our tests and comparisons case-insensitive
$lowerguv = strtolower($genuserverify);

if ( ( $lowerguv == "n") || ($lowerguv == "no") || ($lowerguv == "y") || ($lowerguv == "yes") ) {
	$returnval = TRUE;
} else {
	echo "please indicate n or no, y or yes\n";
	$returnval = FALSE;
}

return $returnval;
} // end function genuserverify()

// end function sink

// ////////////////////////////////////////////////////////////////
// start get information at command line: vhostsubdomain, vhostusername, vhostpassword
// also, generate and derive values
echo "\naddvhost.php\n";
echo "Add a virtual host to Apache\n\n";

// ask and validate inputs
// the readline is trapped in a loop until vhostsubdomainverify() is satisfied
// function will also echo text to the console with reasons for rejection to assist the client
// bad string format or vhost appears to exist already, etc.

// ////////////////////////////////////////////////////////////////
// vhostsubdomain

$vhostsubdomain = "";
while (!$vhostsubdomain || !vhostsubdomainverify($vhostsubdomain) ) {
    $vhostsubdomain = readline("Enter domain xxxxxxxx.xxx or subdomain xxxxxxxx.xxxxxxxx.xxx: ");
}

// putting this here because it is right after we have the $vhostsubdomain string, and just before we need it for $genuseranswer
// will also need this later for derived values like the $vhostwebdir

$vhostsubdomainund = str_replace(".","_",$vhostsubdomain);

// should we prepend with www. as well?

$prependanswer = "";
while (!$prependanswer || !prependverify($prependanswer) ) {
    $prependanswer = readline("Do you wish to prepend the subdomain www.$vhostsubdomain as well (n/y)? ");
}

$prependanswer = strtolower($prependanswer);

// ////////////////////////////////////////////////////////////////
// vhostusername

// default username
// should we offer to automatically generate a username based on the subdomain host name?

$genuseranswer = "";
while (!$genuseranswer || !genuserverify($genuseranswer) ) {
    $genuseranswer = readline("Generate a username? ");
}

$genuseranswer = strtolower($genuseranswer);

if ( ($genuseranswer=="yes") || ($genuseranswer=="y") ) {
	// generate a username
	// we are counting on the novel construction of this name with _ modeled on subdomain

	$vhostusername = $vhostsubdomainund;
	$vhostusernamestrlen = strlen($vhostusername);
	// the unique stuff is closer to the front
	// so we will truncate to first 32 characters
	if ($vhostusernamestrlen > 32) {
		$vhostusername = substr($vhostusername,0,32);
	}

	// what does finger return?
	$fingerstr2 = shell_exec("finger $vhostusername 2>&1");
	if (!str_contains("$fingerstr2","no such user") ) {
		echo "finger found this username to already be in use\n";
		exit();
	}

} else {
	// the client said no to automatic generation of username so we will ask for one
	$vhostusername = "";
	while (!$vhostusername || !usernameverify($vhostusername) ) {
	    $vhostusername = readline("Enter username: ");
	}
} // end if ($genuseranswer=="yes")

// ////////////////////////////////////////////////////////////////
// vhostpasswordplain

// should we offer to automatically generate a plaintext password?

$genpassanswer = "";
while (!$genpassanswer || !genpassverify($genpassanswer) ) {
    $genpassanswer = readline("Generate a plaintext password? ");
}

$genpassanswer = strtolower($genpassanswer);

if ( ($genpassanswer=="yes") || ($genpassanswer=="y") ) {
	// generate a random plaintext password
	$vhostpasswordplain = "";
	$passwordlength = "8";
	$posscharsplain = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
	$posscharssymbols = '!@#%*?';
	$posscharspstrlen = strlen($posscharsplain);
	$posscharssstrlen = strlen($posscharssymbols);
	// first the plain characters
	for ($i=0;$i<($passwordlength-1);$i++) {
		$randomint = random_int(0,$posscharspstrlen-1);
		$randomchar = substr($posscharsplain,$randomint,1);
		$vhostpasswordplain .= $randomchar;
	} //end for $i
	// now the symbol character
		$randomint = random_int(0,$posscharssstrlen-1);
		$randomchar = substr($posscharssymbols,$randomint,1);
		$vhostpasswordplain .= $randomchar;
	// now shuffle the string so the symbol position moves and as bonus the string is different
	$vhostpasswordplain = str_shuffle($vhostpasswordplain);
} else {
	// the client said no to automatic generation of plaintext, so we will ask for one
	$vhostpasswordplain = "";
	while (!$vhostpasswordplain || !passwordplainverify($vhostpasswordplain) ) {
		$vhostpasswordplain = readline("Enter plaintext password: ");
	}
} // end if ($genpassanswer=="yes")

// ////////////////////////////////////////////////////////////////
// vhostpasswordhashed (transformation)

// yes, i tried password_hash() -- it did not work for SHA512, this does.
// tip: apt install whois to get mkpasswd command
$vhostpasswordhashed = shell_exec("mkpasswd -m sha-512 $vhostpasswordplain");
// remove linefeed from the string
$vhostpasswordhashed = str_replace("\n","",$vhostpasswordhashed);

// ////////////////////////////////////////////////////////////////
// end get information at command line: vhostsubdomain, vhostusername, vhostpassword

// ////////////////////////////////////////////////////////////////
// start print collected values

$vhosthomedir = "$bvhwb/$vhostusername";
$vhostwebdir = "$bvhwb/$vhostusername/$vhostsubdomain";

echo "\nvalues collected, generated, and derived\n\n";

echo "vhostsubdomain: $vhostsubdomain\n";
echo "prependanswer: $prependanswer\n";
echo "vhostusername: $vhostusername\n";
echo "genpassanswer: $genpassanswer\n";
if ($echoplaintextpasswords) {
	echo "vhostpasswordplain: $vhostpasswordplain\n";
}
echo "vhostpasswordhashed: $vhostpasswordhashed\n";
echo "vhosthomedir: $vhosthomedir\n";
echo "vhostwebdir: $vhostwebdir\n";

// ////////////////////////////////////////////////////////////////
// end print collected values

// ////////////////////////////////////////////////////////////////
// start engine section

// ////////////////////////////////////////////////////////////////
// create the $vhostusername with $vhosthomedir and $vhostpasswordhashed

// build the string, look at the string, then maybe do a shell_exec of the string
$shelluseraddstr = "useradd -m -d '$vhosthomedir' '$vhostusername' -s '/usr/bin/bash' -p '$vhostpasswordhashed'";

// disable for production
// echo "shelluseraddstr: $shelluseraddstr\n";

// so it will always be declared
$shelluseraddret = "";
// disable for testing other conditions without committing to this
$shelluseraddret = shell_exec($shelluseraddstr);

//echo "shelluseraddret: $shelluseraddret\n";
// non-null (non-0) exit value from shell indicates an error
if ($shelluseraddret) {
	echo "ERROR: there was a problem executing the shell command to create the vhostusername $vhostusername. Stopping.\n";
	exit();
} else {
	//echo "SUCCESS: the vhostusername $vhostusername was created\n";
}

echo "\n";

// ////////////////////////////////////////////////////////////////
// mkdir $vhostwebdir

$mkdirvhostwebdirret = mkdir($vhostwebdir,0775,TRUE);

if (!$mkdirvhostwebdirret) {
	echo "ERROR: there was a problem creating the vhostwebdir $vhostwebdir. Stopping\n";
	exit();
} else {
	//echo "SUCCESS: the vhostwebdir $vhostwebdir was created.\n";
}

// ////////////////////////////////////////////////////////////////
// fwrite $vhostwebdir/index.php

$indexfilecontents = "<?PHP\n\necho \"<p>$vhostsubdomain</p>\\n\";\n";

$fh1 = fopen("$vhostwebdir/index.php","w");
$filesuccess1 = fwrite($fh1,$indexfilecontents);
fclose($fh1);
 
if ($filesuccess1) {

	//chown root $vhosthomedir

	$vhosthomedirownretu1 = chown("$vhosthomedir","root");
	if ($vhosthomedirownretu1) {
		//echo "SUCCESS chown root $vhosthomedir\n";
	} else {
		echo "ERROR chown root $vhosthomedirdir not successful\n";
		exit();
	}

	//chgrp root $vhosthomedir

	$vhosthomedirownretg1 = chgrp("$vhosthomedir","root");
	if ($vhosthomedirownretg1) {
		//echo "SUCCESS chgrp root $vhosthomedir\n";
	} else {
		echo "ERROR chgrp root $vhosthomedirdir not successful\n";
		exit();
	}

	//echo "SUCCESS indexfile written to file: $vhostwebdir/index.php\n";

	// chmod the $vhostwebdir
	chmod("$vhostwebdir", 0755);
	$vhostwebdirperms = substr(sprintf('%o', fileperms("$vhostwebdir")), -4);
	//echo "vhostwebdirperms: $vhostwebdirperms\n";
	if ($vhostwebdirperms == "0755") {
		//echo "SUCCESS chmod 755 $vhostwebdir\n";
	} else {
		echo "ERROR chmod 755 $vhostwebdir not successful. Stopping.\n";
	exit();
	}

	// chown $vhostusername $vhostwebdir
	$vhostwebdirownretu1 = chown("$vhostwebdir",$vhostusername);
	if ($vhostwebdirownretu1) {
		//echo "SUCCESS chown $vhostusername $vhostwebdir\n";
	} else {
		echo "ERROR chown $vhostusername $vhostwebdir not successful\n";
		exit();
	}

	// chgrp $vhostusername $vhostwebdir
	$vhostwebdirownretg1 = chgrp("$vhostwebdir",$vhostusername);
	if ($vhostwebdirownretu1) {
		//echo "SUCCESS chgrp $vhostusername $vhostwebdir\n";
	} else {
		echo "ERROR chgrp $vhostusername $vhostwebdir not successful\n";
		exit();
	}

	// chmod the $vhostwebdir/index.php
	chmod("$vhostwebdir/index.php", 0755);
	$vhostindexperms = substr(sprintf('%o', fileperms("$vhostwebdir/index.php")), -4);
	//echo "vhostindexperms: $vhostindexperms\n";
	if ($vhostindexperms == "0755") {
		//echo "SUCCESS chmod 755 $vhostwebdir/index.php\n";
	} else {
		echo "ERROR chmod 755 $vhostwebdir/index.php not successful. Stopping.\n";
	exit();
	}

	// chown $vhostusername $vhostwebdir/index.php
	$vhostindexownretu1 = chown("$vhostwebdir/index.php",$vhostusername);
	if ($vhostindexownretu1) {
		//echo "SUCCESS chown $vhostusername $vhostwebdir/index.php\n";
	} else {
		echo "ERROR chown $vhostusername $vhostwebdir/index.php not successful\n";
		exit();
	}

	// chgrp $vhostusername $vhostwebdir/index.php
	$vhostindexownretg1 = chgrp("$vhostwebdir/index.php",$vhostusername);
	if ($vhostindexownretu1) {
		//echo "SUCCESS chgrp $vhostusername $vhostwebdir/index.php\n";
	} else {
		echo "ERROR chgrp $vhostusername $vhostwebdir/index.php not successful\n";
		exit();
	}
} else {
	echo "ERROR indexfile not written to file: $vhostwebdir/index.php\n";
	exit();
}

// ////////////////////////////////////////////////////////////////
// fwrite $bvhostconfdir/$vhostsubdomain.conf

$timestring = date("Y/m/d H:i:s T");

$vhostconffilecontents  = "# generated $timestring by addvhost.php\n";
$vhostconffilecontents .= "<VirtualHost $vhostip:80>\n";
$vhostconffilecontents .= "<IfModule mpm_itk_module>\n";
$vhostconffilecontents .= "\tAssignUserID $vhostusername $vhostusername\n";
$vhostconffilecontents .= "</IfModule>\n";
$vhostconffilecontents .= "ServerName $vhostsubdomain\n";
if ( ($prependanswer == "y") || ($prependanswer == "yes") ) {
	$vhostconffilecontents .= "ServerAlias www.$vhostsubdomain\n";
}
$vhostconffilecontents .= "DocumentRoot $vhostwebdir\n";
$vhostconffilecontents .= "ServerAdmin $vhostserveradmin\n";
$vhostconffilecontents .= "CustomLog /var/log/apache2/$vhostsubdomain-access_log combined\n";
$vhostconffilecontents .= "ErrorLog /var/log/apache2/$vhostsubdomain-error_log\n";
$vhostconffilecontents .= "</VirtualHost>\n";

// disable in production
// echo "vhostconffilecontents = \n$vhostconffilecontents\n";

// write the text file
$fh2 = fopen("$bvhostconfdir/$vhostsubdomain.conf","w");
$filesuccess2 = fwrite($fh2,$vhostconffilecontents);
fclose($fh2);
 
if ($filesuccess2) {
	//echo "SUCCESS virtual host config written to file: $bvhostconfdir/$vhostsubdomain.conf\n";
} else {
 	echo "ERROR virtual host config not written to file: $bvhostconfdir/$vhostsubdomain.conf\n";
 	exit();
}

// ////////////////////////////////////////////////////////////////
// shell_exec a2ensite $bvhostconfdir/$vhostsubdomain.conf

// so it will always be declared
$shella2enret = "";
// disable for testing other conditions without committing to this
$shella2enret = shell_exec("a2ensite $vhostsubdomain.conf");

//echo "shella2enret: $shella2enret\n";
// non-null (non-0) exit value from shell indicates an error
if ( str_contains($shella2enret,"ERROR") ) {
	//echo "ERROR: there was a problem executing the shell command to enable the vhostsubdomain $vhostsubdomain. Stopping.\n";
	exit();
} else {
	echo "SUCCESS: enabled vhostsubdomain $vhostsubdomain\n";
}

// ////////////////////////////////////////////////////////////////
// echo to console suggestion that systemctl restart apache2 be executed

echo "\n";
echo "Next steps:\n";
echo "This script did not restart apache2. That is up to you.\n";
echo "systemctl restart apache2\n";
echo "validate site on port 80\n";
echo "run certbot --apache to expand ssl cert\n";
echo "systemctl restart apache2\n";
echo "validate site on port 443\n";
echo "\n";

// ////////////////////////////////////////////////////////////////
// end engine section

Declaring the host name in DNS

Declare the host name in the DNS zone file for the domain:

Testing that the host name loads a “park page”

Use a web browser to visit the host name:

Executing commands as root

This chapter assumes that you are logged in as the root user. If you are not already root, escalate using this command:

sudo su

Installing the finger, whois, and unzip utilities

This script depends on the finger, whois, and unzip utilities.

Enter this command:

apt install finger whois unzip

Downloading and uncompressing the addvhost.zip file

Enter this command:

wget https://blog.gordonbuchan.com/files/addvhost.zip

Enter this command:

unzip addvhost.zip

Enter these commands:

mv addvhost.php /usr/bin
chmod 755 /usr/bin/addvhost.php

Enter this command:

addvhost.php

Completing the addvhost.php questionnaire, entering a username and a plaintext password

In this example, we decline the option to generate a username, and enter a value for the username. We also decline the option to generate a plaintext password, and enter a value for the plaintext password.

root@server01:~# addvhost.php

addvhost.php
Add a virtual host to Apache

Enter domain xxxxxxxx.xxx or subdomain xxxxxxxx.xxxxxxxx.xxx: webpresencestepbystep.ca
Do you wish to prepend the subdomain www.webpresencestepbystep.ca as well (n/y)? y
Generate a username? n
Enter username: webuserca
Generate a plaintext password? n
Enter plaintext password: password

values collected, generated, and derived

vhostsubdomain: webpresencestepbystep.ca
prependanswer: y
vhostusername: webuserca
genpassanswer: n
vhostpasswordplain: password
vhostpasswordhashed: $6$IdLp5YrW.Z3Tvnm$hlRvIBour47UcZrVm0QA2YgLp2z3C3e5W7PwiS3o.KbZz.mtFeCvWdew/eemdec3Wz9t.WEIuIm3Q2EKTuXYd1
vhosthomedir: /usr/web/webuserca
vhostwebdir: /usr/web/webuserca/webpresencestepbystep.ca

SUCCESS: enabled vhostsubdomain webpresencestepbystep.ca

Next steps:
This script did not restart apache2. That is up to you.
systemctl restart apache2
validate site on port 80
run certbot --apache to expand ssl cert
systemctl restart apache2
validate site on port 443

Enter this command:

systemctl restart apache2

Enter this command:

certbot --apache

Enter this command:

systemctl restart apache2

Visiting the virtual host

Use a web browser to visit the host name:

Completing the addvhost.php questionnaire, accepting a generated username and plaintext password

In this example, we accept the option to generate a username. We also accept the option to generate a plaintext password.

Take careful note of the plaintext password value, as shown in the “vhostpasswordplain” field.

root@server01:~# addvhost.php

addvhost.php
Add a virtual host to Apache

Enter domain xxxxxxxx.xxx or subdomain xxxxxxxx.xxxxxxxx.xxx: webpresencestepbystep.com
Do you wish to prepend the subdomain www.webpresencestepbystep.com as well (n/y)? y
Generate a username? y
Generate a plaintext password? y

values collected, generated, and derived

vhostsubdomain: webpresencestepbystep.com
prependanswer: y
vhostusername: webpresencestepbystep_com
genpassanswer: y
vhostpasswordplain: NQeQ2%VT
vhostpasswordhashed: $6$Woe9pPUwnXqUP$9RW60p6SSNfqLJSi4BeAyhe89mBpyTELk2/at7eJcKqou5Q9Y6Nti4P7EoyTV0CBfin6SxlvNHvkZjrpEGxxX0
vhosthomedir: /usr/web/webpresencestepbystep_com
vhostwebdir: /usr/web/webpresencestepbystep_com/webpresencestepbystep.com

SUCCESS: enabled vhostsubdomain webpresencestepbystep.com

Next steps:
This script did not restart apache2. That is up to you.
systemctl restart apache2
validate site on port 80
run certbot --apache to expand ssl cert
systemctl restart apache2
validate site on port 443

Enter this command:

systemctl restart apache2

Enter this command:

certbot --apache

Enter this command:

systemctl restart apache2

Visiting the virtual host

Use a web browser to visit the host name:

Previous step: Chapter 15: Using dwservice.net to provide remote technical support as an alternative to TeamViewer
Next step: Chapter 17: Using subdomains to host multiple websites under a single domain name

Web presence step by step Chapter 15: Using dwservice.net to provide remote technical support as an alternative to TeamViewer

Previous step: Chapter 14: Installing and configuring Live Helper Chat to add text chat support to a website
Next step: Chapter 16: Using a script to automate the creation of a virtual host on an Apache web server

Web presence step by step is a series of posts that show you to how to build a web presence.

In this chapter we install and configure dwservice.net to provide remote technical support as an alternative to TeamViewer.

dwservice.net allows a client to share their computer’s desktop so you can provide technical support

When you build and maintain technical systems, you need to support the clients of those systems, including customers, colleagues, and contractors. dwservice.net allows you to invite a client to share their computer’s desktop so you can provide technical support.

Creating an account on the dwservice.net service

Visit the dwservice.net site:

https://dwservice.net

Create a username and password for the dwservice.net site. You will need it later in this procedure.

dwservice.net provides client software for the Windows, MacOS, and Linux operating systems

This chapter contains sections describing how to install the dwservice.net client for Windows, MacOS, and Linux.

Windows

Visiting the website to download the installer on a Windows workstation

Visit the dwservice.net site:

https://dwservice.net

Click on “Download”:

Click on “Download”:

Right-click on the file name, click “Show in folder”:

Using the dwservice.net client on a Windows workstation in run-once mode

Double-click on the file to run the dwagent.exe setup program:

Select “Run,” click on “Next”:

View on the Windows workstation:

Accessing the Windows workstation from the dwservice.net site

Visit the dwservice.net site:

https://dwservice.net

Enter the username and password displayed on the Windows workstation running the dwservice.net client, click on “Sign in”:

Resources available on the Windows workstation

This page shows the resources available on the Windows workstation:

Screen (remote desktop)

Remote control of the desktop of the Windows workstation:

Files and Folders

Access to the filesystem on the Windows workstation:

Shell

Access to the operating system shell prompt on the Windows workstation:

Installing the dwservice.net client on a Windows workstation to enable unattended access

Right-click on the “dwagent.exe” file. Select “Run as administrator”:

Select “Install,” click on “Next”:

Select “Yes,” click on “Next”:

Select “Creating a new agent, click on “Next”:

Enter the username and password of a valid dwservice.net account in the “DWS user” and DWS password” fields. Enter a name to describe the workstation in the “Agent name” field. Click on “Next”:

Click on “Close”:

Visit the dwservice.net site:

https://dwservice.net

Enter the username and password of a valid dwservice.net account in the “DWS user” and DWS password” fields. Click on “Sign in”:

Click on “Agents”:

Click on the icon for the Windows workstation:

Resources available on the Windows workstation

This page shows the resources available on the Windows workstation:

Screen (remote desktop)

Remote control of the desktop of the Windows workstation:

Files and Folders

Access to the filesystem on the Windows workstation:

Shell

Access to the operating system shell prompt on the Windows workstation:

MacOS

Visiting the website to download the installer on a MacOS workstation

Visit the dwservice.net site:

https://dwservice.net

Click on “Download”:

Click on “Download”:

Click on “Allow”:

Locate the downloaded file, click on it:

This error message appears. Click on “OK”:

On the MacOS workstation, launch “System Preferences.” Click on “Security & Privacy”:

Click on “Open Anyway”:

Click on “Open”:

The dwservice.net package opens:

Click on “DWAgent”:

Click on “Run”:

View on the MacOS workstation:

Accessing the MacOS workstation from the dwservice.net site

Visit the dwservice.net site:

https://dwservice.net

Enter the username and password displayed on the MacOS workstation running the dwservice.net client, click on “Sign in”:

The following message appears on the MacOS workstation. Click on “Open System Preferences”:

Click on “Quit Now”:

On the MacOS workstation, restart the dwservice.net client.

Accessing the MacOS workstation from the dwservice.net site (again)

Visit the dwservice.net site:

https://dwservice.net

Enter the username and password displayed on the MacOS workstation running the dwservice.net client, click on “Sign in”:

Resources available on the MacOS workstation

This page shows the resources available on the MacOS workstation:

This image has an empty alt attribute; its file name is Screenshot-from-2021-05-01-06-49-07-1024x725.png

Screen (remote desktop)

Remote control of the desktop of the MacOS workstation:

Files and Folders

Access to the filesystem on the MacOS workstation:

Shell

Access to the operating system shell prompt on the MacOS workstation:

Installing the dwservice.net client on a MacOS workstation to enable unattended access

Click on “Open”:

Select “Install,” click on “Next”:

enter the “User Name” and “Password” for the MacOS workstation, click on “OK”:

Click on “Next”:

Select “Yes,” click on “Next”:

Select “Creating a new agent,” click on “Next”:

Enter the username and password of a valid dwservice.net account in the “DWS user” and DWS password” fields. Enter a name to describe the workstation in the “Agent name” field. Click on “Next”:

Click on “Close”:

View of the DWAgent Monitor on MacOS:

Click on “OK”:

Visit the dwservice.net site:

https://dwservice.net

Enter the username and password of a valid dwservice.net account in the “DWS user” and DWS password” fields. Click on “Sign in”:

Click on “Agents”:

Click on the icon for the MacOS workstation:

Resources available on the MacOS workstation

This page shows the resources available on the MacOS workstation. Click on “Screen”:

Allowing the dwaggui program to record the computer’s screen (needed for Screen/remote desktop access)

Click on “Open System Preferences”:

Allow the dwgguilnc program to record the screen:

Screen (remote desktop)

Remote control of the desktop of the MacOS workstation:

Files and Folders

Access to the filesystem on the MacOS workstation:

Shell

Access to the operating system shell prompt on the MacOS workstation:

Linux

Ensuring that Wayland is disabled

dwservice.net is not compatible with the Wayland display server.

from a root operating system prompt (use “sudo su” if you are not yet logged in as root), enter these commands:

cd /etc/gdm3
nano custom.conf

(Note: the directory location varies by distribution. For the Fedora distribution, specify the directory “etc/gdm”).

Remove the # comment before “WaylandEnable=false”:

Save and exit the file:

If you had to remove the “#” sign before “WaylandEnable=false” then reboot the Linux workstation so the change can take effect.

Visiting the website to download the installer on a Linux workstation

Visit the dwservice.net site:

https://dwservice.net

Click on “Download”:

Using the dwservice.net client on a Linux workstation in run-once mode

Open a terminal window on the Linux desktop. Change to the folder where the file was downloaded. Enter this command:

bash dwagent.sh

Select “Run,” click on “Next”:

View on the Linux Workstation:

Accessing the Linux workstation from the dwservice.net site

Visit the dwservice.net site:

https://dwservice.net

Enter the username and password displayed on the Linux workstation running the dwservice.net client, click on “Sign in”:

Resources available on the Linux workstation

This page shows the resources available on the Linux workstation:

Screen (remote desktop)

Remote control of the desktop of the Linux workstation:

Files and Folders

Access to the filesystem on the Linux workstation:

Shell

Access to the operating system shell prompt on the Linux workstation:

Installing the dwservice.net client on a Linux workstation to enable unattended access

Open a terminal window on the Linux desktop. Change to the folder where the file was downloaded.

Use “sudo su” to become root if you have not already done so. enter these commands:

Enter this command:

bash dwagent.sh 

Select “Install,” click on “Next”:

Click on “Next”:

Select “Yes,” click on “Next”:

Enter the username and password of a valid dwservice.net account in the “DWS user” and DWS password” fields. Enter a name to describe the workstation in the “Agent name” field. Click on “Next”:

Click on “Close”:

Visit the dwservice.net site:

https://dwservice.net

Enter the username and password of a valid dwservice.net account in the “DWS user” and DWS password” fields. Click on “Sign in”:

Click on “Agents”:

Click on the icon for the Linux workstation:

Resources available on the Linux workstation

This page shows the resources available on the Linux workstation:

Screen (remote desktop)

Remote control of the desktop of the Linux workstation:

Files and Folders

Access to the filesystem on the Linux workstation:

Shell

Access to the operating system shell prompt on the Linux workstation:

Previous step: Chapter 14: Installing and configuring Live Helper Chat to add text chat support to a website
Next step: Chapter 16: Using a script to automate the creation of a virtual host on an Apache web server

Web presence step by step Chapter 14: Installing and configuring Live Helper Chat to add text chat support to a website

Previous step: Chapter 13: Installing and configuring MyBB to create a community forum site
Next step: Chapter 15: Using dwservice.net to provide remote technical support as an alternative to TeamViewer

Web presence step by step is a series of posts that show you to how to build a web presence.

In this chapter we install and configure Live Helper Chat to add text chat support to a website.

Installing the php-curl and php-bcmath extensions, enabling the Apache headers module, and restarting the Apache web server

Live Helper Chat requires the php-curl library, the php-bcmath library, and the headers Apache module.

Use an SSH terminal program to connect to the Ubuntu Linux cloud server you created in Chapter 3: Buying an Ubuntu Linux cloud server from Digital Ocean.

Enter these commands:

apt install php-curl php-bcmath
a2enmod headers
apachectl restart

Creating the MySQL database that will store data for the Live Helper Chat software

Enter this command:

mysql -u root -p

In the MySQL console, enter these commands (where ‘xxxxxx’ is your password):

create user lhc01@localhost;
set password for lhc01@localhost = 'xxxxxx';
create database lhc01;
use lhc01;
grant all privileges on * to lhc01@localhost;
quit

Downloading the zip file containing the Live Helper Chat software

Visit this site:

https://livehelperchat.com

Click on “Download”:

Click on on the link below “Github – recommended link”:

The file downloads:

Extracting the contents of the zip file

If you need detailed instructions on how to uncompress zip files or rename directories on Windows, MacOS, or Linux

This chapter assumes that you have followed the steps in Chapter 8: Installing and configuring WordPress to create a website, and Chapter 9: Installing and configuring phpMyAdmin to manage MySQL databases. If you have not read those chapters, and you are not familiar with how to uncompress a zip file or rename a directory using your operating system, please consult those chapters.

Uncompressing the livehelperchat-master.zip file

We need to uncompress the livehelperchat-master.zip file:

Viewing the extracted folders and files

We can see the lhc_web directory:

Using FileZilla to upload the lhc_web directory to the directory containing the documents for your website

We will use the FileZilla file transfer program to upload the directory “lhc_web” to the directory containing the documents for your website.

If you need detailed instructions on how to install and use FileZilla on Windows, MacOS, or Linux

This chapter assumes that you have read Chapter 7: Configuring the SSH server on an Ubuntu Linux cloud server to limit SFTP directory visibility within chroot jail directories, or that you are familiar with the FileZilla file transfer program.

Using Filezilla, connect to the Ubuntu Linux cloud server you created in Chapter 3: Buying an Ubuntu Linux cloud server from Digital Ocean. On the right (remote) side, select the directory that contains the documents for your website. On the left (local) side, select the directory that contains the “lhc_web” directory. Right-click or command-click on the directory, select Upload:

The transfer completes:

Installing the Live Helper Chat software using the Live Helper Chat installation wizard

For our example domain, we will visit:

https://linuxstepbystep.com/lhc_web/index.php/site_admin/install/install

(substitute your domain name)

Click “Next”:

Complete the fields as shown. Click “Next”:

Complete the field as shown below. Click “Finish installation”:

Click “Login here”:

Generating the Live Helper Chat JavaScript text to add to an HTML header

(Note: for our example domain, we can visit this site at https://linuxstepbyste.com/lhc_web/index.php/site_admin)

Enter your username and password. Click “Login”:

Click on “Settings”:

Under “Mobile,” click on “Settings”:

Select the checkbox “Enable notifications,” click on “Save”:

Click on “Settings”:

Click on “Embed code”:

Click on “Widget embed code (new):

Complete the fields and select the options as required for your site. Copy the code from the text area to the page where you would like the web chat window to appear:

Embedding Live Helper Chat in a WordPress site

To embed the Live Helper Chat application in a WordPress site, we must first install the Insert Headers and Footers plugin for WordPress.

Installing the Insert Headers and Footers WordPress plugin

In your WordPress control panel, click on “Plugins,” then click on “Add New”:

In the search window, enter the text “insert headers and footers,” locate the plugin “Insert Headers and Footers,” click on “Install Now”:

Click on “Activate”:

Click on “Settings,” then click on “Insert Headers and Footers”:

Adding the JavaScript code using the Insert Headers and Footers WordPress plugin

Complete the fields as shown.

Scroll to the bottom of the page. Paste the JavaScript code you generated earlier in the section “Generating the Live Help Chat JavaScript text to add to an HTML header” to the field “Scripts in Footer,” click “Save”:

Cick on “Visit Site”:

The chat window is visible on the WordPress site:

Texting between the site’s visitors and the site’s operators:

Embedding Live Helper Chat in a simple web page

We can add the Live Helper Chat text chat window to a simple web page.

Using an SSH terminal program, connect to your Ubuntu Linux cloud server. Change to the directory containing the documents for your website. Enter the command:

nano test.html

Paste the JavaScript code you generated in the section “Generating the Live Help Chat JavaScript text to add to an HTML header,” press Control-X to save and exit the file:

Enter these commands (where username is the username that owns the documents for the website):

chown username:username test.html
chmod 755 test.html

View the test.html page in a web browser:

Accessing the Live Helper Chat operator panel

For our example domain, we can visit this site at https://linuxstepbyste.com/lhc_web/index.php/site_admin (Substitute your domain name.)

Enter your username and password. Click “Login”:

Click on “Chat”:

Configuring and Using the Android client

Download the “Live Helper Chat” app from the Google Play Store. When you launch the application, you will see this screen. Click on “+”:

Complete the fields below as shown (substitute your domain name). Click “Login”:

Configuring and using the iOS client

Download the “Live Helper Chat” app from the Apple App Store. When you launch the application, you will see this screen. Click on “Allow”:

Click on “+”:

Complete the fields as shown (substitute your domain name). Click “Login”:

Previous step: Chapter 13: Installing and configuring MyBB to create a community forum site
Next step: Chapter 15: Using dwservice.net to provide remote technical support as an alternative to TeamViewer

Web presence step by step Chapter 13: Installing and configuring MyBB to create a community forum site

Previous step: Chapter 12: Installing and configuring the WooCommerce plugin for WordPress to enable a shopping cart for transactions
Next step: Chapter 14: Installing and configuring Live Helper Chat to add text chat support to a website

Web presence step by step is a series of posts that show you to how to build a web presence.

In this chapter, we install and configure MyBB to create a community forum site.

If you are not familiar with SSH terminal programs

This chapter assumes that you have read Chapter 4: Using an SSH terminal program to connect to an Ubuntu Linux cloud server. If you are not familiar with SSH terminal programs, please consult that chapter.

Creating a MySQL database to host the data for the MyBB community forum site

Use an SSH terminal program to connect to the Ubuntu Linux cloud server you created in Chapter 3: Buying an Ubuntu Linux cloud server from Digital Ocean.

From the system prompt, enter the command:

mysql -u root -p

From the MySQL console prompt, enter these commands (use a unique password in place of ‘xxxxxx’):

create user bb01@localhost;
set password for bb01@localhost = 'xxxxxx';
create database bb01;
use bb01;
grant all privileges on * to bb01@localhost;
quit

Installing the php-gd extension

In order to generate graphics representing captchas, we need to install the php-gd extension.

Enter the command:

apt install php-gd

Enter the command:

systemctl restart apache2

Configuring a Gmail account to allow MyBB to send email messages

We need to select or create a Gmail account that the MyBB community forum software will use to send email messages. This can be the same Gmail account you selected or created in Chapter 11: Installing and configuring the WP Mail SMTP plugin for WordPress to enable WordPress to send email messages.

Click on the settings icon (gear symbol in upper-right):

Click on “See all settings”:

Click on “Accounts and Import”:

Click on “Other Google Account settings”:

Click on “Security”:

In the section “Less secure app access,” click on “Turn on access (not recommended)”:

Enable the control so that the page displays “Allow less secure apps: ON”:

Click on “Forwarding and POP/IMAP,” select the option “Enable IMAP,” click on “Save Changes”:

Downloading the MyBB zip file

Visit this site:

https://mybb.com/download/

Click on “Download MyBB x.x.xx” (where x.x.xx is the version):

The file downloads:

If you are using Chrome, right-click or command-click on the name of the file, select “Show in folder”:

Uncompressing the MyBB zip file and renaming the Upload directory

If you need detailed instructions on how to uncompress zip files or rename directories on Windows, MacOS, or Linux

This chapter assumes that you have followed the steps in Chapter 8: Installing and configuring WordPress to create a website, and Chapter 9: Installing and configuring phpMyAdmin to manage MySQL databases. If you have not read those chapters, and you are not familiar with how to uncompress a zip file or rename a directory using your operating system, please consult those chapters.

Uncompressing the mybb_xxxx.zip file

We need to uncompress the mybb_xxxx.zip file:

Uncompressing the zip file extracts 2 directories: “Documentation,” and “Upload”:

Renaming the directory named “Upload” to “community”

Rename the “Upload” directory to “community”:

Using FileZilla to upload the community directory to the directory containing the documents for your website

We will use the FileZilla file transfer program to upload the directory renamed as “community” to the directory containing the documents for your website.

If you need detailed instructions on how to install and use FileZilla on Windows, MacOS, or Linux

This chapter assumes that you have read Chapter 7: Configuring the SSH server on an Ubuntu Linux cloud server to limit SFTP directory visibility within chroot jail directories, or that you are familiar with the FileZilla file transfer program.

Using Filezilla, connect to the Ubuntu Linux cloud server you created in Chapter 3: Buying an Ubuntu Linux cloud server from Digital Ocean. On the right (remote) side, select the directory that contains the documents for your website. On the left (local) side, select the directory that contains the “community” directory. Right-click or command-click on the directory, select Upload:

The transfer completes:

Installing the MyBB software using the MyBB installation wizard

For our example domain, we will visit:

https://linuxstepbystep.com/community

(substitute your domain name)

Click “Next”:

Click “Next”:

Click “Next”:

Complete the fields as show below. Click “Next”:

Scroll to the bottom of the web page. Click “Next”:

Click “Next”:

Click “Next”:

Complete the fields as show below. Click “Next”:

Complete the fields as show below. Click “Next”:

This screen will display when we have finished:

Visiting the community forum site

For our example domain, we will visit:

https://linuxstepbystep.com/community

(substitute your domain name)

Logging into the admin account

Enter the username and password for the admin user. Click “Login”:

Click on “Admin CP”:

Enter the username and password for the admin user. Enter the secret PIN. Click “Login”:

Configuring the MyBB community forum site

Click on “Configuration”:

Scroll towards the bottom of the web page:

Click on “Mail Settings”:

Configure settings for “Mail handler”

Mail handler: select "SMTP mail"
SMTP hostname: smtp.gmail.com
SMTP port: 587
SMTP username: the address of the gmail account you selected or created earlier in this procedure
SMTP Encryption Mode: select "TLS encryption"

Click “Save Settings”:

Click on “Login and Registration Options”:

For “Registration Method,” select “Send Email Verification”:

Scroll to the bottom of the web page. Click on “Save Settings”:

Registering as a new user on the community forum site

Use a web browser to visit the community forum site. Click on “Register.” On the page titled “Registration Agreement,” consider then if you decide click “I agree”:

Complete the fields as show in the example below. Click “Submit Registration!”:

MyBB displays this message:

Check the email account you provided during registration. View the message sent by MyBB community forum site:

To activate your account, click on the link in the email:

Welcome as a member:

We will create a post. Click on “My Forum”:

Click on “Post Thread”:

In the field “Thread Subject:” enter a subject.

In the field “Your Message:” enter the text of a message.

Click on “Post Thread”:

The message is displayed. Click on “My Category”:

We now have 1 thread and 1 post:

Previous step: Chapter 12: Installing and configuring the WooCommerce plugin for WordPress to enable a shopping cart for transactions
Next step: Chapter 14: Installing and configuring Live Helper Chat to add text chat support to a website