Using the left mouse button to select and copy text in terminals and the middle mouse button to paste has been a feature of X-Windows, and the various window managers built on top of X-Windows, since the early 1990s. With the release of Ubuntu 20.04 and Gnome 3.36 Canonical has removed this convention, forcing a more awkward and slower select, right click, select Copy from a menu, point, right click, select Paste from menu to do the same thing.
If you want to restore select-to-copy, middle button to paste functionality to Ubuntu 20.04 just follow these steps.
Restore select-to-copy functionality
Edit the file .Xresources in your home directory.
Add the line:
xterm*selectToClipboard: true
… to the file, then logout of your desktop and log back in, or reboot.
Once you’ve done that any text that you select in the Terminal program with your left mouse button will be copied to your clipboard. Left click a word and the word is copied to the clipboard. Left click and drag to select and copy an entire line, an entire paragraph, or more.
Restore middle-button paste functionality
Install gnome-tweaks:
sudo apt-get install gnome-tweaks
Click “Activities” in the upper right and search for “tweaks”, click the “Tweaks” icon.
Select “Keyboard & Mouse” and turn “Middle Click Paste” to “on”.
Once you’ve done that, clicking the middle mouse button will paste text from your clipboard back into the terminal.
When installing a fresh copy of Ubuntu one of the options is to install with a LUKS-encrypted Logical Volume Manager Volume Group (LVM VG). This puts your root volume on the encrypted LVM VG. When you power up your machine Ubuntu prompts you to enter the decryption passphrase in order to decrypt the VG and start your computer. Without the passphrase the contents of your hard drive are unreadable.
If you add encrypted external drives and/or additional VGs you will end up with multiple encrypted volumes. Ubuntu will prompt you for the passphrase of each additional encrypted volume when you boot up the machine.
If you don’t want to enter multiple, different passphrases each time you boot, you can store the passphrases for additional volumes on the encrypted root filesystem of your first drive using the /etc/crypttab file. You’ll just be prompted for one passphrase, of the first VG, and that decrypts the passphrases needed to decrypt the additional volumes.
Here’s how it works.
The /etc/crypttab file contains 4 fields per line: the name of the encrypted volume, a UUID identifying the storage device, the name of a file with the decryption passphrase, and encryption options.
The first volume, nvme0n1p5, is the encrypted boot volume. It contains the root filesystem and the /root home directory. The third field is “none” which means that Ubuntu will prompt you for a decryption passphrase in order to unlock and decrypt the drive.
The remaining volumes have files defined that contain the decryption passphrase for each volume. Those files are hidden files in the /root home directory. Once the nvme0n1p5 volume is decrypted and mounted, the remaining volumes are automatically decrypted using the passphrases stored in the hidden files.
The end result is that all of your drives are encrypted, but you only have to enter one passphrase to unlock all of your drives.
I’ve been setting up and tearing down Kubernetes clusters for testing various things for the past year, mostly using Vagrant/Virtualbox but also some VMware vSphere and OpenStack deployments.
I wanted to set something a little more permanent up at my home lab — a cluster where I could add and remove nodes, run nodes on multiple physical machines, and use different types of compute hardware.
Set up the virtual machines
To get started I used a desktop System76 Wild Dog Pro Linux box (4.5 GHz i7-7700K, 64GB DDR4) and my create-vm script to create six Ubuntu 18.04 “Bionic Beaver” VMs for the cluster:
for n in $(seq 1 6); do create-vm -n node$n \ -i ./ubuntu-18.04-server-amd64.iso \ -k ./ubuntu.ks \ -r 4096 \ -c 2 \ -s 40 done
With these parameters each VM will have 4GB RAM, 2 VCPUs, and a 40GB hard drive.
Install and configure Kubespray
I cloned Kubespray into a directory and created an Ansible inventory file following the instructions from the README.
git clone git@github.com:kubernetes-sigs/kubespray.git cd kubespray pip install -r requirements.txt rm -Rf inventory/mycluster/ cp -rfp inventory/sample inventory/mycluster declare -a IPS=($(for n in $(seq 1 6); do get-vm-ip node$n; done)) CONFIG_FILE=inventory/mycluster/hosts.ini \ python3 contrib/inventory_builder/inventory.py ${IPS[@]}
The inventory.py script generates an Ansible hosts inventory file in inventory/mycluster/hosts.ini with all of your VM IP addresses.
I like to add one variable override to the bottom of hosts.ini which copies the kubectl credentials over to my host machine. That way I can run kubectl commands directly from my desktop. The extra lines to add to the bottom of hosts.ini are:
[all:vars] kubectl_localhost=true
Install Kubernetes
To install Kubernetes on the VMs I run the Kubespray cluster.yaml playbook:
Once the playbooks have finished, you should have a fully-operational Kubernetes cluster running on your desktop.
At this point you should be able to query the cluster from your desktop using kubectl. For example:
$ kubectl cluster-info Kubernetes master is running at https://192.168.122.251:6443 coredns is running at https://192.168.122.251:6443/api/v1/namespaces/kube-system/services/coredns:dns/proxy kubernetes-dashboard is running at https://192.168.122.251:6443/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
$ kubectl get nodes NAME STATUS ROLES AGE VERSION node1 Ready master,node 3d6h v1.13.0 node2 Ready master,node 3d6h v1.13.0 node3 Ready node 3d6h v1.13.0 node4 Ready node 3d6h v1.13.0 node5 Ready node 3d6h v1.13.0 node6 Ready node 3d6h v1.13.0
$ kubectl get pods --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE kube-system calico-kube-controllers-67f89845f-6zbvx 1/1 Running 1 3d6h kube-system calico-node-jh7ng 1/1 Running 2 3d6h kube-system calico-node-l9vfb 1/1 Running 2 3d6h kube-system calico-node-mqxjx 1/1 Running 2 3d6h ...
Set up the Kubernetes Dashboard
One of the first things I like to do is set up access to the Kubernetes dashboard. First I set up a service account for the admin user:
Finally I plug the dashboard URL that I got from kubectl cluster-info into my browser, select “Token” authentication, and cut and paste in the bearer token to log into the system.
Once logged in, an overview of my cluster pops up:
With a minimal amount of working compute infrastructure, it’s easy to set up your own production-quality Kubernetes cluster using Kubespray.
Since virsh domifaddr doesn’t work to get the IP addresses of VMs on a bridged network, I wrote a get-vm-ip script (which you can download from Github) which uses this to get the IP of a running VM:
HOSTNAME=[your vm name]
MAC=$(virsh domiflist $HOSTNAME | awk '{ print $5 }' | tail -2 | head -1)
arp -a | grep $MAC | awk '{ print $2 }' | sed 's/[()]//g'
The virsh command gets the MAC address, the last line finds the IP address using arp.
I was looking for a way to automate the creation of VMs for testing various distributed system / cluster software packages. I’ve used Vagrant in the past but I wanted something that would:
Allow me to use raw ISO files as the basis for guest VMs.
Guest VMs should be set up with bridged IPs that are routable from the host.
Guest VMs should be able to reach the Internet.
Other hosts on the local network should be able to reach guest VMs. (Setting up additional routes is OK).
VM creation should work with any distro that supports Kickstart files.
Scripts should be able to create and delete VMs in a scripted, fully-automatic manner.
Guest VMs should be set up to allow passwordless ssh access from the “ansible” user.
I’ve previously used virsh’s virt-install tool to create VMs and I like how easy it is to set up things like extra network interfaces and attach existing disk images. The scripts in this repo fully automate the virsh VM creation process.
Scripts
I put all of my code into a Github repo containing these scripts:
create-vm – Use .iso and kickstart files to auto-generate a VM.
delete-vm – Delete a virtual machine created with create-vm.
get-vm-ip – Get the IP address of a VM managed by virsh.
encrypt-pw – Returns a SHA512 encrypted password suitable for pasting into Kickstart files.
I’ve also included a sample ubuntu.ks Kickstart file for creating an Ubuntu host.
Host setup
I’m running the scripts from a host with Ubuntu Linux 18.10 installed. I added the following to the host’s Ansible playbook to install the necessary virtualization packages:
The Ansible user: Although I’d prefer to create the “ansible” user as a locked account,with no password just an ssh public key, Kickstart on Ubuntu does not allow this, so I do set up an encrypted password.
To set up your own password, use the encrypt-pw script to create a SHA512-hashed password that you can copy and paste into the Kickstart file. After a VM is created you can use this password if you need to log into the VM via the console.
To use your own ssh key, replace the ssh key in the %post section with your own public key.
The %post section at the bottom of the Kickstart file does a couple of things:
It updates all packages with the latest versions.
To configure a VM with Ansible, you just need ssh access to a VM and Python installed. on the VM. So I use %post to install an ssh-server and Python.
I start the serial console, so that virsh console $vmname works.
I add a public key for Ansible, so I can configure the servers with Ansible without entering a password.
Despite the name, the commands in the %post section are not the last commands executed by Kickstart on an Ubuntu 18.10 server. The “ansible” user is added after the %post commands are executed. This means that the Ansible ssh public key gets added before the ansible user is created.
To make key-based logins work I set the UID:GID of authorized_keys to 1000:1000. The user is later created with UID=1000, GID=1000, which means that the authorized_keys file ends up being owned by the ansible user by the time the VM creation is complete.
Create an Ubuntu 18.10 server
This creates a VM using Ubuntu’s text-based installer. Since the `-d` parameter is used,progress of the install is shown on screen.
This starts the VM creation process and exits. Creation of the VMs continues in the background.
for n in `seq 1 8`; do create-vm -n node$n \ -i ~/isos/ubuntu-18.10-server-amd64.iso \ -k ~/conf/ubuntu.ks done
Delete 8 virtual machines
for n in `seq 1 8`; do delete-vm node$n done
Connect to a VM via the console
virsh console node1
Connect to a VM via ssh
ssh ansible@`get-vm-ip node1`
Generate an Ansible hosts file
( echo '[hosts]' for n in `seq 1 8`; do ip=`get-vm-ip node$n` echo "node$n ansible_ip=$ip ansible_user=ansible" done ) > hosts
Handy virsh commands
virsh list – List all running VMs.
virsh domifaddr node1 – Get a node’s IP address. Does not work with all network setups,which is why I wrote the get-vm-ip script.
virsh net-list – Show what networks were created by virsh.
virsh net-dhcp-leases $network – Shows current DHCP leases when virsh is acting as the DHCP server. Leases may be shown for machines that no longer exist.
Known Issues
VMs created without the -d (debug mode) parameter may be created in “stopped” mode. To start them up, run the command virsh start $vmname
Depending on how your host is set up, you may need to run these scripts as root.
Ubuntu text mode install messes up terminal screens. Run reset from the command line to restore a terminal’s functionality.
I use Ansible to set a guest’s hostname, not Kickstart, so all Ubuntu guests created have the host name “ubuntu”.
# create-vm - Use .iso and kickstart files to auto-generate a VM.
# Copyright 2018 Earl C. Ruby III # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License.
This script will take an .iso file created by revisor and generate a VM from it.
OPTIONS: -h Show this message -n Host name (required) -i Full path and name of the .iso file to use (required) -k Full path and name of the Kickstart file to use (required) -r RAM in MB (defaults to ${RAM}) -c Number of VCPUs (defaults to ${VCPUS}) -s Amount of storage to allocate in GB (defaults to ${STORAGE}) -b Bridge interface to use (defaults to ${BRIDGE}) -m MAC address to use (default is to use a randomly-generated MAC) -v Verbose -d Debug mode EOF }
while getopts "h:n:i:k:r:c:s:b:m:v:d" option; do case "${option}" in h) usage exit 0 ;; n) HOSTNAME=${OPTARG};; i) ISO_FQN=${OPTARG};; k) KS_FQN=${OPTARG};; r) RAM=${OPTARG};; c) VCPUS=${OPTARG};; s) STORAGE=${OPTARG};; b) BRIDGE=${OPTARG};; m) MAC=${OPTARG};; v) VERBOSE=1;; d) DEBUG=1;; esac done
if [[ -z $HOSTNAME ]]; then echo "ERROR: Host name is required" usage exit 1 fi
if [[ -z $ISO_FQN ]]; then echo "ERROR: ISO file name or http url is required" usage exit 1 fi
if [[ -z $KS_FQN ]]; then echo "ERROR: Kickstart file name or http url is required" usage exit 1 fi
if ! [[ -f $ISO_FQN ]]; then echo "ERROR: $ISO_FQN file not found" usage exit 1 fi
if ! [[ -f $KS_FQN ]]; then echo "ERROR: $KS_FQN file not found" usage exit 1 fi KS_FILE=$(basename "$KS_FQN")
if [[ ! -z $VERBOSE ]]; then echo "Building ${HOSTNAME} using MAC ${MAC} on ${BRIDGE}" echo "======================= $KS_FQN =======================" cat "$KS_FQN" echo "==============================================" set -xv fi
# System language
lang en_US
# Language modules to install
langsupport en_US
# System keyboard
keyboard us
# System mouse
mouse
# System timezone
timezone --utc Etc/UTC
# Root password
rootpw --disabled
# Initial user
user ansible --fullname "ansible" --iscrypted --password $6$CfjrLvwGbzSPGq49$t./6zxk9D16P6J/nq2eBVWQ74aGgzKDrQ9LdbTfVA0IrHTQ7rQ8iq61JTE66cUjdIPWY3fN7lGyR4LzrGwnNP.
# Reboot after installation
reboot
# Use text mode install
text
# Install OS instead of upgrade
install
# Use CDROM installation media
cdrom
# System bootloader configuration
bootloader --location=mbr
# Clear the Master Boot Record
zerombr yes
# Partition clearing information
clearpart --all
# Disk partitioning information
part / --fstype ext4 --size 3700 --grow
part swap --size 200
# System authorization infomation
auth --useshadow --enablemd5
# Firewall configuration
firewall --enabled --ssh
# Do not configure the X Window System
skipx
%post --interpreter=/bin/bash
echo ### Redirect output to console
exec < /dev/tty6 > /dev/tty6
chvt 6
echo ### Update all packages
apt-get update
apt-get -y upgrade
# Install packages
apt-get install -y openssh-server vim python
echo ### Enable serial console so virsh can connect to the console
systemctl enable serial-getty@ttyS0.service
systemctl start serial-getty@ttyS0.service
echo ### Add public ssh key for Ansible
mkdir -m0700 -p /home/ansible/.ssh
cat <<EOF >/home/ansible/.ssh/authorized_keys
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC14kgOfzuOA4+hD16JFTAtXWc0UkzvXw3TrPxivh+/t86FH1N1qlXLLZn60voysLHnyiwTXrC8Zy0H8rckRopepozMBRegeklnkLHaiFDBdbAkHm8DMOd5QuGedBZ+s80+05btzOVxZcN5M6m4A03vLGGoOqnJvewv1u/yP6et0hP2Bs6D9ycczOpXeOgKnUXt1rciVYTk9xwOXFWcZ5phnXSCGA1w0BACK2CZaCKmsAT5YR7PA4N+7xVMvhOgo3gt8dNxNtmEtkZSlYYqkJehuldt1IPpfQ5/QYngYKX1ZCKS2LHc9Ys3F8QX3djhOhqFL+kcDMrdTaT5GlAFcgqebIao5mTYpiqc72YbvbCMWRVomhE0TWgliIftR/65NzOf8N4b2fE/hakLkIsGyR7TQiNmAHgagqBX/qdBJ7QJdNN7GH2aGP/Ni7b9xX2jsWXRj8QcSee+IDgfm2k/uKGvI6+RotRVx/EjwOGVUeGp8txP8l4T0AmYsgiL1Phe5swAPaMj4R+m38dwzr2WF/PViI1upF/Weczoiu0dDODLsijdHBAIju9BEDBzcFbDPoLLKHOSMusy86CVGNSEaDZUYwZ2GHY6anfEaRzTJtqRKNvsGiSRJAeKIQrZ16e9QPihcQQQNM+Z9QW7Ppaum8f2QlFMit03UYMplw0EpNPAWQ== ansible@host
EOF
echo ### Set permissions for Ansible directory and key. Since the "Initial user"
echo ### is added *after* %post commands are executed, I use the UID:GID
echo ### as a hack since I know that the first user added will be 1000:1000.
chown -R 1000:1000 /home/ansible
chmod 0600 /home/ansible/.ssh/authorized_keys
echo ### Change back to terminal 1
chvt 1