Add print server for label printer to have it easily usable via SSH
Add and configure a print server for the Brother P-touch QL 500 label printer, so that it can be easily used via SSH. Do the following to make that work: - Configure the print server host. - Package printer-driver-ptouch to have a working driver for the label printer. - Configure CUPS. - Add a script "forcecommand-lpr-wrapper", which works together with the ForceCommand sshd_config option and wraps lpr to provide an easy interface to use the Brother QL 500 label printer via SSH. - Add a print user and configure SSH to have the "forcecommand-lpr-wrapper" script accessible without a password using the print user via SSH.
This commit is contained in:
		
					parent
					
						
							
								6a0218c132
							
						
					
				
			
			
				commit
				
					
						c97f169b77
					
				
			
		
					 7 changed files with 248 additions and 0 deletions
				
			
		
							
								
								
									
										7
									
								
								config/hosts/ptouch-print-server/configuration.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								config/hosts/ptouch-print-server/configuration.nix
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | |||
| { ... }: | ||||
| 
 | ||||
| { | ||||
|   networking.hostName = "ptouch-print-server"; | ||||
| 
 | ||||
|   system.stateVersion = "23.11"; | ||||
| } | ||||
							
								
								
									
										9
									
								
								config/hosts/ptouch-print-server/default.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								config/hosts/ptouch-print-server/default.nix
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| { ... }: | ||||
| 
 | ||||
| { | ||||
|   imports = [ | ||||
|     ./configuration.nix | ||||
|     ./networking.nix | ||||
|     ./printing.nix | ||||
|   ]; | ||||
| } | ||||
|  | @ -0,0 +1,84 @@ | |||
| #!/usr/bin/env python3 | ||||
| 
 | ||||
| # A script for usage with the ForceCommand sshd_config option. | ||||
| # It calls lpr with some standard arguments, but also parses | ||||
| # SSH_ORIGINAL_COMMAND to potentially provide a different set of arguments to | ||||
| # lpr. | ||||
| # | ||||
| # This wrapper is written for interacting with the Brother QL 500 label printer. | ||||
| # | ||||
| # The following options can be provided as an SSH command and this script will | ||||
| # then pass them to the lpr call: <MediaType> <PageSize> | ||||
| # - MediaType can be one of: | ||||
| #   - Labels | ||||
| #   - Tape (this is the default) | ||||
| # - PageSize can be one of: | ||||
| #   - 12mm | ||||
| #   - 12mm-circular | ||||
| #   - 17x54mm | ||||
| #   - 17x87mm | ||||
| #   - 23x23mm | ||||
| #   - 24mm-circular | ||||
| #   - 29mm | ||||
| #   - 29x90mm | ||||
| #   - 38mm | ||||
| #   - 38x90mm | ||||
| #   - 50mm | ||||
| #   - 54mm | ||||
| #   - 58mm-circular | ||||
| #   - 62mm | ||||
| #   - 62x29mm | ||||
| #   - 62x100mm | ||||
| #   - Custom.WIDTHxHEIGHT (with WIDTH and HEIGHT needing to be either one to | ||||
| #     three digits) | ||||
| #   - label-wide (this being a convenience alias for Custom.62x35mm and it also | ||||
| #     being the default) | ||||
| #   - label-item (this being a convenience alias for 38x90mm) | ||||
| #  | ||||
| # So using these options in a complete setup would look like this for example: | ||||
| # cat label-item.pdf | ssh print@ptouch-print-server.z9.ccchh.net labels label-item | ||||
| # This being equivalent to: | ||||
| # cat label-item.pdf | ssh print@ptouch-print-server.z9.ccchh.net Labels 38x90mm | ||||
| # | ||||
| # The options are case-insensitive. | ||||
| # | ||||
| # The options are derived from: lpoptions -p Brother-QL-500 -l | ||||
| 
 | ||||
| import os, re, subprocess | ||||
| 
 | ||||
| mediaType = "Tape" | ||||
| pageSize = "Custom.62x35mm" | ||||
| 
 | ||||
| def parseGivenOptions(): | ||||
|     givenOptionsString = os.environ["SSH_ORIGINAL_COMMAND"] | ||||
|     givenOptionsIterator = iter(givenOptionsString.split(" ")) | ||||
| 
 | ||||
