$ genqr-wifi.sh mynetworkname
Wi-Fi network: mynetworkname
█████████████████████████████████████████
█████████████████████████████████████████
████ ▄▄▄▄▄ █ █▄▄█ ▄▀▄ █▀ ▄█ ▄▄▄▄▄ ████
████ █ █ █ ▀▄ █ █▀█▄▀▄▀▀ ▀██ █ █ ████
████ █▄▄▄█ █▀██▀▀▄▀ ▄▀█ ▀▀▄█▄█ █▄▄▄█ ████
████▄▄▄▄▄▄▄█▄▀▄█ █▄█▄█▄▀ ▀ ▀ █▄▄▄▄▄▄▄████
████▄▄██▄ ▄█▀▀▀▄▀ █▀▄█▄ ██▀▀█▀ █ ▄▄▄████
████ ▀▄ █▄▄▄█▀ ▄ ▄█▄█▄▀ █▀█▄ ▀▄▀▄ ▄▀████
████▄█▀▀ ▄▄█▄▀▄█▄ ██▄▀ █▄▀██▀ ▀ ▀▀█▄████
████ ▄▄▀██▄▄ ▀▀█▀▄▄▄ █▄▀▀█▀▄▀ ▀▄▀▄█ ████
████▀ ▄ ▀▄▄▀█ ▄▄▀ ██▄▄▀▄█▄▄███▄▄▀▀█ █████
████▀█▀▀█▀▄▀█▀ ▄▄ ▄▄▀▄▀ ▀▀█▀▄▀█▀█▄ █████
████▀▄█▄█ ▄▄ █▄█▄▀ ███▄ █ █▀██ ▀█ ▀▄████
██████ ▀ ▄▄ ▄▀█▀▄▄▄ ▀█▀█▀█▄▀ ▀▄▀█ ▀▀████
████▄▄█▄▄█▄▄▀ █▄ ██ █ █▄ █ ▄▄▄ █ ▄▀████
████ ▄▄▄▄▄ █▀██ █▄█▄▄█▄▀ ▀ █ █▄█ ▀ █████
████ █ █ █▄▀ ▀▀ ▄▀▀█ ▄███▄▄ ▄ ▀▀ ████
████ █▄▄▄█ █▀ ▀█▄ ▀▄█▀▄ ▀███▀▀▄▄█▀██▀████
████▄▄▄▄▄▄▄█▄█████▄█▄█▄▄█████▄███▄█▄▄████
█████████████████████████████████████████
█████████████████████████████████████████
Cool, huh!? 😎
Concepts and security concerns
Credentials management
Firstly, I'd like to highlight the value of generating something locally on your machine rather than a random app or
site.
Entering credentials in online QR code generators come with significant risks as you basically enter them on an
untrusted website.
Even if they promise generating them in a local Javascript-only fashion, I consider it a bad practice to enter these
credentials in a browser window.
This post is about generating the QR code locally (with qrencode
) and credentials won't leave your system.
Secondly, the use of a local password manager application that allows for integration in shell scripts. I use pass, and I have the passwords for Wi-Fi networks and keys for Wireguard tunnels in there. The use of this as the password manager allows for easy integration in shell scripts in its design. But technically you could use any password store that allows you to obtain the secrets needed in shell scripts.
In this post I'll demo two example applications for this: Wi-Fi passwords (phones typically support that nowadays) and setting up a Wireguard tunnel (as supported by the Wireguard app for Android).
At work
This could also be very useful in the office to quickly on-board a new team member for example!
QR codes specification
Technically, it's possible to encode anything as a QR code, it's basically just freeform with respect to QR. What's more important, though, is how to encode the text you feed the QR-encoder.
For Wi-Fi access there's a standard on how it's structured; it is available in the Wi-Fi Alliance specification document section 7.
Some examples of the WIFI URI format are as follows:
WIFI:T:WPA;S:MyNet;P:MyPassword;;
- STA that supports WPA3-Personal might use SAE or PSK (WPA3-Personal transition mode)
- STA that does not support WPA3-Personal uses PSK (WPA2-Personal)
For Wireguard tunnels in the app it's just the whole configuration file (that you'd normally use with wg setconf
)
contents itself.
With the text you want to encode ready, you can then use any QR code generator. One example is LibreOffice Writer (Menu Insert → Object → QR Code).
Scripts
These scripts should be pretty much self-descriptive.
They just format a text entry and just pipe it to qrencode --type=ANSIUTF8
, so you'll have to install qrencode
on
your system.
Put the scripts somewhere on your PATH
, make them executable (e.g. chmod +x genqr-wifi.sh
) and make sure to adjust
the logic to your needs and situation to obtain the credentials.
Wi-Fi
genqr-wifi.sh
:
#!/usr/bin/env bash
set -e -u
set -o pipefail
# Obtain Wi-Fi password from `wifi/<SSID>` entry in passwordstore
# and print its QR-code for access on the commandline using ANSI
# terminal format.
function show_wifi_qr() {
# TODO: figure out how to properly escape SSID and password
# special characters.
WPA3_FLAG= # set "R:1;" to indicate WPA-3 compatibility
WIFI_SSID="$1"
WIFI_PASS="$(pass show "wifi/${WIFI_SSID}" | head -n1)"
QR_CODE_TEXT="WIFI:T:WPA;${WPA3_FLAG}S:${WIFI_SSID};P:${WIFI_PASS};;"
echo "Wi-Fi network: ${WIFI_SSID}"
echo -n "${QR_CODE_TEXT}" | qrencode --type=ANSIUTF8
}
show_wifi_qr "$1"
Wireguard
This assumes a pass
layout like this, but should be trivial to adjust to your needs.
The entry should contain the private key as printed with wg genkey
and the psk
shared secret I use for additional
security, contents as printed by wg genpsk
.
$ pass ls
Password Store
└── wireguard
├── myinstance
│ ├── mydevice
│ ├── myotherdevice
│ ├── myserver
│ └── psk
[...]
genqr-wireguard.sh
:
#!/usr/bin/env bash
set -e -u
set -o pipefail
function prepare_wireguard_credentials() {
INSTANCE_NAME=$1
DEVICE_NAME=$2
DEVICE_IP_ADDRESS=$3
DEVICE_DNS_SERVERS=$4
PEER_INVENTORY_NAME=$5
PEER_ENDPOINT=$6
PEER_ALLOWED_IPS=$7
DEVICE_PRIVATE_KEY=$(pass show "wireguard/${INSTANCE_NAME}/${DEVICE_NAME}" | head -n1)
PEER_PUBLIC_KEY=$(pass show "wireguard/${INSTANCE_NAME}/${PEER_INVENTORY_NAME}" | head -n1 | wg pubkey)
PEER_PRESHAREDKEY=$(pass show "wireguard/${INSTANCE_NAME}/psk" | head -n1)
}
function make_wireguard_config() {
WIREGUARD_CONFIG="# ${INSTANCE_NAME} for device ${DEVICE_NAME}
[Interface]
PrivateKey = ${DEVICE_PRIVATE_KEY}
Address = ${DEVICE_IP_ADDRESS}
DNS = ${DEVICE_DNS_SERVERS}
[Peer]
PublicKey = ${PEER_PUBLIC_KEY}
PresharedKey = ${PEER_PRESHAREDKEY}
Endpoint = ${PEER_ENDPOINT}
AllowedIPs = ${PEER_ALLOWED_IPS}
"
}
function show_wireguard_qr() {
prepare_wireguard_credentials "$@"
make_wireguard_config
echo "Wireguard instance: ${INSTANCE_NAME} for device ${DEVICE_NAME}"
echo -n "${WIREGUARD_CONFIG}" | qrencode --type=ANSIUTF8
}
show_wireguard_qr myinstance mydevice 10.1.2.3/24 "8.8.8.8 9.9.9.9" \
myserver 11.22.33.44:51820 0.0.0.0/0
show_wireguard_qr myinstance myotherdevice 10.1.2.4/24 "8.8.8.8 9.9.9.9" \
myserver 11.22.33.44:51820 0.0.0.0/0
Results
Now whenever someone asks me for access on my Wi-Fi with a phone 📱, I can just type the following and it will always include the current password. ✅
For Wireguard I like it in particular just for my own phone. Whenever I change the configuration or rotate keys, I can just delete it and scan the new one with ease. 😎
At work, I create a git repository that includes the script and password store files, add the use direnv to
adjust the PASSWORD_STORE_DIR
and PATH
and the possibilities are endless!