<sub>(2025/12/09)</sub> #Brother #Certificates #Cloudflare #Tailscale #Printer <sub>Article inspiration:</sub> - <sub>https://blog.poggs.com/2020/03/18/printer-security-installing-tls-certificates-on-hp-printers-automatically/</sub> - <sub>https://github.com/gregtwallace/brother-cert/releases</sub> ## Introduction I am pretty proud of this blog post as I managed to figure out an automatic method to deploy a Let's Encrypt certificate to my personal Brother printer using a simple Bash script combined with Cloudflare DNS integration. Now you may ask why bother? Well I always wanted to deploy a proper certificate to my own personal printer but not maintain a local only certificate authority. Not only that, I also wanted a system in place that would do this automatically similar to how my Nginx Proxy Manager (NPM) container handles certificates for the various Docker web apps that I deploy. Even though my specific situation is unique to myself, you can definitely use this blog post as a starting point for your own needs. I heavily rely on Tailscale with Cloudflare for my homelab as I take the various Tailscale IPs of my devices, give them a public DNS A record, and then configure NPM to route the DNS requests to their specific application hosted on them. This allows me to use a proper domain name (instead of the typical internal name) that is routable/pingable externally but can only be accessed by my Tailscale network. For example my Zabbix instance, `https://monitor.owltec.ca`, has a public DNS A record but this instance is only accessible via my Tailnet. Unfortunately, I cannot install Tailscale on my Brother printer so it is unable to receive a Tailscale IP. However, NextDNS gives me the ability to do local DNS rewrites so now `printer.owltec.ca` will redirect to a local IPv4 address. While not a Tailscale IP, it works well, as similar to my Tailnet, you have to be using my NextDNS for the DNS redirect to work. I suppose I could give `printer.owltec.ca` a public DNS A record with a local IPv4 address (as Cloudflare will allow me) but that is a bit messy as I want to prevent DNS rebinding. For accessing the printer there are two main ways I would access it which includes via the web management portal and from my client devices. Accessing console with HTTPS, I could use NPM off my Docker host to redirect my requests to the printer but then I am adding additional complexity for no real gain as NPM is still connecting to my printer via HTTP (so there is no end-to-end encryption) and I wanted my printer to be independent of any reverse proxy (in case the reverse proxy host is down). Also putting a reverse proxy in front of the printer does not fix the issue of the client device connecting printer with an unencrypted protocol (such as IPP) as I want to use IPP with the HTTPS setting. Now with my thought process out of the way, let me explain what I did. My setup: - DNS records managed by Cloudflare - Personal DNS managed by NextDNS - My homelab machine (a Mac Mini) that will be running my bash scripts via `Cronicle` - My Brother DCP-L2550DW printer ## Prerequisites 1. A Brother printer (duh) 2. A virtual machine or device that will be used to run Bash scripts via `Cronicle` that will: - Request a certificate with `Certbot` via Cloudflare - FYI - Brother printers require a `RAS-2048` certificate which means we need to specify that option when we request a certificate (as the printer cannot use a more modern certificate format) - Deploy the new certificate to your Brother printer using the `Brother Cert` tool - [Download the latest version of `Brother Cert`](https://github.com/gregtwallace/brother-cert) and extract the files for later use - This application is what will install our newly created certificate on our Brother printer by converting our PEM certificate to the PKCS#12 format that is required for Brother printers While I am using `Cronicle`, you are free to use any other solution to run scripts automatically or you can simply run the script manually. ### Mac Setup To install the prerequisites on our Mac we need to install [Homebrew:](http://brew.sh/) - `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"` Once `Homebrew` has been installed we will then install: - `Certbot`: - `brew install certbot` - `Cronicle`: - First we need to install `nodejs` -> `brew install node` - Then we will become root (`sudo -i`) and then install `Cronicle` -> `curl -s https://raw.githubusercontent.com/jhuckaby/Cronicle/master/bin/install.js | node` - Configure `Cronicle` based on your own values by editing the `/opt/cronicle/conf/config.json` file -> [setup values located here](https://github.com/jhuckaby/Cronicle/blob/master/docs/Setup.md#setup) - After `Cronicle` has been installed and configured, run the post install script -> `/opt/cronicle/bin/control.sh setup` - Finally, start `Cronicle` -> `/opt/cronicle/bin/control.sh start` - Now to access your `Cronicle` instance connect to following address in your web browser -> http://YOUR_SERVER_HOSTNAME:3012/ with the following default credentials (`admin` and `admin`) - If you wish to further customize `Cronicle` (including the SMTP settings and what not), [please check out their guide](https://github.com/jhuckaby/Cronicle/blob/master/docs/Configuration.md) ### Cloudflare For the script to automatically create a certificate, `Certbot` will request a certificate from Let's Encrypt and will use Cloudflare to verify domain ownership. In my case, Cloudflare is my DNS provider but I am sure you could modify my script by adapting it to work with your provider. In any case, we need to generate an API that will be used for the script. [Please check out the following guide to generate an API key.](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/) Make sure to create an API key with limited permissions as that is all that this script requires (`DNS:Edit` only): ![[Brother - Cert - 01.webp]] ![[Brother - Cert - 02.webp]] ### Brother Cert The final piece of this puzzle is the `Brother Cert` tool created by [Greg Wallace](https://github.com/gregtwallace) ([check out his other work as I have featured it on my site](https://owltec.ca/Windows+Server/Deploying+An+Internal+HTTPS+Certificate+for+a+UPS+APC+with+ADCS+\(Active+Directory+Certificate+Services\)+with+APC+P15+Tool)). [Download the latest version based on your operating system (in my case I downloaded the latest Mac version), extract the files, and store it somewhere that can referenced later on](https://github.com/gregtwallace/brother-cert). This application is what will install our newly created certificate on our Brother printer by converting our PEM certificate to the PKCS#12 format that is required for Brother printers and will restart our printer when the certificate has been installed. However, because `Brother Cert` is an unsigned application you will have to manually run it once and then allow it to run on your Mac by going into `Privacy & Security` under `System Settings` then selecting `Allow anyway` for the application. Afterwards, the application will run just fine in the script. ![[Brother - Cert - 06.webp]] ![[Brother - Cert - 08.webp]] ## The Script The `printer-cert-master.sh` script performs the following two main tasks: 1. Obtains a valid SSL certificate from Let's Encrypt via `Certbot` and Cloudflare 2. Deploys the certificate to the Brother printer First, the script uses `certbot` with the Cloudflare DNS plugin to obtain a Let's Encrypt certificate by programmatically creating a DNS TXT record to prove domain ownership, specifically requesting RSA-2048 keys (required by Brother printers) instead of more modern ECDSA keys. Once the certificate is acquired, the script copies the certificate files to a shared storage location and immediately deploys them to the printer using the `brother-cert` tool, which connects to your printer via HTTPS (falling back to HTTP if the printer has an expired certificate), converts the PEM certificates to PKCS#12 format, uploads the new certificate to the printer, and triggers an automatic printer reboot to activate the new certificate. Besides the script, there are two additional files `cloudflare.ini` and `printer.ini` which are credential files used with Cloudflare and my Brother printer respectively. My decision to split the credentials is to allow for the use of the `cloudflare.ini` file to be shared across all `certbot` scripts while keeping the device-specific passwords isolated for better security and easier credential rotation. ### Cloudflare.ini ``` # Cloudflare API credentials for certbot # DO NOT SHARE THIS FILE - Contains sensitive credentials dns_cloudflare_api_token = YOUR CLOUDFLARE API TOKEN ``` ### printer.ini ``` # Brother Printer Credentials # DO NOT SHARE THIS FILE - Contains sensitive credentials printer_name = YOUR PRINTER DNS NAME printer_password = YOUR ADMIN PASSWORD FOR YOUR PRINTER ``` ### printer-cert-master.sh: Below is the script I am using, please take note of the following variables that will need to be modified for your own personal usage: - `DOMAIN="DNS NAME FOR PRINTER"` - This is the URL that is used to access your printer (and what the certificate will use) -> in my case it is `https://ot-prt01.owltec.ca` - `EMAIL="ADMIN EMAIL"` - This is the email used for certificate verification - `CERT_DESTINATION="CERT LOCATION"` - Where you want to save your newly generated certificate - `CLOUDFLARE_CREDENTIALS="cloudflare.ini LOCATION"` - Location of your `Cloudflare.ini` file - `PRINTER_CREDENTIALS="printer.ini LOCATION"` - Location of your `printer.ini` file - `BROTHER_CERT_TOOL="brother cert PROGRAM LOCATION" - Location of the `Brother Cert` tool you had previously downloaded - `VENV_PATH="VENV LOCATION"` - Where you would like to store your virtual environment for this script ``` #!/bin/bash # Master Certificate Management Script for Brother Printer (OT-PRT01.owltec.ca) # Combines certificate acquisition (certbot) and deployment (brother-cert) # This script automates the entire certificate lifecycle echo "==============================================" echo " Brother Printer Certificate Management" echo " YOUR PRINTER NAME (YOUR PRINTER IP) echo "==============================================" echo "" # Configuration DOMAIN="DNS NAME FOR PRINTER" EMAIL="ADMIN EMAIL" CERT_DESTINATION="CERT LOCATION" CLOUDFLARE_CREDENTIALS="cloudflare.ini LOCATION" PRINTER_CREDENTIALS="printer.ini LOCATION" BROTHER_CERT_TOOL="brother cert PROGRAM LOCATION" VENV_PATH="VENV LOCATION" # ============================================ # VENV SETUP # ============================================ echo "Checking Python virtual environment..." # Check if venv exists, create if not if [ ! -d "$VENV_PATH" ]; then echo "Virtual environment not found. Creating at $VENV_PATH..." python3 -m venv "$VENV_PATH" if [ $? -eq 0 ]; then echo "✓ Virtual environment created successfully" else echo "✗ Failed to create virtual environment" exit 1 fi else echo "✓ Virtual environment found" fi # Activate the virtual environment source "$VENV_PATH/bin/activate" if [ $? -eq 0 ]; then echo "✓ Virtual environment activated" echo " Using Python: $(which python3)" echo " Using pip: $(which pip3)" else echo "✗ Failed to activate virtual environment" exit 1 fi # Upgrade pip to latest version to avoid warnings echo "Upgrading pip..." pip3 install --upgrade pip --quiet if [ $? -eq 0 ]; then echo "✓ pip upgraded to latest version" else echo "⚠ Failed to upgrade pip, but continuing anyway" fi echo "" # ============================================ # PART 1: CERTIFICATE ACQUISITION (CERTBOT) # ============================================ echo "STEP 1: Certificate Acquisition" echo "================================" echo "" # Check if cloudflare credentials file exists if [ ! -f "$CLOUDFLARE_CREDENTIALS" ]; then echo "Error: Cloudflare credentials file not found at $CLOUDFLARE_CREDENTIALS" echo "" echo "Please create the file with the following content:" echo "" echo "# Cloudflare API token (recommended)" echo "dns_cloudflare_api_token = YOUR_API_TOKEN_HERE" echo "" echo "Then run: chmod 600 $CLOUDFLARE_CREDENTIALS" exit 1 fi # Check if certbot-dns-cloudflare is installed in venv if ! command -v certbot &> /dev/null || ! python3 -c "import certbot_dns_cloudflare" 2>/dev/null; then echo "certbot-dns-cloudflare plugin not found in venv. Installing..." pip3 install certbot certbot-dns-cloudflare if [ $? -eq 0 ]; then echo "✓ certbot and certbot-dns-cloudflare installed successfully" else echo "✗ Failed to install required packages" exit 1 fi else echo "✓ certbot-dns-cloudflare plugin found" fi # Create destination directory if it doesn't exist mkdir -p "$CERT_DESTINATION" echo "Requesting SSL certificate for $DOMAIN using Cloudflare DNS..." echo "Using RSA key for Brother printer compatibility..." echo "" # Request certificate using Cloudflare DNS verification certbot certonly \ --dns-cloudflare \ --dns-cloudflare-credentials "$CLOUDFLARE_CREDENTIALS" \ --dns-cloudflare-propagation-seconds 30 \ --email "$EMAIL" \ --agree-tos \ --no-eff-email \ --domain "$DOMAIN" \ --key-type rsa \ --keep-until-expiring \ --non-interactive CERTBOT_EXIT=$? # Check if successful if [ $CERTBOT_EXIT -eq 0 ]; then echo "" echo "✓ Certificate check completed successfully!" echo "" echo "Copying certificates to $CERT_DESTINATION..." # Copy certificates to destination cp /etc/letsencrypt/live/$DOMAIN/fullchain.pem "$CERT_DESTINATION/fullchain.pem" cp /etc/letsencrypt/live/$DOMAIN/privkey.pem "$CERT_DESTINATION/privkey.pem" cp /etc/letsencrypt/live/$DOMAIN/cert.pem "$CERT_DESTINATION/cert.pem" cp /etc/letsencrypt/live/$DOMAIN/chain.pem "$CERT_DESTINATION/chain.pem" # Set appropriate permissions chmod 644 "$CERT_DESTINATION/fullchain.pem" chmod 644 "$CERT_DESTINATION/cert.pem" chmod 644 "$CERT_DESTINATION/chain.pem" chmod 600 "$CERT_DESTINATION/privkey.pem" echo "" echo "Certificate files copied to:" echo " Full Chain: $CERT_DESTINATION/fullchain.pem" echo " Private Key: $CERT_DESTINATION/privkey.pem" echo " Certificate: $CERT_DESTINATION/cert.pem" echo " Chain: $CERT_DESTINATION/chain.pem" echo "" else echo "" echo "✗ Certificate request failed with exit code: $CERTBOT_EXIT" echo "Skipping deployment step." exit 1 fi # ============================================ # PART 2: CERTIFICATE DEPLOYMENT (BROTHER-CERT) # ============================================ echo "" echo "STEP 2: Certificate Deployment" echo "===============================" echo "" # Certificate files for deployment PRIVATE_KEY="$CERT_DESTINATION/privkey.pem" CERTIFICATE="$CERT_DESTINATION/fullchain.pem" # Check if printer credentials file exists if [ ! -f "$PRINTER_CREDENTIALS" ]; then echo "Error: Printer credentials file not found at $PRINTER_CREDENTIALS" echo "" echo "Please create the file with the following content:" echo "" echo "# Brother Printer Credentials" echo "printer_name = YOUR_PRINTER_HOSTNAME" echo "printer_password = YOUR_PRINTER_PASSWORD" echo "" echo "Then run: chmod 600 $PRINTER_CREDENTIALS" exit 1 fi # Read credentials from file PRINTER_HOST=$(grep "^printer_name" "$PRINTER_CREDENTIALS" | cut -d'=' -f2 | tr -d ' ') PRINTER_PASSWORD=$(grep "^printer_password" "$PRINTER_CREDENTIALS" | cut -d'=' -f2 | tr -d ' ') # Validate credentials were loaded if [ -z "$PRINTER_HOST" ] || [ -z "$PRINTER_PASSWORD" ]; then echo "Error: Could not read printer_name or printer_password from $PRINTER_CREDENTIALS" exit 1 fi # Check if brother-cert tool exists if [ ! -f "$BROTHER_CERT_TOOL" ]; then echo "Error: brother-cert tool not found at $BROTHER_CERT_TOOL" exit 1 fi # Check if certificate files exist if [ ! -f "$PRIVATE_KEY" ]; then echo "Error: Private key not found at $PRIVATE_KEY" exit 1 fi if [ ! -f "$CERTIFICATE" ]; then echo "Error: Certificate not found at $CERTIFICATE" exit 1 fi echo "Deploying certificate to printer at $PRINTER_HOST..." echo "" # Attempt 1: Try HTTPS first (for printers with valid certificates) echo "Attempting HTTPS connection..." "$BROTHER_CERT_TOOL" \ --hostname "$PRINTER_HOST" \ --password "$PRINTER_PASSWORD" \ --keyfile "$PRIVATE_KEY" \ --certfile "$CERTIFICATE" 2>&1 DEPLOY_EXIT=$? # If HTTPS failed, try HTTP fallback if [ $DEPLOY_EXIT -ne 0 ]; then echo "" echo "⚠ HTTPS connection failed (likely due to invalid/expired certificate on printer)" echo "Retrying with HTTP fallback..." echo "" "$BROTHER_CERT_TOOL" \ --hostname "$PRINTER_HOST" \ --password "$PRINTER_PASSWORD" \ --keyfile "$PRIVATE_KEY" \ --certfile "$CERTIFICATE" \ --http DEPLOY_EXIT=$? fi echo "" if [ $DEPLOY_EXIT -eq 0 ]; then echo "✓ Certificate deployed successfully!" echo "" echo "The printer will restart to activate the new certificate." echo "Please wait a few moments before accessing the printer web interface." echo "" echo "You can verify the certificate by visiting:" echo " https://$PRINTER_HOST" echo " https://$DOMAIN" echo "" echo "==============================================" echo " Certificate Management Complete!" echo "==============================================" echo "" else echo "✗ Certificate deployment failed with exit code: $DEPLOY_EXIT" echo "Please check the error messages above." exit 1 fi ``` ### Cronicle Setup Here is a screenshot of my `Cronicle` setup for deploying this script so it automatically installs a new Brother certificate every month. As previously mentioned, you can also just manually run the script as well To reference the script in `Cronicle`, select `Shell Script` under `Plugin`: ``` #!/bin/sh # Renew printer.owltec.ca certificate monthly sudo /PATH/TO/SCRIPT/printer-cert-master.sh ``` ![[Brother - Cert - 03.webp]] ## Conclusion If you followed my guide successfully, you should now have a Brother printer with a fancy SSL certificate with a proper domain name! ![[Brother - Cert - 05.webp]] You can confirm if the script worked successfully by signing into the admin portal by navigating to `Security` and then selecting `certificate`: ![[Brother - Cert - 04.webp]]