feat(labelprint): 4x6 label print proxy on a Raspberry Pi
A Pi 3B+ (stickah.local) shares a Phomemo PM246 to the LAN as a plain CUPS queue, so any machine can print 4x6 labels -- fulfillr-site's shipping labels in particular -- without installing the vendor driver, which is x86-64 only. The role builds the TSPL CUPS driver from source instead. It is Debian, not Fedora, so it lives in its own inventory and playbook (make deploy-labelprint / check-labelprint) and the home.debyl.io roles can never run against it. make bootfs renders its cloud-init first-boot files onto a freshly imaged SD card from the same templates the role uses. The Wi-Fi credentials for the home and rescue networks are in the vault. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
be0d02b938
commit
6cd4d56de1
@@ -0,0 +1,11 @@
|
||||
# {{ ansible_managed }} -- rendered by `make bootfs`
|
||||
#
|
||||
# cloud-init applies user-data once per INSTANCE, not once per boot: rewrite
|
||||
# user-data on a card that has already booted and nothing happens, because
|
||||
# cloud-init recognises the instance id and skips straight to per-boot modules.
|
||||
#
|
||||
# Deriving the id from a hash of user-data itself fixes that. Re-running
|
||||
# `make bootfs` with no changes leaves the id alone, so a card keeps its
|
||||
# identity; change anything in user-data and the id changes with it, and the
|
||||
# Pi re-applies the new config on its next boot without a reflash.
|
||||
instance-id: {{ labelprint_hostname }}-{{ labelprint_user_data_id }}
|
||||
@@ -0,0 +1,18 @@
|
||||
# {{ ansible_managed }} -- rendered by `make bootfs`
|
||||
#
|
||||
# Ethernet only, on purpose. Wi-Fi is NOT configured here: cloud-init renders
|
||||
# this through netplan into a persistent profile of netplan's own, which is not
|
||||
# a file this repo manages or can readily update. The Wi-Fi profiles are written
|
||||
# straight into /etc/NetworkManager/system-connections by user-data instead, so
|
||||
# the image and Ansible manage the same files.
|
||||
#
|
||||
# A card that has already booted with Wi-Fi in network-config keeps netplan's
|
||||
# profile. That is fine, and useful -- see the "Coexisting with netplan" note in
|
||||
# roles/labelprint/tasks/wifi.yml.
|
||||
network:
|
||||
version: 2
|
||||
ethernets:
|
||||
eth0:
|
||||
dhcp4: true
|
||||
dhcp6: true
|
||||
optional: true
|
||||
@@ -0,0 +1,125 @@
|
||||
#cloud-config
|
||||
# {{ ansible_managed }} -- rendered by `make bootfs BOOTFS=...`
|
||||
#
|
||||
# First boot of the label print proxy. The job of this file is to make the Pi
|
||||
# REACHABLE and nothing more: a login that works, a network that works, and a
|
||||
# rescue access point for when it does not. The printer driver and the CUPS
|
||||
# queue are Ansible's job -- see roles/labelprint/.
|
||||
#
|
||||
# The Wi-Fi profiles and the rescue watchdog below are rendered from the very
|
||||
# same templates the role uses, so the first `make deploy-labelprint` reports no
|
||||
# change on any of them. That is the point: the Pi can already rescue itself
|
||||
# before Ansible has ever run.
|
||||
#
|
||||
# Applies on FIRST BOOT ONLY. Rewriting this file on a card that has already
|
||||
# booted does nothing -- reflash the image.
|
||||
|
||||
hostname: {{ labelprint_hostname }}
|
||||
manage_etc_hosts: true
|
||||
manage_resolv_conf: false
|
||||
timezone: America/New_York
|
||||
keyboard:
|
||||
model: pc105
|
||||
layout: "us"
|
||||
|
||||
apt:
|
||||
preserve_sources_list: true
|
||||
|
||||
users:
|
||||
- name: {{ labelprint_user }}
|
||||
shell: /bin/bash
|
||||
lock_passwd: false
|
||||
# Deliberately trivial, and deliberately not hashed: this password is
|
||||
# documented in roles/labelprint/README.md, so a hash of it would protect
|
||||
# nothing while pretending otherwise. It exists so that a Pi which will not
|
||||
# take the SSH key -- wrong key, reflashed card, someone else's laptop -- is
|
||||
# still reachable from the LAN or the rescue AP, which is the only place it
|
||||
# can be reached from at all (see templates/nftables.conf.j2).
|
||||
plain_text_passwd: {{ labelprint_user_password }}
|
||||
sudo: "ALL=(ALL) NOPASSWD:ALL"
|
||||
groups: [sudo, adm, lpadmin, plugdev, dialout, netdev, users]
|
||||
ssh_authorized_keys:
|
||||
- {{ lookup('file', labelprint_authorized_key_file) }}
|
||||
|
||||
# The key is what Ansible actually uses; the password is the fallback.
|
||||
ssh_pwauth: true
|
||||
disable_root: true
|
||||
chpasswd:
|
||||
expire: false
|
||||
|
||||
# Pre-installing what the role needs makes the first deploy quick, and means a
|
||||
# Pi that comes up on the rescue AP with no internet still has cups and
|
||||
# NetworkManager. Ansible installs the same list, so nothing here is load-bearing.
|
||||
package_update: true
|
||||
packages:
|
||||
{% for pkg in labelprint_deps %}
|
||||
- {{ pkg }}
|
||||
{% endfor %}
|
||||
|
||||
write_files:
|
||||
# Stop cloud-init rewriting the network on a future re-instance.
|
||||
- path: /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg
|
||||
owner: root:root
|
||||
permissions: '0644'
|
||||
content: |
|
||||
network: {config: disabled}
|
||||
|
||||
# Belt and braces. netplan can hand NetworkManager an "only manage what I
|
||||
# listed" policy, and what this image lists is eth0; a stock Raspberry Pi OS
|
||||
# leaves that policy empty, but an unmanaged wlan0 would take the rescue AP
|
||||
# down with it, so it is not worth depending on.
|
||||
- path: /etc/NetworkManager/conf.d/10-labelprint.conf
|
||||
owner: root:root
|
||||
permissions: '0644'
|
||||
content: |
|
||||
[keyfile]
|
||||
unmanaged-devices=none
|
||||
|
||||
[device]
|
||||
# A randomised MAC would give the Pi a different DHCP lease on every
|
||||
# association, which makes it hard to find on the UDM Pro's client list.
|
||||
wifi.scan-rand-mac-address=no
|
||||
|
||||
- path: /etc/NetworkManager/system-connections/home-wifi.nmconnection
|
||||
owner: root:root
|
||||
permissions: '0600'
|
||||
content: |
|
||||
{{ lookup('template', 'roles/labelprint/templates/home-wifi.nmconnection.j2') | indent(6) }}
|
||||
|
||||
- path: /etc/NetworkManager/system-connections/rescue-ap.nmconnection
|
||||
owner: root:root
|
||||
permissions: '0600'
|
||||
content: |
|
||||
{{ lookup('template', 'roles/labelprint/templates/rescue-ap.nmconnection.j2') | indent(6) }}
|
||||
|
||||
- path: /usr/local/sbin/wifi-rescue
|
||||
owner: root:root
|
||||
permissions: '0755'
|
||||
content: |
|
||||
{{ lookup('template', 'roles/labelprint/templates/wifi-rescue.sh.j2') | indent(6) }}
|
||||
|
||||
- path: /etc/systemd/system/wifi-rescue.service
|
||||
owner: root:root
|
||||
permissions: '0644'
|
||||
content: |
|
||||
{{ lookup('template', 'roles/labelprint/templates/wifi-rescue.service.j2') | indent(6) }}
|
||||
|
||||
- path: /etc/systemd/system/wifi-rescue.timer
|
||||
owner: root:root
|
||||
permissions: '0644'
|
||||
content: |
|
||||
{{ lookup('template', 'roles/labelprint/templates/wifi-rescue.timer.j2') | indent(6) }}
|
||||
|
||||
runcmd:
|
||||
# RPi OS soft-blocks the radio until a regulatory domain is known. cmdline.txt
|
||||
# carries cfg80211.ieee80211_regdom=US, but unblock anyway -- a blocked radio
|
||||
# is the one failure that takes the rescue AP down with it.
|
||||
- [rfkill, unblock, wifi]
|
||||
- [systemctl, enable, --now, ssh]
|
||||
- [systemctl, enable, --now, avahi-daemon]
|
||||
# Let NetworkManager pick up the keyfiles written above. The profile netplan
|
||||
# renders from network-config is left alone on purpose -- see the "Coexisting
|
||||
# with netplan" note in roles/labelprint/tasks/wifi.yml.
|
||||
- [nmcli, connection, reload]
|
||||
- [systemctl, daemon-reload]
|
||||
- [systemctl, enable, --now, wifi-rescue.timer]
|
||||
Reference in New Issue
Block a user