|     givenMediaType = next(givenOptionsIterator, "") | ||||
|     givenPageSize = next(givenOptionsIterator, "") | ||||
| 
 | ||||
|     global mediaType | ||||
|     if givenMediaType.lower() == "labels": | ||||
|         mediaType = "Labels" | ||||
|     elif givenMediaType.lower() == "tape": | ||||
|         mediaType = "Tape" | ||||
| 
 | ||||
|     global pageSize | ||||
|     pageSizeRegex = re.compile(r"^((12mm(-circular)?)|(24mm-circular)|(58mm-circular)|(((17x(54|87))|(23x23)|((29|38)(x90)?)|(62x(29|100))|50|54|62)mm))$", re.ASCII | re.IGNORECASE) | ||||
|     pageSizeMatch = pageSizeRegex.match(givenPageSize) | ||||
|     pageSizeCustomRegex = re.compile(r"^custom\.(\d{1,3})x(\d{1,3})$", re.ASCII | re.IGNORECASE) | ||||
|     pageSizeCustomMatch = pageSizeCustomRegex.match(givenPageSize) | ||||
|     if givenPageSize.lower() == "label-wide": | ||||
|         pageSize = "Custom.62x35mm" | ||||
|     elif givenPageSize.lower() == "label-item": | ||||
|         pageSize = "38x90mm" | ||||
|     elif pageSizeMatch: | ||||
|         pageSize = givenPageSize.lower() | ||||
|     elif pageSizeCustomMatch: | ||||
|         width = pageSizeCustomMatch.group(1) | ||||
|         height = pageSizeCustomMatch.group(2) | ||||
|         pageSize = "Custom.{}x{}".format(width, height) | ||||
| 
 | ||||
| if "SSH_ORIGINAL_COMMAND" in os.environ: | ||||
|     parseGivenOptions() | ||||
| 
 | ||||
| subprocess.run(["lpr", "-P", "Brother-QL-500", "-o", "MediaType={}".format(mediaType), "-o", "PageSize={}".format(pageSize)]) | ||||
|  | @ -0,0 +1,7 @@ | |||
| from distutils.core import setup | ||||
| 
 | ||||
| setup( | ||||
|     name = "forcecommand-lpr-wrapper", | ||||
|     version = "0.0.1", | ||||
|     scripts = ["./forcecommand-lpr-wrapper.py"] | ||||
| ) | ||||
							
								
								
									
										23
									
								
								config/hosts/ptouch-print-server/networking.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								config/hosts/ptouch-print-server/networking.nix
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | |||
| { ... }: | ||||
| 
 | ||||
| { | ||||
|   networking = { | ||||
|     interfaces.net0 = { | ||||
|       ipv4.addresses = [ | ||||
|         { | ||||
|           address = "10.31.208.13"; | ||||
|           prefixLength = 25; | ||||
|         } | ||||
|       ]; | ||||
|     }; | ||||
|     defaultGateway = "10.31.208.1"; | ||||
|     nameservers = [ | ||||
|       "10.31.208.1" | ||||
|     ]; | ||||
|   }; | ||||
| 
 | ||||
|   systemd.network.links."10-net0" = { | ||||
|     matchConfig.MACAddress = "BC:24:11:F2:CF:8F"; | ||||
|     linkConfig.Name = "net0"; | ||||
|   }; | ||||
| } | ||||
							
								
								
									
										104
									
								
								config/hosts/ptouch-print-server/printing.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								config/hosts/ptouch-print-server/printing.nix
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,104 @@ | |||
| # Sources for this configuration: | ||||
| # - https://nixos.wiki/wiki/Printing | ||||
| 
 | ||||
| { pkgs, lib, ... }: | ||||
| 
 | ||||
