Commit Diff


commit - d1172766f4e19c7760b5a64856b4a78bda069262
commit + f436a7665bd7e1aded037f8ec6bb97c6122abd97
blob - /dev/null
blob + a971eaef403f229b6cc8e9cca377c46160b24154 (mode 755)
--- /dev/null
+++ bin/wg_gen
@@ -0,0 +1,192 @@
+#!/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)"