From 1c7634f2383ce04ab56dae22abd8bfb0f57fe1e2 Mon Sep 17 00:00:00 2001 From: NoudAndi Date: Mon, 23 Mar 2026 19:59:21 +0100 Subject: [PATCH 1/2] Enhance CUPS add-on: secure admin interface, update installation instructions, and improve configuration handling --- README.md | 17 +++++-- cups/Dockerfile | 9 ++-- cups/config.yaml | 8 +-- cups/rootfs/etc/cont-init.d/cups.sh | 79 +++++++++++++++++++++++++---- cups/rootfs/etc/services.d/cups/run | 4 ++ 5 files changed, 91 insertions(+), 26 deletions(-) create mode 100755 cups/rootfs/etc/services.d/cups/run diff --git a/README.md b/README.md index 54ee59a..89352e7 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ This Home Assistant add-on provides a CUPS (Common Unix Printing System) print s - **Network Printing**: Share printers across your local network using CUPS - **Web Interface**: Access the CUPS administration panel at `http://:631` to add and manage printers -- **Secure Administration**: Optional authentication for the CUPS admin interface +- **Secure Administration**: Authenticated CUPS admin interface with add-on credentials - **Printer Support**: Compatible with a wide range of network and USB printers - **Lightweight**: Built on Alpine Linux for minimal resource usage - **Data Persistence**: Printer settings and configurations persist across restarts and updates @@ -40,12 +40,12 @@ If you prefer to manually install: 2. Copy the repository to your Home Assistant add-ons directory: ```bash - scp -r cups-addon/cups root@:/addons/ + scp -r cups-addon/cups root@:/addons/local/ ``` 3. In Home Assistant, go to **Settings** → **Add-ons** → **Add-on Store**. 4. Click the 3-dot menu (top right) → **Repositories**. -5. Add `/addons` as a repository URL and click **Add**. +5. Add `local_addons` as a repository URL and click **Add**. 6. Refresh the add-on store to see "CUPS Print Server." 7. Install the add-on. @@ -58,8 +58,15 @@ admin_username: printadmin admin_password: your_secure_password ``` -- **admin_username**: Username for the CUPS admin interface (default: printadmin) -- **admin_password**: Password for the CUPS admin interface +- **admin_username**: Username for the CUPS admin interface (required) +- **admin_password**: Password for the CUPS admin interface (required) + +Printing from LAN clients remains open, while CUPS administration requires the configured credentials. + +## Networking Notes + +This add-on uses host networking so mDNS/Bonjour announcements are visible on the LAN. +This is required for AirPrint discovery by iOS/macOS clients. After configuring: diff --git a/cups/Dockerfile b/cups/Dockerfile index 9be700c..eda3404 100644 --- a/cups/Dockerfile +++ b/cups/Dockerfile @@ -12,8 +12,8 @@ ENV CUPS_SERVERROOT=/data/cups/config # Setup base RUN \ - echo "http://dl-cdn.alpinelinux.org/alpine/v$(cat /etc/alpine-release | cut -d'.' -f1,2)/main" > /etc/apk/repositories && \ - echo "http://dl-cdn.alpinelinux.org/alpine/v$(cat /etc/alpine-release | cut -d'.' -f1,2)/community" >> /etc/apk/repositories && \ + echo "https://dl-cdn.alpinelinux.org/alpine/v$(cat /etc/alpine-release | cut -d'.' -f1,2)/main" > /etc/apk/repositories && \ + echo "https://dl-cdn.alpinelinux.org/alpine/v$(cat /etc/alpine-release | cut -d'.' -f1,2)/community" >> /etc/apk/repositories && \ apk update && \ apk add --no-cache \ cups \ @@ -26,8 +26,5 @@ RUN \ # Copy data COPY rootfs / -# Expose CUPS web interface port -EXPOSE 631 - HEALTHCHECK \ - CMD lpstat -r || exit 1 \ No newline at end of file + CMD lpstat -r || exit 1 diff --git a/cups/config.yaml b/cups/config.yaml index a1da56a..d298f97 100644 --- a/cups/config.yaml +++ b/cups/config.yaml @@ -17,13 +17,9 @@ map: - data:rw - share:rw init: false -ports: - 631/tcp: 631 -ports_description: - 631/tcp: CUPS web interface and printing port options: - admin_username: "admin" - admin_password: "admin" + admin_username: "printadmin" + admin_password: "change_me_now" schema: admin_username: str admin_password: password diff --git a/cups/rootfs/etc/cont-init.d/cups.sh b/cups/rootfs/etc/cont-init.d/cups.sh index 6b82286..7b0605b 100755 --- a/cups/rootfs/etc/cont-init.d/cups.sh +++ b/cups/rootfs/etc/cont-init.d/cups.sh @@ -1,4 +1,25 @@ #!/usr/bin/with-contenv bash +set -euo pipefail + +source /usr/lib/bashio/bashio.sh + +ADMIN_USER="$(bashio::config 'admin_username')" +ADMIN_PASSWORD="$(bashio::config 'admin_password')" + +if [[ -z "${ADMIN_USER}" ]]; then + bashio::log.fatal "Configuration admin_username must not be empty" + exit 1 +fi + +if [[ -z "${ADMIN_PASSWORD}" ]]; then + bashio::log.fatal "Configuration admin_password must not be empty" + exit 1 +fi + +if [[ "${ADMIN_PASSWORD}" == "change_me_now" ]]; then + bashio::log.fatal "Set a strong admin_password in add-on configuration before starting" + exit 1 +fi # Create CUPS data directories for persistence mkdir -p /data/cups/cache @@ -15,8 +36,9 @@ chmod -R 775 /data/cups # Create CUPS configuration directory if it doesn't exist mkdir -p /etc/cups -# Basic CUPS configuration without admin authentication -cat > /data/cups/config/cupsd.conf << EOL +# Write default configuration only on first run to avoid clobbering user changes. +if [[ ! -f /data/cups/config/cupsd.conf ]]; then + cat > /data/cups/config/cupsd.conf << EOL # Listen on all interfaces Listen 0.0.0.0:631 @@ -29,8 +51,10 @@ Listen 0.0.0.0:631 Allow 192.168.0.0/16 -# Admin access (no authentication) +# Admin access requires authentication + AuthType Basic + Require user @SYSTEM Order allow,deny Allow localhost Allow 10.0.0.0/8 @@ -38,7 +62,27 @@ Listen 0.0.0.0:631 Allow 192.168.0.0/16 -# Job management permissions + + AuthType Basic + Require user @SYSTEM + Order allow,deny + Allow localhost + Allow 10.0.0.0/8 + Allow 172.16.0.0/12 + Allow 192.168.0.0/16 + + + + AuthType Basic + Require user @SYSTEM + Order allow,deny + Allow localhost + Allow 10.0.0.0/8 + Allow 172.16.0.0/12 + Allow 192.168.0.0/16 + + +# Job listing remains LAN-accessible Order allow,deny Allow localhost @@ -47,7 +91,19 @@ Listen 0.0.0.0:631 Allow 192.168.0.0/16 - +# Printing remains LAN-accessible + + Order allow,deny + Allow localhost + Allow 10.0.0.0/8 + Allow 172.16.0.0/12 + Allow 192.168.0.0/16 + + +# Admin-level operations require authenticated system user + + AuthType Basic + Require user @SYSTEM Order allow,deny Allow localhost Allow 10.0.0.0/8 @@ -59,16 +115,21 @@ Listen 0.0.0.0:631 WebInterface Yes # Default settings -DefaultAuthType None +DefaultAuthType Basic JobSheets none,none PreserveJobHistory No EOL +fi + +if ! id -u "${ADMIN_USER}" >/dev/null 2>&1; then + adduser -D -H -s /sbin/nologin "${ADMIN_USER}" +fi + +addgroup "${ADMIN_USER}" lpadmin >/dev/null 2>&1 || true +printf '%s:%s\n' "${ADMIN_USER}" "${ADMIN_PASSWORD}" | chpasswd # Create a symlink from the default config location to our persistent location ln -sf /data/cups/config/cupsd.conf /etc/cups/cupsd.conf ln -sf /data/cups/config/printers.conf /etc/cups/printers.conf ln -sf /data/cups/config/ppd /etc/cups/ppd ln -sf /data/cups/config/ssl /etc/cups/ssl - -# Start CUPS service -/usr/sbin/cupsd -f \ No newline at end of file diff --git a/cups/rootfs/etc/services.d/cups/run b/cups/rootfs/etc/services.d/cups/run new file mode 100755 index 0000000..3235449 --- /dev/null +++ b/cups/rootfs/etc/services.d/cups/run @@ -0,0 +1,4 @@ +#!/usr/bin/with-contenv bash +set -e + +exec /usr/sbin/cupsd -f From bb4032879adef37b517554c11cd47c341b7eb390 Mon Sep 17 00:00:00 2001 From: NoudAndi Date: Mon, 23 Mar 2026 20:07:41 +0100 Subject: [PATCH 2/2] Add force_regenerate_config option and manage cupsd.conf updates --- README.md | 13 +++ cups/config.yaml | 2 + cups/rootfs/etc/cont-init.d/cups.sh | 134 +++++++++-------------- cups/rootfs/etc/cups/cupsd.conf.template | 79 +++++++++++++ 4 files changed, 144 insertions(+), 84 deletions(-) create mode 100644 cups/rootfs/etc/cups/cupsd.conf.template diff --git a/README.md b/README.md index 89352e7..ae5ae46 100644 --- a/README.md +++ b/README.md @@ -56,13 +56,26 @@ The add-on provides the following configuration options: ```yaml admin_username: printadmin admin_password: your_secure_password +force_regenerate_config: false ``` - **admin_username**: Username for the CUPS admin interface (required) - **admin_password**: Password for the CUPS admin interface (required) +- **force_regenerate_config**: One-shot option to replace persisted `cupsd.conf` with the latest managed default (a backup is created) Printing from LAN clients remains open, while CUPS administration requires the configured credentials. +## Updating cupsd.conf Safely + +The add-on manages default `cupsd.conf` from a dedicated template file and tracks the applied template hash. + +- If no config exists, it creates a managed default file. +- If a managed file is based on an older template hash (or managed version), it is automatically backed up and replaced. +- If your file is unmanaged (custom/legacy), it is left untouched. + +To apply the latest managed defaults on an existing install, set `force_regenerate_config: true` for one restart. The old file is backed up as `cupsd.conf.bak.`. +After restart, set `force_regenerate_config` back to `false`. + ## Networking Notes This add-on uses host networking so mDNS/Bonjour announcements are visible on the LAN. diff --git a/cups/config.yaml b/cups/config.yaml index d298f97..fa8343b 100644 --- a/cups/config.yaml +++ b/cups/config.yaml @@ -20,7 +20,9 @@ init: false options: admin_username: "printadmin" admin_password: "change_me_now" + force_regenerate_config: false schema: admin_username: str admin_password: password + force_regenerate_config: bool startup: services diff --git a/cups/rootfs/etc/cont-init.d/cups.sh b/cups/rootfs/etc/cont-init.d/cups.sh index 7b0605b..d0edb2e 100755 --- a/cups/rootfs/etc/cont-init.d/cups.sh +++ b/cups/rootfs/etc/cont-init.d/cups.sh @@ -5,6 +5,10 @@ source /usr/lib/bashio/bashio.sh ADMIN_USER="$(bashio::config 'admin_username')" ADMIN_PASSWORD="$(bashio::config 'admin_password')" +FORCE_REGENERATE_CONFIG="$(bashio::config 'force_regenerate_config')" + +CUPSD_CONF="/data/cups/config/cupsd.conf" +TEMPLATE_PATH="/etc/cups/cupsd.conf.template" if [[ -z "${ADMIN_USER}" ]]; then bashio::log.fatal "Configuration admin_username must not be empty" @@ -36,89 +40,51 @@ chmod -R 775 /data/cups # Create CUPS configuration directory if it doesn't exist mkdir -p /etc/cups -# Write default configuration only on first run to avoid clobbering user changes. -if [[ ! -f /data/cups/config/cupsd.conf ]]; then - cat > /data/cups/config/cupsd.conf << EOL -# Listen on all interfaces -Listen 0.0.0.0:631 - -# Allow access from local network - - Order allow,deny - Allow localhost - Allow 10.0.0.0/8 - Allow 172.16.0.0/12 - Allow 192.168.0.0/16 - - -# Admin access requires authentication - - AuthType Basic - Require user @SYSTEM - Order allow,deny - Allow localhost - Allow 10.0.0.0/8 - Allow 172.16.0.0/12 - Allow 192.168.0.0/16 - - - - AuthType Basic - Require user @SYSTEM - Order allow,deny - Allow localhost - Allow 10.0.0.0/8 - Allow 172.16.0.0/12 - Allow 192.168.0.0/16 - - - - AuthType Basic - Require user @SYSTEM - Order allow,deny - Allow localhost - Allow 10.0.0.0/8 - Allow 172.16.0.0/12 - Allow 192.168.0.0/16 - - -# Job listing remains LAN-accessible - - Order allow,deny - Allow localhost - Allow 10.0.0.0/8 - Allow 172.16.0.0/12 - Allow 192.168.0.0/16 - - -# Printing remains LAN-accessible - - Order allow,deny - Allow localhost - Allow 10.0.0.0/8 - Allow 172.16.0.0/12 - Allow 192.168.0.0/16 - - -# Admin-level operations require authenticated system user - - AuthType Basic - Require user @SYSTEM - Order allow,deny - Allow localhost - Allow 10.0.0.0/8 - Allow 172.16.0.0/12 - Allow 192.168.0.0/16 - - -# Enable web interface -WebInterface Yes - -# Default settings -DefaultAuthType Basic -JobSheets none,none -PreserveJobHistory No -EOL +if [[ ! -f "${TEMPLATE_PATH}" ]]; then + bashio::log.fatal "Managed CUPS template not found at ${TEMPLATE_PATH}" + exit 1 +fi + +TEMPLATE_SHA256="$(sha256sum "${TEMPLATE_PATH}" | awk '{print $1}')" + +MANAGED_CONFIG_VERSION="3" + +write_managed_cupsd_conf() { + { + echo "# HA_ADDON_MANAGED_VERSION=${MANAGED_CONFIG_VERSION}" + echo "# HA_ADDON_TEMPLATE_SHA256=${TEMPLATE_SHA256}" + cat "${TEMPLATE_PATH}" + } > "${CUPSD_CONF}" +} + +backup_and_regenerate_cupsd_conf() { + local ts + ts="$(date +%Y%m%d%H%M%S)" + cp "${CUPSD_CONF}" "${CUPSD_CONF}.bak.${ts}" + write_managed_cupsd_conf +} + +if [[ ! -f "${CUPSD_CONF}" ]]; then + bashio::log.info "No persisted cupsd.conf found, creating managed default configuration" + write_managed_cupsd_conf +else + CURRENT_MANAGED_VERSION="$(grep -E '^# HA_ADDON_MANAGED_VERSION=' "${CUPSD_CONF}" | head -n1 | cut -d'=' -f2 || true)" + CURRENT_TEMPLATE_SHA256="$(grep -E '^# HA_ADDON_TEMPLATE_SHA256=' "${CUPSD_CONF}" | head -n1 | cut -d'=' -f2 || true)" + + if [[ "${FORCE_REGENERATE_CONFIG}" == "true" ]]; then + bashio::log.warning "force_regenerate_config=true: replacing existing cupsd.conf and writing backup" + backup_and_regenerate_cupsd_conf + elif [[ -n "${CURRENT_MANAGED_VERSION}" ]]; then + if [[ "${CURRENT_MANAGED_VERSION}" != "${MANAGED_CONFIG_VERSION}" ]]; then + bashio::log.info "Updating managed cupsd.conf from version ${CURRENT_MANAGED_VERSION} to ${MANAGED_CONFIG_VERSION}" + backup_and_regenerate_cupsd_conf + elif [[ "${CURRENT_TEMPLATE_SHA256}" != "${TEMPLATE_SHA256}" ]]; then + bashio::log.info "Updating managed cupsd.conf because template content changed" + backup_and_regenerate_cupsd_conf + fi + elif [[ -z "${CURRENT_MANAGED_VERSION}" ]]; then + bashio::log.warning "Existing cupsd.conf is unmanaged; keeping it unchanged. Set force_regenerate_config=true to replace it with managed defaults" + fi fi if ! id -u "${ADMIN_USER}" >/dev/null 2>&1; then @@ -129,7 +95,7 @@ addgroup "${ADMIN_USER}" lpadmin >/dev/null 2>&1 || true printf '%s:%s\n' "${ADMIN_USER}" "${ADMIN_PASSWORD}" | chpasswd # Create a symlink from the default config location to our persistent location -ln -sf /data/cups/config/cupsd.conf /etc/cups/cupsd.conf +ln -sf "${CUPSD_CONF}" /etc/cups/cupsd.conf ln -sf /data/cups/config/printers.conf /etc/cups/printers.conf ln -sf /data/cups/config/ppd /etc/cups/ppd ln -sf /data/cups/config/ssl /etc/cups/ssl diff --git a/cups/rootfs/etc/cups/cupsd.conf.template b/cups/rootfs/etc/cups/cupsd.conf.template new file mode 100644 index 0000000..17ba9fa --- /dev/null +++ b/cups/rootfs/etc/cups/cupsd.conf.template @@ -0,0 +1,79 @@ +# Listen on all interfaces +Listen 0.0.0.0:631 + +# Allow access from local network + + Order allow,deny + Allow localhost + Allow 10.0.0.0/8 + Allow 172.16.0.0/12 + Allow 192.168.0.0/16 + + +# Admin access requires authentication + + AuthType Basic + Require user @SYSTEM + Order allow,deny + Allow localhost + Allow 10.0.0.0/8 + Allow 172.16.0.0/12 + Allow 192.168.0.0/16 + + + + AuthType Basic + Require user @SYSTEM + Order allow,deny + Allow localhost + Allow 10.0.0.0/8 + Allow 172.16.0.0/12 + Allow 192.168.0.0/16 + + + + AuthType Basic + Require user @SYSTEM + Order allow,deny + Allow localhost + Allow 10.0.0.0/8 + Allow 172.16.0.0/12 + Allow 192.168.0.0/16 + + +# Job listing remains LAN-accessible + + Order allow,deny + Allow localhost + Allow 10.0.0.0/8 + Allow 172.16.0.0/12 + Allow 192.168.0.0/16 + + +# Printing remains LAN-accessible + + Order allow,deny + Allow localhost + Allow 10.0.0.0/8 + Allow 172.16.0.0/12 + Allow 192.168.0.0/16 + + +# Admin-level operations require authenticated system user + + AuthType Basic + Require user @SYSTEM + Order allow,deny + Allow localhost + Allow 10.0.0.0/8 + Allow 172.16.0.0/12 + Allow 192.168.0.0/16 + + +# Enable web interface +WebInterface Yes + +# Default settings +DefaultAuthType Basic +JobSheets none,none +PreserveJobHistory No