Closing the digital divide with Compudopt and Ubuntu Desktop

This article is more than 1 year old.


Compudopt is a US based organisation with an inspiring mission; to provide technology access and education to under-resourced youth and their communities. This goal speaks to the heart of Ubuntu’s own mission, to bring free software to the widest audience.

When Compudopt reached out to the Ubuntu Desktop and Landscape teams here at Canonical for advice on how to use Ubuntu Desktop to breathe new life into refurbished devices we were excited to help!

In this spotlight, Thijs van de Kamp, National Director of Technical Operations at Compudopt, talks us through how he used a combination of iPXE and Subiquity, the Ubuntu Server installer, to deploy and configure custom desktop images and enroll them in Landscape. He also takes us through some of his specific configurations as a reference for other users looking to do the same.

In Ubuntu 23.04, Subiquity will become the default installer for Ubuntu Desktop, check out the end of this post for more information on this exciting change.

But now, over to Thijs!

About Compudopt

My name is Thijs van de Kamp, and I am the National Director of Technical Operations at Compudopt. Compudopt is a non-profit organisation dedicated to providing technology access and education to under-resourced youth and their communities. Our programs aim to eliminate limited access to computers, facilitate growth in technical and digital literacy skills, help provide no or low cost high-speed internet options, and support the future of youth and their communities. As the National Director of Technical Operations, I am proud to be part of this mission and to help bridge the digital divide for those in need.

Why Ubuntu

We use Ubuntu for the machines that we distribute to the community; it is a robust, free solution that gives an excellent experience to our end-users.

Ubuntu’s security features are one of its biggest benefits as an open-source solution. The operating system’s architecture is designed to prioritise security, with regular updates and patches released to address any vulnerabilities that are discovered. These updates are also available to users free of charge, ensuring that their devices remain secure without any added financial burden.

Furthermore, Ubuntu’s open-source nature means that its security features are constantly being scrutinised and improved by developers all around the world, making it a constantly-evolving and reliable solution for security-conscious individuals.

Overall, Ubuntu’s status as an open-source operating system provides numerous benefits and conveniences, from its cost-effectiveness and user-friendly interface to its security features and continuous development.

These advantages make Ubuntu an excellent choice for Compudopt to provide reliable, affordable, and secure devices to the communities we serve.

The challenge

Compudopt covers multiple locations across the United States, and has provisioned and donated tens of thousands of laptops since it was founded in 2007.

At Compudopt, we strive to ensure that every individual who receives one of our devices has an identical experience, which includes access to comprehensive support.

To achieve this goal, we employ a standardised installation process across multiple sites, which enables our support staff to have a clear understanding of the device’s capabilities. However, creating a consistent installation process poses a significant challenge.

Fortunately, by utilising Subiquity, we can customise the device’s image according to our preferences using a single YAML file. 

Additionally, Landscape, a helpful tool for support, enables us to remotely assist device recipients with tasks such as password resets, eliminating the need for them to visit one of our local sites or mail the device to us for support.

One of the major challenges in creating a standardised installation process is ensuring that all necessary drivers are included. Fortunately, Ubuntu’s extensive device support means that the deployment process is quicker and more efficient than other available options.

The solution

At our organisation, each location has a single bare-metal iPXE server that we manually configure to serve installations. However, we plan to streamline this process by using GitHub to ensure that each site has the most up-to-date configuration. Our workbenches are equipped with switches that can deploy software packages, including the Ubuntu installation, from the iPXE server.

To ensure the security and availability of our Landscape data, we currently host it on Google Cloud. One of the most valuable features of this setup is the ability to take snapshots of the VM, which enhances reliability and minimises potential data loss.

iPXE Ubuntu Desktop installation configuration in detail

The deployment we created leverages iPXE and starts from a base Ubuntu Server ISO. 

The steps in this guide are designed to accomplish the following:

  • Serve a custom package in a PPA to be installed during deployment.
  • Install Ubuntu Desktop.
  • Set a custom background.
  • Create a hidden administrator account for tech support purposes.
  • Install applications for the end users.
  • Address network issues turning the Server ISO to Desktop installation.
  • Use a proxy cache server to minimise the WAN load during installation.

Prerequisites

  • tftpd-hpa
  • apache2
  • squid-deb-proxy

Server Configuration

TFTPD-HPA

