commit - d1172766f4e19c7760b5a64856b4a78bda069262
commit + f436a7665bd7e1aded037f8ec6bb97c6122abd97
blob - /dev/null
blob + a971eaef403f229b6cc8e9cca377c46160b24154 (mode 755)
--- /dev/null
+++ bin/wg_gen
+#!/bin/sh -e
+#
+# Generate WireGuard server and client configs for an entire /24 subnet.
+# Server gets .1, clients get .2 through .254.
+#
+# Usage: wg_gen [-d dns] [-o dir] [-i wgN] wg_server_ip port endpoint
+#
+
+# Show usage and exit.
+usage() {
+ echo "usage: ${0##*/} [-d dns] [-o dir] [-i wgN] wg_server_ip port endpoint" >&2
+ exit 1
+}
+
+# Derive X25519 public key from a base64-encoded private key using only
+# openssl(1). Constructs a PKCS#8 DER-encoded X25519 private key per
+# RFC 8410 (OID 1.3.101.110) and extracts the raw 32-byte public key
+# from the SubjectPublicKeyInfo DER output.
+#
+# Reference: RFC 8410, Section 7 — "Algorithm Identifiers for Ed25519,
+# Ed448, X25519, and X448 for Use in the Internet X.509 Public Key
+# Infrastructure" (https://www.rfc-editor.org/rfc/rfc8410)
+# Prior art:
+# https://gist.github.com/Aleksanaa/9886c9d7d50f1c815400657578ee9a76
+# https://gist.github.com/yrpeng/0b51f6f91931b70f6523db9c2d3ba835
+get_pubkey() {
+ {
+ printf '\x30\x2e\x02\x01\x00\x30\x05\x06\x03\x2b\x65\x6e\x04\x22\x04\x20'
+ echo "$1" | openssl enc -base64 -d
+ } | openssl pkey -inform DER -pubout -outform DER 2>/dev/null \
+ | tail -c 32 \
+ | openssl enc -base64
+}
+
+# Write a standard WireGuard .conf for one client.
+# Arguments: name ip privkey server_pubkey psk allowed_ips endpoint port [dns]
+write_client_conf() {
+ _name="$1" _ip="$2" _privkey="$3"
+ _srv_pubkey="$4" _psk="$5" _aips="$6"
+ _endpoint="$7" _port="$8" _dns="$9"
+
+ cat <<-EOF > "${OUTDIR}/${_name}.conf"
+ [Interface]
+ # ${_name} private key
+ PrivateKey = ${_privkey}
+ Address = ${_ip}/32
+ EOF
+
+ if [ -n "$_dns" ]; then
+ echo "DNS = ${_dns}" >> "${OUTDIR}/${_name}.conf"
+ fi
+
+ cat <<-EOF >> "${OUTDIR}/${_name}.conf"
+
+ [Peer]
+ # ${_endpoint} public key
+ PublicKey = ${_srv_pubkey}
+ PresharedKey = ${_psk}
+ AllowedIPs = ${_aips}
+ Endpoint = ${_endpoint}:${_port}
+ PersistentKeepalive = 25
+ EOF
+}
+
+# Write an OpenBSD hostname.wg for one client.
+# Arguments: name ip privkey server_pubkey psk allowed_ips endpoint port
+write_client_hn() {
+ _name="$1" _ip="$2" _privkey="$3"
+ _srv_pubkey="$4" _psk="$5" _aips="$6"
+ _endpoint="$7" _port="$8" _dns="$9"
+
+ cat <<EOF > "${OUTDIR}/${_name}.hostname.wg"
+# ${_name}
+description "${_name}"
+wgkey ${_privkey}
+inet ${_ip} 255.255.255.0
+up
+
+# Peer ${_endpoint}
+wgpeer ${_srv_pubkey} \\
+ wgdescr "${_endpoint}" \\
+ wgpsk ${_psk} \\
+ wgendpoint ${_endpoint} ${_port} \\
+ wgaip ${_aips} wgpka 25
+EOF
+}
+
+
+#### Argument parsing
+
+DNS=""
+OUTDIR="./wg-configs"
+IFNAME="wg0"
+
+while getopts "d:i:o:" _opt; do
+ case $_opt in
+ d) DNS="$OPTARG";;
+ i) IFNAME="$OPTARG";;
+ o) OUTDIR="$OPTARG";;
+ *) usage;;
+ esac
+done
+shift $((OPTIND - 1))
+
+if [ $# -ne 3 ]; then
+ usage
+fi
+
+SERVER_IP="$1"
+PORT="$2"
+ENDPOINT="$3"
+
+
+#### Validation
+
+# Server IP must end in .1.
+_base="${SERVER_IP%.*}"
+_last="${SERVER_IP##*.}"
+if [ "$_last" != "1" ]; then
+ echo "${0##*/}: server_ip must end in .1 (got ${SERVER_IP})" >&2
+ exit 1
+fi
+
+# Derive the /24 subnet base for AllowedIPs.
+SUBNET="${_base}.0/24"
+
+# Set AllowedIPs based on DNS flag.
+if [ -n "$DNS" ]; then
+ ALLOWED_IPS="0.0.0.0/0"
+else
+ ALLOWED_IPS="${SUBNET}"
+fi
+
+
+#### Setup
+
+mkdir -p "$OUTDIR"
+
+# Generate server keypair.
+SERVER_PRIVKEY=$(openssl rand -base64 32)
+SERVER_PUBKEY=$(get_pubkey "$SERVER_PRIVKEY")
+
+# Start building the server hostname.if config.
+SERVER_HN="# ${IFNAME}
+description \"${IFNAME}\"
+wgkey ${SERVER_PRIVKEY}
+wgport ${PORT}
+inet ${SERVER_IP} 255.255.255.0
+up
+"
+
+
+#### Main loop — generate client configs and accumulate server peers.
+
+_i=2
+while [ $_i -le 254 ]; do
+ _client_name="client-${_i}"
+ _client_ip="${_base}.${_i}"
+
+ # Generate client keypair and preshared key.
+ _client_privkey=$(openssl rand -base64 32)
+ _client_pubkey=$(get_pubkey "$_client_privkey")
+ _psk=$(openssl rand -base64 32)
+
+ # Write client configs.
+ write_client_conf "$_client_name" "$_client_ip" "$_client_privkey" \
+ "$SERVER_PUBKEY" "$_psk" "$ALLOWED_IPS" "$ENDPOINT" "$PORT" "$DNS"
+
+ write_client_hn "$_client_name" "$_client_ip" "$_client_privkey" \
+ "$SERVER_PUBKEY" "$_psk" "$ALLOWED_IPS" "$ENDPOINT" "$PORT"
+
+ # Append peer block to server config.
+ SERVER_HN="${SERVER_HN}
+# Peer ${_client_name}
+wgpeer ${_client_pubkey} \\
+ wgdescr \"${_client_name}\" \\
+ wgpsk ${_psk} \\
+ wgaip ${_client_ip}/32
+"
+
+ _i=$((_i + 1))
+done
+
+
+#### Write server config.
+
+echo "$SERVER_HN" > "${OUTDIR}/hostname.${IFNAME}"
+
+
+#### Permissions — configs contain private keys.
+
+echo "Generated configs in ${OUTDIR}/ for ${_base}.0/24 (253 clients)"