format_list_bulleted

Sandboxing fragile CUPS network printer server through kernel namespace

dark_mode
Sandboxing fragile CUPS network printer server through kernel namespace
The last defense: cgroup and namespace.
cups
visibility -- public -- group --

For any program running as root on a machine that missing support for MAC/SELinux/AppArmor, follow the idea below.

So that attackers can’t hurt your machine or harm your data on disks while the daemon is facing RCE holes whatever reported or 0-day.

CUPS is such a holy old shit.

Sandboxing

[Unit]
Description=CUPS Scheduler
Documentation=man:cupsd(8)
After=network.target nss-user-lookup.target time-set.target nslcd.service
Requires=cups.socket

[Service]
ExecStart=/usr/bin/cupsd -l
Type=notify
Slice=system-cups.slice
Restart=on-failure
RestartSec=5s

# Make everything read-only, it excludes /proc /sys /dev
ProtectSystem=strict
ProtectKernelTunables=true
ProtectControlGroups=true
NoNewPrivileges=true

PrivateTmp=true
PrivateMounts=true

# Isolate critical devices like hard drives and /dev/mem
PrivateDevices=true

# Allow accessing USB devices - encoded as 189
DeviceAllow=char-189 rwm

# Deny access to them
InaccessiblePaths=/boot /home /media /mnt /root

# Mask /etc/ and make garbage purging with tmpfs
TemporaryFileSystem=/etc /var /var/cache/cups /var/spool/cups

# Keep authentication info
BindReadOnlyPaths=/etc/passwd /etc/shadow /etc/group

# Passthrough devices, CUPS data directories and sockets.
BindPaths=/dev/bus/usb /etc/cups /var/log/cups /run/cups

[Install]
Also=cups.socket cups.path
WantedBy=printer.target multi-user.target

After setting up, debug the namespace environment, adjust and follow your expectation:

nsenter -t $(pidof cupsd) -m -u -i -n /bin/bash

Check what still have to be fixed and adjust your systemd service unit.

Now you can expose it to the Internet or WireGuard, access in the way you want.

Service Discovery

It’s completely unnecessary launching cups-browsed for printer discover, instead you should use modern, safe and mainstream alternatives DNS-SD. Avahi can do it for you.

avahi-publish-service "My InkJet Printer" _ipp._tcp 631 "rp=printers/My_Printer_Name" "qtotal=1" "txtvers=1"

Your printer, or more exactly the entry you advertised via DNS-SD, now should show on your Android native printer service.

Expose printers to network

ServerName YourDomain.net
ServerAlias *
Port 631

# Restrict access to the server...
<Location />
  Order allow,deny

  # Add it!
  Allow from all
</Location>

Then operate your editor, replace all keyword @SYSTEM to some user name lpadmin who have login password set. You can administrate your CUPS server via web interface through this user, it’s HTTP Basic Auth.

Anything is fine, sure you can reverse proxy it through nginx wrap a SSL layer and pass a fixed Host header value to deceive CUPS server.

Everybody who can connect to CUPS can send a printer job, whatever this printer is shared or not.

CUPS client

Set clients up, written in /etc/cups/client.conf:

ServerName 192.168.1.1:631

You don’t need running CUPS services on your client machines: shut it down. Just write into the config file and force them to connect to the target CUPS server.

Now, print a test page.