We need to make sure that the TFTP server knows where the .ipxe files are located. You can set this in the file located at “/etc/default/tftpd-hpa”. This is where you can point to the folder and set several options, we will give a quick explanation of the options we used.

TFTP_OPTIONS

-c,allow for new files to be created.

--secure, change root directory on startup. This means the remote host does not need to pass along the directory as part of the transfer, and increases security

--listen, run the server in standalone (listen) mode, rather than run from inetd. In listen mode, the `–timeout` option is ignored, and the `–address` option can be used to specify a specific local address or port to listen to.

-v, increase the logging verbosity of tftpd. This flag can be specified multiple times for even higher verbosity

The live version of our tftpd-hpa looks like this:

# /etc/default/tftpd-hpa

TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/pxe-boot"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="-c --secure --listen -v"
OPTIONS="-l -s -r"

Apache2

During the installation we use a web server, for our purposes we chose Apache2. The file that needs to be modified is “/etc/apache2/sites-available/000-default.conf”.

Apache2 Options

DocumentRoot /pxe-boot, this can be any directory, we choose to host the files in the same folder for a few different reasons.

Require all granted, this is to make sure we have full access to the files during installation.

<VirtualHost *:80>
#       ServerAdmin some@gmail.com
        DocumentRoot /pxe-boot

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

<Directory />
        Options +FollowSymLinks +Indexes
        Require all granted
</Directory>

Squid-deb-proxy

We are using squid-deb-proxy to cache all the packages that we install, this is to make sure we do not have a WAN bottleneck and in case we lose our connection we can still complete our deployments. The files we need to modify are located at “/etc/squid-deb-proxy/squid-deb-proxy.conf”, we also need to create a file at “/etc/squid-deb-proxy/mirror-dstdomain.acl.d/10-default”.

The file squid-deb-proxy.conf is a large file, we have included the important parts below:

# allow access only to official archive mirrors
# uncomment the third and fouth line to permit any unlisted domain
#http_access deny !to_archive_mirrors
#http_access allow !to_archive_mirrors

# don't cache domains not listed in the mirrors file
# uncomment the third and fourth line to cache any unlisted domains
cache deny !to_archive_mirrors
#cache allow !to_archive_mirrors

# allow access from our network and localhost
http_access allow allowed_networks

# And finally deny all other access to this proxy
http_access deny all

To enable the relevant sources and store the contents of the “10-default” file we use:

# /etc/squid-deb-proxy/mirror-dstdomain.acl.d/10-default
# 
# network destinations that are allowed by this cache

# launchpad personal package archives (disabled by default)
ppa.launchpad.net
ppa.launchpadcontent.net
#private-ppa.launchpad.net

# add additional mirror domains here (disabled by default)
#linux.dropbox.com
#download.virtualbox.org
#archive.getdeb.net
#packages.medibuntu.org
dl.google.com
#repo.steampowered.com
download.anydesk.com
boot.linuxgroove.com
geoip.ubuntu.com

Configuring the iPXE boot menu

The iPXE boot menu file should be located in the TFTP root directory. We use more options for the actual deployment, however for this example we are focussing on the Ubuntu installation. The iPXE menu file should look like this, we will explain in more detail below.

#!ipxe
:MENU
menu
item --gap -- ----------------Operating System Installation ------------------
item ubuntu	Ubuntu 22.04 LTS Installation
item --gap -- ----------------iPXE Shell -------------------------------------
item shell	iPXE Shell
choose --default return target && goto ${target}

:ubuntu

set base-url http://192.168.1.107/ubuntu/22.04
kernel ${base-url}/casper/vmlinuz
initrd ${base-url}/casper/initrd
imgargs vmlinuz initrd=initrd \
 ip=dhcp \
 url=${base-url}//${baseurl}install.iso \
 cloud-config-url=/dev/null\
 autoinstall \
 ds=nocloud-net;s=http://192.168.1.107/ubuntu/22.04/jammy/ \
boot
goto MENU

:shell
shell ||
goto MENU

We are setting the base-url for the installation first, this is where we keep the files that are important for the installer hosted by the Apache2 server that we configured earlier.

vmlinuzand initrd are located in the casper folder. You can mount the ISO to the folder, by adding this to fstab you can ensure that these are always available upon reboot. For example the fstab entry can look like this

