diff --git a/files/paperless-virtual-printer/Dockerfile b/files/paperless-virtual-printer/Dockerfile
new file mode 100644
index 0000000..2bf8e98
--- /dev/null
+++ b/files/paperless-virtual-printer/Dockerfile
@@ -0,0 +1,25 @@
+FROM debian:bullseye-slim
+ARG USERNAME=print PASSWORD=print
+ENV \
+ PRINTER_ID=Paperless \
+ PRINTER_NAME=Paperless \
+ HOSTNAME=127.0.0.1 \
+ OUTPUT_SUBPATH=vprint \
+ OUTPUT_USERNAME=$USERNAME
+
+# Dependenicies
+RUN apt-get update
+RUN apt-get install -y --no-install-recommends cups printer-driver-cups-pdf gettext
+
+# Cups config/setup
+RUN useradd -G lp,lpadmin -s /bin/bash -p "$(openssl passwd -1 $PASSWORD)" $USERNAME
+
+# discoverability
+RUN apt-get install -y avahi-daemon
+RUN sed -i 's/.*enable\-dbus=.*/enable\-dbus\=no/' /etc/avahi/avahi-daemon.conf
+
+# Copy configs
+WORKDIR /opt/vp
+COPY . .
+
+CMD ["./entrypoint.sh"]
diff --git a/files/paperless-virtual-printer/cups-pdf.conf b/files/paperless-virtual-printer/cups-pdf.conf
new file mode 100644
index 0000000..3b43e69
--- /dev/null
+++ b/files/paperless-virtual-printer/cups-pdf.conf
@@ -0,0 +1,9 @@
+# Cups PDF printer to /mnt
+
+Out /mnt/output
+AnonDirName /mnt/output
+Label 0
+AnonUser ${OUTPUT_USERNAME}
+
+Grp lpadmin
+DecodeHexStrings 1
diff --git a/files/paperless-virtual-printer/cupsd.conf b/files/paperless-virtual-printer/cupsd.conf
new file mode 100644
index 0000000..f5f41ba
--- /dev/null
+++ b/files/paperless-virtual-printer/cupsd.conf
@@ -0,0 +1,190 @@
+#
+# Configuration file for the CUPS scheduler. See "man cupsd.conf" for a
+# complete description of this file.
+#
+
+# Log general information in error_log - change "warn" to "debug"
+# for troubleshooting...
+LogLevel warn
+PageLogFormat
+
+# Deactivate CUPS' internal logrotating, as we provide a better one, especially
+# LogLevel debug2 gets usable now
+MaxLogSize 0
+
+# Only listen for connections from the local machine.
+Listen *:631
+Listen /run/cups/cups.sock
+ServerAlias *
+
+# Show shared printers on the local network.
+Browsing On
+BrowseLocalProtocols dnssd
+
+# Default authentication type, when authentication is required...
+DefaultAuthType Basic
+DefaultEncryption Never
+
+# Web interface setting...
+WebInterface Yes
+
+# Restrict access to the server...
+
+ Order allow,deny
+ Allow all
+
+
+# Restrict access to the admin pages...
+
+ Order allow,deny
+ Allow all
+
+
+# Restrict access to configuration files...
+
+ AuthType Default
+ Require user @SYSTEM
+ Order allow,deny
+
+
+# Restrict access to log files...
+
+ AuthType Default
+ Require user @SYSTEM
+ Order allow,deny
+
+
+# Set the default printer/job policies...
+
+ # Job/subscription privacy...
+ JobPrivateAccess default
+ JobPrivateValues default
+ SubscriptionPrivateAccess default
+ SubscriptionPrivateValues default
+
+ # Job-related operations must be done by the owner or an administrator...
+
+ Order deny,allow
+
+
+
+ Require user @OWNER @SYSTEM
+ Order deny,allow
+
+
+ # All administration operations require an administrator to authenticate...
+
+ AuthType Default
+ Require user @SYSTEM
+ Order deny,allow
+
+
+ # All printer operations require a printer operator to authenticate...
+
+ AuthType Default
+ Require user @SYSTEM
+ Order deny,allow
+
+
+ # Only the owner or an administrator can cancel or authenticate a job...
+
+ Require user @OWNER @SYSTEM
+ Order deny,allow
+
+
+
+ Order deny,allow
+
+
+
+# Set the authenticated printer/job policies...
+
+ # Job/subscription privacy...
+ JobPrivateAccess default
+ JobPrivateValues default
+ SubscriptionPrivateAccess default
+ SubscriptionPrivateValues default
+
+ # Job-related operations must be done by the owner or an administrator...
+
+ AuthType Default
+ Order deny,allow
+
+
+
+ AuthType Default
+ Require user @OWNER @SYSTEM
+ Order deny,allow
+
+
+ # All administration operations require an administrator to authenticate...
+
+ AuthType Default
+ Require user @SYSTEM
+ Order deny,allow
+
+
+ # All printer operations require a printer operator to authenticate...
+
+ AuthType Default
+ Require user @SYSTEM
+ Order deny,allow
+
+
+ # Only the owner or an administrator can cancel or authenticate a job...
+
+ AuthType Default
+ Require user @OWNER @SYSTEM
+ Order deny,allow
+
+
+
+ Order deny,allow
+
+
+
+# Set the kerberized printer/job policies...
+
+ # Job/subscription privacy...
+ JobPrivateAccess default
+ JobPrivateValues default
+ SubscriptionPrivateAccess default
+ SubscriptionPrivateValues default
+
+ # Job-related operations must be done by the owner or an administrator...
+
+ AuthType Negotiate
+ Order deny,allow
+
+
+
+ AuthType Negotiate
+ Require user @OWNER @SYSTEM
+ Order deny,allow
+
+
+ # All administration operations require an administrator to authenticate...
+
+ AuthType Default
+ Require user @SYSTEM
+ Order deny,allow
+
+
+ # All printer operations require a printer operator to authenticate...
+
+ AuthType Default
+ Require user @SYSTEM
+ Order deny,allow
+
+
+ # Only the owner or an administrator can cancel or authenticate a job...
+
+ AuthType Negotiate
+ Require user @OWNER @SYSTEM
+ Order deny,allow
+
+
+
+ Order deny,allow
+
+
diff --git a/files/paperless-virtual-printer/entrypoint.sh b/files/paperless-virtual-printer/entrypoint.sh
new file mode 100755
index 0000000..9f4e15b
--- /dev/null
+++ b/files/paperless-virtual-printer/entrypoint.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+set -e
+
+declare -r usermap_original_uid=$(id -u print)
+declare -r usermap_original_gid=$(id -g print)
+declare -r usermap_new_uid=${USERMAP_UID:-$usermap_original_uid}
+declare -r usermap_new_gid=${USERMAP_GID:-${usermap_original_gid:-$usermap_new_uid}}
+
+if [[ ${usermap_new_uid} != "${usermap_original_uid}" ]]; then
+ echo "${log_prefix} Mapping UID to $usermap_new_uid"
+ usermod --non-unique --uid "${usermap_new_uid}" print
+else
+ echo "${log_prefix} No UID changes"
+fi
+
+if [[ ${usermap_new_gid} != "${usermap_original_gid}" ]]; then
+ echo "${log_prefix} Mapping GID to $usermap_new_gid"
+ groupmod --non-unique --gid "${usermap_new_gid}" print
+else
+ echo "${log_prefix} No GID changes"
+fi
+
+cat cups-pdf.conf | envsubst > /etc/cups/cups-pdf.conf
+cat cupsd.conf | envsubst > /etc/cups/cupsd.conf
+cat printers.conf | envsubst > /etc/cups/printers.conf
+cat vprint.service | envsubst > /etc/avahi/services/vprint.service
+cp /usr/share/ppd/cups-pdf/CUPS-PDF_noopt.ppd /etc/cups/ppd/${PRINTER_ID}.ppd
+
+/usr/sbin/avahi-daemon --daemonize
+exec /usr/sbin/cupsd -f
diff --git a/files/paperless-virtual-printer/printers.conf b/files/paperless-virtual-printer/printers.conf
new file mode 100644
index 0000000..d84d1b5
--- /dev/null
+++ b/files/paperless-virtual-printer/printers.conf
@@ -0,0 +1,22 @@
+# Printer configuration file for CUPS v2.2.10
+# Written by cupsd
+# DO NOT EDIT THIS FILE WHEN CUPSD IS RUNNING
+
+UUID urn:uuid:b5105127-039f-3bdc-6153-762925a06b8f
+Info ${PRINTER_NAME}
+Location
+MakeModel Generic PostScript Printer
+DeviceURI cups-pdf:/
+State Idle
+StateTime 1666751881
+ConfigTime 1666751900
+Type 8400972
+Accepting Yes
+Shared Yes
+JobSheets none none
+QuotaPeriod 0
+PageLimit 0
+KLimit 0
+OpPolicy default
+ErrorPolicy retry-job
+
diff --git a/files/paperless-virtual-printer/vprint.service b/files/paperless-virtual-printer/vprint.service
new file mode 100644
index 0000000..3d89d9b
--- /dev/null
+++ b/files/paperless-virtual-printer/vprint.service
@@ -0,0 +1,22 @@
+
+
+ ${PRINTER_NAME}
+
+ _ipp._tcp
+ _universal._sub._ipp._tcp
+ 631
+ adminurl=http://${HOSTNAME}/printers/${PRINTER_ID}
+ txtvers=1
+ qtotal=1
+ Transparent=T
+ URF=DM3
+ rp=printers/${PRINTER_ID}
+ note=Virtual ${PRINTER_NAME}
+ product=(Generic PostScript Printer)
+ ty=Generic PostScript Printer
+ printer-state=3
+ printer-type=0x800004
+ application/postscript
+ pdl=application/octet-stream,application/pdf,application/postscript,application/vnd.cups-raster,image/gif,image/jpeg,image/png,image/tiff,image/urf,text/html,text/plain,application/msword,application/pclm,application/rss+xml,application/sgml
+
+
diff --git a/group_vars/docker/vars.yaml b/group_vars/docker/vars.yaml
index 0d36413..e0a9547 100644
--- a/group_vars/docker/vars.yaml
+++ b/group_vars/docker/vars.yaml
@@ -115,6 +115,14 @@ all_container_users:
gid: 215
hosts:
- memory_alpha
+ - username: paperless-printer
+ uid: 216
+ gid: 216
+ groups:
+ - archive
+ - scanners
+ hosts:
+ - memory_alpha
container_user_opts:
shell: /usr/sbin/nologin
diff --git a/host_vars/memory_alpha/vars.yaml b/host_vars/memory_alpha/vars.yaml
index bc4522f..ae9d744 100644
--- a/host_vars/memory_alpha/vars.yaml
+++ b/host_vars/memory_alpha/vars.yaml
@@ -4,6 +4,7 @@ target_os: ubuntu
appdata_path: /mnt/zfs/appdata
archive_path: /mnt/zfs/archive
+paperless_consume_path: /mnt/transient/paperless/consume
media_downloads_mountpoint: "/mnt/zfs/media-downloads"
@@ -580,7 +581,7 @@ docker_compose_projects:
owner: paperless
group: archive
mode: "u+rwx,g+rwx"
- - source: /mnt/transient/paperless/consume
+ - source: "{{ paperless_consume_path }}"
target: /usr/src/paperless/consume
owner: paperless
group: scanners
@@ -666,8 +667,33 @@ docker_compose_projects:
restart: unless-stopped
labels:
- "traefik.enable=false"
+ - service_name: virtual-printer
+ build:
+ context: virtual-printer
+ src: paperless-virtual-printer
+ restart: unless-stopped
+ environment:
+ USERMAP_UID: "{{ (container_users | selectattr('username', 'equalto', 'paperless-printer') | list | first).uid }}"
+ USERMAP_GID: "{{ (nas_groups | selectattr('name', 'equalto', 'scanners') | list | first).gid }}"
+ networks:
+ - virtual-printer-vlan
+ volumes:
+ - source: "{{ paperless_consume_path }}"
+ target: /mnt/output
+ create: false
+ ports:
+ - 631:631
networks:
- name: paperless
+ - name: virtual-printer-vlan
+ driver: macvlan
+ driver_opts:
+ parent: "{{ ansible_default_ipv4.interface }}"
+ ipam:
+ config:
+ - subnet: "192.168.5.0/24"
+ ip_range: "192.168.5.134/32" # Static IP
+ gateway: "192.168.5.1"
- name: proxy
external: true
alias: "{{ traefik_docker_network }}"