| let | ||||
|   # https://github.com/philpem/printer-driver-ptouch | ||||
|   printer-driver-ptouch = pkgs.stdenv.mkDerivation rec { | ||||
|     pname = "printer-driver-ptouch"; | ||||
|     version = "1.7"; | ||||
| 
 | ||||
|     src = pkgs.fetchgit { | ||||
|       url = "https://github.com/philpem/printer-driver-ptouch"; | ||||
|       rev = "v${version}"; | ||||
|       hash = "sha256-3ZotSHn7lERp53hAzx47Ct/k565rEoensCcltwX/Xls="; | ||||
|     }; | ||||
| 
 | ||||
|     nativeBuildInputs = [ | ||||
|       pkgs.autoreconfHook | ||||
|       pkgs.perl | ||||
|     ]; | ||||
| 
 | ||||
|     buildInputs = [ | ||||
|       pkgs.cups | ||||
|       pkgs.libpng | ||||
|       pkgs.perlPackages.XMLLibXML | ||||
|       pkgs.foomatic-db-engine | ||||
|     ]; | ||||
| 
 | ||||
|     patches = [ | ||||
|       # Add this patch to have the package actually build sucessfully. | ||||
|       # https://github.com/philpem/printer-driver-ptouch/pull/35 | ||||
|       (pkgs.fetchpatch { | ||||
|         name = "fix-brother-ql-600.xml.patch"; | ||||
|         url = "https://patch-diff.githubusercontent.com/raw/philpem/printer-driver-ptouch/pull/35.patch"; | ||||
|         hash = "sha256-y5bHKFeRXx8Wdl1++l4QNGgiY41LY5uzrRdOlaZyF9I="; | ||||
|       }) | ||||
|     ]; | ||||
| 
 | ||||
|     # Used the following as a reference on how to generate the ppd files. | ||||
|     # https://salsa.debian.org/printing-team/ptouch-driver/-/blob/4ba5d2c490ea1230374aa4b0bf711bf77f1ab0c7/debian/rules#L34 | ||||
|     postInstall = '' | ||||
|       mkdir -p $out/share/cups | ||||
|       FOOMATICDB=$out/share/foomatic ${pkgs.foomatic-db-engine}/bin/foomatic-compiledb -t ppd -d $out/share/cups/model | ||||
|       rm -r $out/share/foomatic | ||||
|     ''; | ||||
| 
 | ||||
|     postPatch = '' | ||||
|       patchShebangs --build foomaticalize | ||||
|     ''; | ||||
|   }; | ||||
|   forcecommand-lpr-wrapper = pkgs.python3Packages.buildPythonApplication { | ||||
|     name = "forcecommand-lpr-wrapper"; | ||||
|     src = ./forcecommand-lpr-wrapper; | ||||
| 
 | ||||
|     propagatedBuildInputs = [ | ||||
|       pkgs.cups | ||||
|     ]; | ||||
|   }; | ||||
| in | ||||
| { | ||||
|   services.printing = { | ||||
|     enable = true; | ||||
|     drivers = [ printer-driver-ptouch ]; | ||||
|     stateless = true; | ||||
|   }; | ||||
| 
 | ||||
|   hardware.printers = { | ||||
|     ensurePrinters = [ | ||||
|       { | ||||
|         name = "Brother-QL-500"; | ||||
|         location = "Z9"; | ||||
|         deviceUri = "usb://Brother/QL-500?serial=J8Z249208"; | ||||
|         model = "Brother-QL-500-ptouch-ql.ppd"; | ||||
|         ppdOptions = { | ||||
|           PageSize = "Custom.62x35mm"; | ||||
|         }; | ||||
|       } | ||||
|     ]; | ||||
|     ensureDefaultPrinter = "Brother-QL-500"; | ||||
|   }; | ||||
| 
 | ||||
|   users.users.print = { | ||||
|     isNormalUser = true; | ||||
|     description = "User for printing via SSH."; | ||||
|     password = ""; | ||||
|   }; | ||||
| 
 | ||||
|   # PasswordAuthentication being set to false just puts "auth required | ||||
|   # pam_deny.so # deny (order 12400)" for pam.d/sshd, so enable | ||||
|   # PasswordAuthentication to have it not do that. | ||||
|   services.openssh.settings.PasswordAuthentication = lib.mkForce true; | ||||
|   # The following doesn't need to be set in order for empty passwords to work | ||||
|   # apparently: | ||||
|   # security.pam.services.sshd.allowNullPassword = true; | ||||
|   services.openssh.extraConfig = '' | ||||
|     Match User print | ||||
|       PubkeyAuthentication no | ||||
|       AuthenticationMethods none | ||||
|       PermitEmptyPasswords yes | ||||
|       ForceCommand ${forcecommand-lpr-wrapper}/bin/forcecommand-lpr-wrapper.py  | ||||
|     Match User * | ||||
|   ''; | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue