Debian Container for Isolating Close-source Printer Drivers

Main idea:

  • Put the close-source printer drivers in a Debian container with CUPS installed
  • The main system will communicate with the container via CUPS network printing

Modification since the last version:

  • Stop using systemd-nspawn that is a part of the systemd bloatware.
  1. Install relevant packages:

    1
    2
    sudo pacman -S debootstrap debian-archive-keyring
    sudo pacman -S lxc dnsmasq
  2. Create the Debian container:

    1
    sudo lxc-create -n debian-printer -t download -- -d debian -r bookworm -a amd64
  3. Set up network interface:

    1
    2
    3
    # /etc/default/lxc-net

    USE_LXC_BRIDGE="true"
    1
    2
    3
    4
    5
    6
    7
    # First enable the service
    sudo systemctl start lxc-net
    sudo systemctl enable lxc-net

    # Or
    sudo rc-service lxc-net start
    sudo rc-update add lxc-net default

Now, check that lxcbr0 is up and running:

1
ip addr show lxcbr0
1
2
# /etc/lxc/dnsmasq.conf
dhcp-host=playtime,10.0.3.50

Then set the IP address at /var/lib/lxc/debian-printer/config

  1. Starting the container:
1
2
sudo lxc-start -n debian-printer
sudo lxc-attach -n debian-printer
  1. Install CUPS
    First, enter the container’s shell:

    1
    sudo lxc-attach -n debian-printer

    Temporarily set up the routing table:

    1
    2
    3
    4
    ip link set eth0 up
    ip addr add 10.0.3.50/24 dev eth0
    ip route add default via 10.0.3.1
    printf 'nameserver 1.1.1.1\nnameserver 8.8.8.8\n' > /etc/resolv.conf

    Then, inside the container, install CUPS:

    1
    2
    3
    4
    5
    6
    7
    apt update
    apt install cups usbutils
    systemctl enable cups
    systemctl start cups
    dpkg --add-architecture i386
    apt update
    apt install libc6:i386 libstdc++6:i386
  2. Install the close-source printer drivers:
    On the host:

    1
    sudo cp hl3045*.deb /var/lib/lxc/debian-printer/rootfs/root/

    Then, inside the container:

    1
    dpkg -i hl3045cnlpr-1.1.2-1.i386.deb hl3045cncupswrapper-1.1.2-2.i386.deb
  3. Create a stable syslink for the printer on the host (change the idVendor and idProduct values based on lsusb output):

    1
    2
    # Find the correct info
    udevadm info -a -n /dev/usb/lp0
    1
    2
    # /etc/udev/rules.d/99-brother-hl3045cn.rules
    SUBSYSTEM=="usbmisc", KERNEL=="lp*", ATTRS{idVendor}=="04f9", ATTRS{idProduct}=="004e", SYMLINK+="usb/brother_hl3045cn"
  4. Configure device passthrough

    1
    2
    3
    4
    5
    6
    7
    8
    # /var/lib/lxc/debian-printer/config

    # Allow usblp printer character devices.
    # /dev/usb/lp0 is usually major 180.
    lxc.cgroup2.devices.allow = c 180:* rwm

    # bind only the stable printer path to /dev/usb/lp0:
    lxc.mount.entry = /dev/usb/brother_hl3045cn dev/usb/lp0 none bind,create=file 0 0
  5. Configure CUPS on the subsystem:

    1
    2
    3
    4
    5
    6
    7
    8
    # /etc/cups/cupsd.conf
    ...
    Listen 0.0.0.0:631
    ...
    # Then in each of the <Location> sections, add:
    Allow 192.168.50.1
    ...
    # FileDevice Yes
    1
    2
    3
    4
    systemctl restart cups
    lpadmin -p HL3045CN -E \
    -v file:///dev/usb/lp0 \
    -P /opt/brother/Printers/hl3045cn/cupswrapper/brother_hl3045cn_printer_en.ppd
    1
    2
    # /etc/cups/cups-files.conf
    FileDevice yes
  6. Configuring auto-start of the container:

    1
    2
    3
    # /var/lib/lxc/debian-printer/config
    lxc.start.auto = 1
    lxc.start.delay = 5

Info for non-usblp printers

Use USBIP to forward the whole USB device to the container.
The container’s virtual bus will be visible on the host as a new USB device, but it should not interfere with the host’s USB subsystem.

Appendix: Using systemd-nspawn