/pxe-boot/ubuntu/22.04/install.iso /pxe-boot/ubuntu/22.04/iso iso9660 loop,ro,auto 0 0"

`imgargs lets you use arguments for the kernel:

This will let the machine get an ip address from the DHCP server:

ip=dhcp

The file `install.iso is the Ubuntu 22.04 LTS Server ISO, this will let the installer know where it is located.

url=${base-url}//${baseurl}install.iso

This command prevents the iso from being loaded twice and makes it possible to load on a machine with 4GB of RAM.

cloud-config-url=/dev/null

This is indicating it is an autoinstall, it is a bit redundant since it is also located in the user-data.

Autoinstall

This is letting Subiquity know where the “user-data” and “meta-data” are located.

ds=nocloud-net;s=http://192.168.1.107/ubuntu/22.04/jammy/

Autoinstall configuration

The user-data we are using looks like this:

#cloud-config
autoinstall:
  identity:
    hostname: jammy-desktop
    password: $6$5lpwCLsKLEzMkSJc$keOAhA6aO/5RocGhmhVA7LSNuW911Rx5HHXFEa75oGK20cEdAAgn14H5f5nGeq6QgcSyLPrWcg1.JvjXbhrN/
    realname: Ubuntu user
    username: ubuntu
  keyboard:
    layout: us
    toggle: null
    variant: ''
  locale: en_US.UTF-8
  proxy: "http://192.168.1.107:8000"
  apt:
    preserve_sources_list: true
    sources:
      compudopt-ppa:
        source: ppa:compudopt/compudopt
  packages:
  - compudopt-overrides
  - ubuntu-desktop^
  - plymouth-theme-ubuntu-logo
  - grub-gfxpayload-lists
  - systemd-hwe-hwdb
  - python3-magic
  - gimp
  - ubuntu-restricted-addons
  - ubuntu-restricted-extras
  - landscape-client
  kernel:
    package: linux-generic-hwe-22.04
  storage:
    layout:
      name: direct
  ssh:
    allow-pw: true
    authorized-keys: []
    install-server: false
  updates: all
  timezone: America/Chicago
  late-commands:
  - 'sed -i.bak -e "s|#WaylandEnable=false$|DefaultSession=ubuntu-xorg.desktop|g" /target/etc/gdm3/custom.conf'
  - 'echo "%sudo ALL=(ALL) NOPASSWD:ALL" > /target/etc/sudoers.d/nopw'
  - chmod 440 /target/etc/sudoers.d/nopw
  - curtin in-target --target=/target -- sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT=""/GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"/' /etc/default/grub
  - curtin in-target --target=/target -- update-grub
  - curtin in-target --target=/target -- wget -O /tmp/google-chrome-stable_current_amd64.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
  - curtin in-target --target=/target -- apt install /tmp/google-chrome-stable_current_amd64.deb -y
  - curtin in-target --target=/target -- wget -O /tmp/anydesk.deb https://download.anydesk.com/linux/anydesk_6.2.1-1_amd64.deb
  - curtin in-target --target=/target -- apt install /tmp/anydesk.deb -y
  - curtin in-target --target=/target -- apt install scratch -y
  - curtin in-target --target=/target -- apt remove byobu -y
  - curtin in-target --target=/target -- wget -O /sbin/reset.sh http://boot.linuxgroove.com/ubuntu/reset.sh
  - chmod +x /target/sbin/reset.sh
  - curtin in-target --target=/target -- useradd -m -r -s /bin/bash admin
  - 'echo "admin:password" | chpasswd -R /target'
  - curtin in-target --target=/target -- usermod -aG sudo admin
  - touch /target/var/lib/AccountsService/users/admin
  - 'echo "[User]" >> /target/var/lib/AccountsService/users/admin'
  - 'echo "SystemAccount=true" >> /target/var/lib/AccountsService/users/admin'
  - 'rm -f /target/etc/netplan/*.yaml'
  - 'echo "# Let NetworkManager manage all devices on this system" > /target/etc/netplan/01-network-manager-all.yaml'
  - 'echo "network:" >> /target/etc/netplan/01-network-manager-all.yaml'
  - 'echo "  version: 2" >> /target/etc/netplan/01-network-manager-all.yaml'
  - 'echo "  renderer: NetworkManager" >> /target/etc/netplan/01-network-manager-all.yaml'
  - curtin in-target --target=/target -- wget -O /sbin/reset.sh http://192.168.1.107/ubuntu/22.04/scripts/reset.sh
  - chmod +x /target/sbin/reset.sh
  - curtin in-target --target=/target -- wget -O /home/snaps.tar http://192.168.1.107/ubuntu/22.04/snaps/snaps.tar
  - curtin in-target --target=/target -- wget -O /home/snaps.sh http://192.168.1.107/ubuntu/22.04/scripts/snaps.sh
  - chmod +x /target/home/snaps.sh
  - curtin in-target --target=/target -- wget -O /etc/rc.local http://192.168.1.107/ubuntu/22.04/scripts/rc.local
  - chmod +x /target/etc/rc.local
  - 'mkdir -p /target/home/ubuntu/.local/share/applications'
  - 'cp /target/usr/share/applications/provider-setup.desktop /target/home/ubuntu/.local/share/applications/provider-setup.desktop'
  - 'sed -i.bak -e "s|NoDisplay=true$||g" /target/home/ubuntu/.local/share/applications/provider-setup.desktop'
  - 'mkdir -p /target/home/ubuntu/.config/autostart'
  - 'touch /target/home/ubuntu/.config/gnome-initial-setup-done'
  - 'cp /target/usr/share/applications/provider-setup.desktop /target/home/ubuntu/.config/autostart/'
  - 'echo "X-GNOME-Autostart-enabled=true" >> /target/home/ubuntu/.config/autostart/provider-setup.desktop'
  - curtin in-target --target=/target -- chown -R 1000:1000 /home/ubuntu
  version: 1

Let’s break this down in more detail:

This is letting Subiquity know that it is an autoinstall.

autoinstall

We set up squid-deb-proxy earlier, this is letting Subiquity know to use this proxy.

proxy: “http://192.168.1.107:8000”

We are using custom .deb package hosting on our ppa. This installs the desktop background and an application created to register our machines with Landscape and gather system information for our administrators.

apt:
    preserve_sources_list: true
    sources:
      compudopt-ppa:
        source: ppa:compudopt/compudopt

This is where we indicate which packages will be installed. We had to add certain packages for the desktop experience since we are starting from the server ISO.

packages:
  - compudopt-overrides
  - ubuntu-desktop^
  - plymouth-theme-ubuntu-logo
  - grub-gfxpayload-lists
  - systemd-hwe-hwdb
  - python3-magic
  - gimp
  - ubuntu-restricted-addons
  - ubuntu-restricted-extras
  - landscape-client

We are using several late commands on our installation, so this is a great time to do some additional configuration.

We disabled Wayland so we are able to use AnyDesk for our support team. Once AnyDesk picks up support for Wayland we will enable it.

  - 'sed -i.bak -e "s|#WaylandEnable=false$|DefaultSession=ubuntu-xorg.desktop|g" /target/etc/gdm3/custom.conf'

This is where we create the hidden admin account for our tech support, by making setting `SystemAccount=true it will not show up on the login screen.

 - curtin in-target --target=/target -- useradd -m -r -s /bin/bash admin
  - 'echo "admin:password" | chpasswd -R /target'
  - curtin in-target --target=/target -- usermod -aG sudo admin
  - touch /target/var/lib/AccountsService/users/admin
  - 'echo "[User]" >> /target/var/lib/AccountsService/users/admin'
  - 'echo "SystemAccount=true" >> /target/var/lib/AccountsService/users/admin'

Here we are updating grub, installing Chrome, AnyDesk and Scratch. This way we get the most up-to-date versions, if there are connection issues this will still work since we are using the squid-deb-proxy that it can always fall back to.

  - curtin in-target --target=/target -- update-grub
  - curtin in-target --target=/target -- wget -O /tmp/google-chrome-stable_current_amd64.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
  - curtin in-target --target=/target -- apt install /tmp/google-chrome-stable_current_amd64.deb -y
  - curtin in-target --target=/target -- wget -O /tmp/anydesk.deb https://download.anydesk.com/linux/anydesk_6.2.1-1_amd64.deb
  - curtin in-target --target=/target -- apt install /tmp/anydesk.deb -y
  - curtin in-target --target=/target -- apt install scratch -y
  - curtin in-target --target=/target -- apt remove byobu -y

The Ubuntu Server does not use network-manager so when changing to desktop during installation you will get an error message. This makes sure that the network manager is functioning as needed in Ubuntu Desktop.

 - 'rm -f /target/etc/netplan/*.yaml'
  - 'echo "# Let NetworkManager manage all devices on this system" > /target/etc/netplan/01-network-manager-all.yaml'
  - 'echo "network:" >> /target/etc/netplan/01-network-manager-all.yaml'
  - 'echo "  version: 2" >> /target/etc/netplan/01-network-manager-all.yaml'
  - 'echo "  renderer: NetworkManager" >> /target/etc/netplan/01-network-manager-all.yaml'

We are running a cron job on the server that downloads the updated snaps for our installation and puts them in a tar file. This tar then gets installed onto the server. As a result we are not using WAN during installation.

  - curtin in-target --target=/target -- wget -O /home/snaps.tar http://192.168.1.107/ubuntu/22.04/snaps/snaps.tar

The script we use is located on the server side and we have it set up as a weekly cron job. It looks like this:

#!/bin/bash

old=$PWD
echo $old

cd `dirname $0`
for i in gnome-42-2204 gnome-3-38-2004 firefox snapd-desktop-integration snap-store zoom-client vlc core18 chromium gtk-common-themes; do
        snap download --channel=stable $i
done

cd ..

tar cvf snaps.tar snaps
if [ $? -eq 0 ]; then
	echo "snaps.tar archive created"
	rm snaps/*.assert snaps/*.snap
fi

cd $old

The rc.local script cleans the snaps and restarts NetworkManager so the internet connection works on boot. 

  - curtin in-target --target=/target -- wget -O /etc/rc.local http://192.168.1.107/ubuntu/22.04/scripts/rc.local
  - chmod +x /target/etc/rc.local

The script looks like this:

#!/bin/bash

/home/snaps.sh

if [ $? -eq 0 ]; then
	rm -rf /home/snap*
	rm /etc/rc.local
	nmcli n off
	sleep 2
	nmcli n on
fi

Finally, in our setup we are using a Ubiquiti Dream Machine Pro, you can point to a DHCP Boot and TFTP server in the web GUI.

Signing off!

I want to express my heartfelt gratitude to both Ken VanDine, Engineering Manager for Ubuntu Desktop, and Rajan Patel, Product Manager for Landscape, for their exceptional support throughout this entire process. Their expertise and assistance have been invaluable in making this a seamless and smooth experience from start to finish. Along the way, we learned a great deal about the intricate details of Ubuntu deployment and Landscape integration, thanks to their guidance and knowledge.

Now back to Oliver!

Further Reading & Resources

We hope you enjoyed this insight into Compudopt’s mission and their experience deploying Ubuntu Desktop. In this example the team started from an Ubuntu Server ISO and leveraged Subiquity to transform it into a full Desktop environment. However, from Ubuntu 23.04 onwards, Subiquity will also become the default installer for Ubuntu Desktop. This means that some of the steps above will be significantly streamlined as necessary desktop packages will already be present. To find out more about Landscape, Subiquity and how to leverage autoinstall on Ubuntu Desktop 23.04, check out the links below.

To learn more about Compudopt and their mission, don’t forget to check out Compudopt.org.

If your organisation is looking to adopt Ubuntu Desktop and you’d like to find out more about how Canonical can help you get started, please:

Get in touch!

All photos courtesy of Compudopt National.

Ubuntu desktop

Learn how the Ubuntu desktop operating system powers millions of PCs and laptops around the world.

Newsletter signup

Get the latest Ubuntu news and updates in your inbox.

By submitting this form, I confirm that I have read and agree to Canonical's Privacy Policy.

Related posts

Canonical releases Landscape 24.04 LTS

Landscape 24.04 LTS is Landscape’s first LTS release, with a modernised backend, web portal, snap management, and repository management features.

Entra ID authentication on Ubuntu at scale with Landscape

Authd allows Entra ID authentication on both Ubuntu Desktop and Server. Learn how to configure Authd at scale using Landscape and Cloud-init

Imagining the future of Cybersecurity

October 2024 marks the 20th anniversary of Ubuntu. The cybersecurity landscape has significantly shifted since 2004. If you have been following the Ubuntu...