Deprecated: I no longer use bloatware

  1. Install relevant packages:

    1
    sudo pacman -S debootstrap debian-archive-keyring
  2. Create the Debian container:

    1
    2
    3
    4
    5
    6
    sudo mkdir -p /var/lib/machines/debian-printer
    sudo debootstrap \
    --arch=amd64 \
    --include=dbus,libpam-systemd \
    stable \
    /var/lib/machines/debian-printer

    Remember to set the password for the root user in the container.

  3. Install CUPS
    First, enter the container’s shell:

    1
    sudo systemd-nspawn -D /var/lib/machines/debian-printer

    Then, inside the container, install CUPS:

    1
    2
    3
    4
    5
    apt update
    apt install cups usbutils
    systemctl enable cups
    systemctl start cups
    apt install libc6:i386 libstdc++6:i386
  4. Install the close-source printer drivers:
    On the host:

    1
    sudo cp hl3045*.deb /var/lib/machines/debian-printer/root/

    Then, inside the container:

    1
    2
    3
    dpkg --add-architecture i386
    apt update
    dkpg -i hl3045cnlpr-1.1.2-1.i386.deb hl3045cncupswrapper-1.1.2-2.i386.deb
  5. Create a stable syslink for the printer on the host (change the idVendor and idProduct values based on lsusb output):

    1
    2
    # Find the correct info
    udevadm info -a -n /dev/usb/lp0
    1
    2
    # /etc/udev/rules.d/99-brother-hl3045cn.rules
    SUBSYSTEM=="usbmisc", KERNEL=="lp*", ATTRS{idVendor}=="04f9", ATTRS{idProduct}=="004e", SYMLINK+="usb/brother_hl3045cn"
  6. Configure the network for the host:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # /etc/systemd/network/70-printer-veth.network
    [Match]
    Kind=veth
    Name=ve-debian-p*

    [Link]
    RequiredForOnline=no

    [Network]
    # Default to using a /28 prefix, giving up to 13 addresses per container.
    Address=192.168.50.1/30
    LinkLocalAddressing=no
    DHCPServer=no
    IPv6AcceptRA=no
    IPv6SendRA=no
    1
    2
    sudo systemctl enable systemd-networkd
    sudo systemctl restart systemd-networkd
  7. Configure the network for the container:

    1
    2
    3
    4
    5
    sudo systemd-nspawn \
    -D /var/lib/machines/debian-printer \
    --network-veth \
    --bind=/dev/usb/brother_hl3045cn:/dev/usb/lp0 \
    --boot
    1
    2
    3
    4
    5
    6
    7
    8
    # /etc/systemd/network/70-container-ve.network
    [Match]
    Name=host0

    [Network]
    Address=192.168.50.2/30
    LinkLocalAddressing=no
    IPv6AcceptRA=no
    1
    2
    sudo systemctl enable systemd-networkd
    sudo systemctl restart systemd-networkd

    Check both machines with ip addr.

  8. Configure CUPS on the subsystem:

    1
    2
    3
    4
    5
    6
    7
    8
    # /etc/cups/cupsd.conf
    ...
    Listen 0.0.0.0:631
    ...
    # Then in each of the <Location> sections, add:
    Allow 192.168.50.1
    ...
    FileDevice Yes
    1
    2
    3
    4
    systemctl restart cups
    lpadmin -p HL3045CN -E \
    -v file:///dev/usb/lp0 \
    -P /opt/brother/Printers/hl3045cn/cupswrapper/brother_hl3045cn_printer_en.ppd
  9. Create nspawn service for the container:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # /etc/systemd/nspawn/debian-printer.nspawn
    [Exec]
    PrivateUsers=yes
    Capability=
    Boot=yes

    [Network]
    Private=yes

    [Files]
    Bind=/dev/usb/brother_hl3045cn:/dev/usb/lp0
  10. Create the printer on the host:

    1
    2
    3
    4
    lpadmin -p brother \
    -E \
    -v ipp://192.168.50.2:631/printers/HL3045CN \
    -m everywhere

Then update config on the host:

1
2
3
4
5
6
#/etc/systemd/system/[email protected]/override.conf
[Service]
DevicePolicy=closed
DeviceAllow=/dev/usb/brother_hl3045cn rwm
DeviceAllow=/dev/usb/lp0 rwm

1
2
sudo systemctl daemon-reload
sudo machinectl restart debian-printer

Purging the container:

1
2
3
4
5
sudo machinectl stop debian-printer
sudo systemctl disable --now [email protected]
sudo rm -f /etc/systemd/nspawn/debian-printer.nspawn
sudo rm -rf /etc/systemd/system/[email protected]
sudo rm -rf /var/lib/machines/debian-printer