Skip to content

Instantly share code, notes, and snippets.

@mosquito
Last active November 15, 2022 08:33
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mosquito/a3b392557a7ef171e65fe5fc61b96c2f to your computer and use it in GitHub Desktop.
Save mosquito/a3b392557a7ef171e65fe5fc61b96c2f to your computer and use it in GitHub Desktop.
Wireguard client config python generator

Wireguard client config generator

Generates config files for mobile apps (qr code) and text configs

Installation

$ python3.9 -m venv .venv
$ .venv/bin/pip install colorama configargparse cryptography qrcode yaml
$ .venv/bin/python wireguard-gen.py --help
usage: wireguard-gen.py [-h] [-c CONFIG] [--qr] [--ipv4-subnet IPV4_SUBNET] [--ipv6-subnet IPV6_SUBNET] [--endpoint ENDPOINT] [--server-public-key SERVER_PUBLIC_KEY]
                        [--mtu MTU] [--table TABLE] [--dns DNS [DNS ...]] [--allowed-ips ALLOWED_IPS [ALLOWED_IPS ...]] [--listen-port LISTEN_PORT]
                        [--persistent-keepalive PERSISTENT_KEEPALIVE] [--use-preshared-key]
                        client_number

Wireguard client config generator

optional arguments:
  -h, --help            show this help message and exit
  -c CONFIG, --config CONFIG

Client configuration options:
  client_number         IP addresses shift
  --qr                  print cinfiguration as QRCode [env var: WG_QR] (default: False)

Server options:
  --ipv4-subnet IPV4_SUBNET
                        [env var: WG_IPV4_SUBNET] (default: None)
  --ipv6-subnet IPV6_SUBNET
                        [env var: WG_IPV6_SUBNET] (default: None)
  --endpoint ENDPOINT   [env var: WG_ENDPOINT] (default: None)
  --server-public-key SERVER_PUBLIC_KEY
                        [env var: WG_SERVER_PUBLIC_KEY] (default: None)
  --mtu MTU             [env var: WG_MTU] (default: None)
  --table TABLE         [env var: WG_TABLE] (default: None)
  --dns DNS [DNS ...]   [env var: WG_DNS] (default: None)
  --allowed-ips ALLOWED_IPS [ALLOWED_IPS ...]
                        [env var: WG_ALLOWED_IPS] (default: ['0.0.0.0/0', '::/0'])
  --listen-port LISTEN_PORT
                        [env var: WG_LISTEN_PORT] (default: 0)
  --persistent-keepalive PERSISTENT_KEEPALIVE
                        [env var: WG_PERSISTENT_KEEPALIVE] (default: 0)
  --use-preshared-key   [env var: WG_USE_PRESHARED_KEY] (default: False)

Args that start with '--' (eg. --qr) can also be set in a config file (specified via -c). Config file syntax allows: key=value, flag=true, stuff=[a,b,c] (for details, see
syntax at https://goo.gl/R74nmi). If an arg is specified in more than one place, then commandline values override environment variables which override config file values
which override defaults.

Define server parameters

create file which describes server parameters

# wireguard-test.conf
dns=["1.1.1.1"]
endpoint = test.example.com:51820
ipv4-subnet = 10.255.0.0/24
ipv6-subnet = fe00::/64
persistent-keepalive = 25
server-public-key = LDR4+9cmEZx2VyqFOOr3rGZEoG+UrO2TSUB8gWP5+20=
use-preshared-key = 1

Create a client

$ .vnv/bin/python wireguard-gen.py -c wireguard-test.conf 2 --qr
Server config:

 
peers:
- AllowedIPs: 10.255.0.2/32,fe00::2/128
  PresharedKey: dfSFGiGibwT5LonAn7/sp+arf1DoFjL2l8jTk22tq0A=
  PublicKey: I4EZx5zQYqKSjGNEcHnOn96hQS32JQbtSlpcJVJpOR8=


Client config:

 
[Interface]
Address = 10.255.0.2/32,fe00::2/128
DNS = 1.1.1.1
PrivateKey = UHuvxk0p4sKZqOaUpDLCVUNmKOKktrtpvzjRC0p1wHI=

[Peer]
AllowedIPs = 0.0.0.0/0,::/0
Endpoint = test.example.com:51820
PersistentKeepalive = 25
PresharedKey = dfSFGiGibwT5LonAn7/sp+arf1DoFjL2l8jTk22tq0A=
PublicKey = LDR4+9cmEZx2VyqFOOr3rGZEoG+UrO2TSUB8gWP5+20=
import argparse
import base64
import configparser
import io
import os
from ipaddress import IPv4Network, IPv6Network
import qrcode
import yaml
from colorama import Back, Fore, Style
from configargparse import ArgumentParser
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
parser = ArgumentParser(
auto_env_var_prefix="WG_",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="Wireguard client config generator",
)
parser.add("-c", "--config", required=False, is_config_file=True)
group = parser.add_argument_group("Client configuration options")
group.add_argument("client_number", help="IP addresses shift", type=int)
group.add_argument("--qr", help="print cinfiguration as QRCode", action="store_true")
group = parser.add_argument_group("Server options")
group.add_argument("--ipv4-subnet", type=IPv4Network)
group.add_argument("--ipv6-subnet", type=IPv6Network)
group.add_argument("--endpoint", required=True)
group.add_argument("--server-public-key", required=True)
group.add_argument("--mtu")
group.add_argument("--table")
group.add_argument("--dns", nargs="+")
group.add_argument("--allowed-ips", nargs="+", default=["0.0.0.0/0", "2000::/3"])
group.add_argument("--listen-port", type=int, default=0)
group.add_argument("--persistent-keepalive", type=int, default=0)
group.add_argument("--use-preshared-key", action="store_true")
def get_host(net, hostnum):
for idx, host in enumerate(net.hosts()):
if idx == hostnum - 1:
return f"{host}/{net.max_prefixlen}"
def keygen():
private = X25519PrivateKey.generate()
return (
base64.b64encode(
private.private_bytes(
serialization.Encoding.Raw,
serialization.PrivateFormat.Raw,
serialization.NoEncryption(),
),
).decode(),
base64.b64encode(
private.public_key().public_bytes(
serialization.Encoding.Raw,
serialization.PublicFormat.Raw,
),
).decode(),
)
def pskgen():
return base64.b64encode(
X25519PrivateKey.generate().public_key().public_bytes(
serialization.Encoding.Raw,
serialization.PublicFormat.Raw,
),
).decode()
def strip_section(kv):
for key, value in kv.items():
if value:
yield key, value
def main():
args = parser.parse_args()
private, public = keygen()
psk = pskgen() if args.use_preshared_key else ""
addresses = []
for subnet in filter(None, (args.ipv4_subnet, args.ipv6_subnet)):
addresses.append(get_host(subnet, args.client_number))
interface_section = {
"Address": ",".join(addresses),
"DNS": ",".join(args.dns),
"ListenPort": args.listen_port,
"MTU": args.mtu,
"PrivateKey": private,
"Table": args.table,
}
peer_section = {
"AllowedIPs": ",".join(args.allowed_ips),
"Endpoint": args.endpoint,
"PersistentKeepalive": args.persistent_keepalive,
"PresharedKey": psk,
"PublicKey": args.server_public_key,
}
config = configparser.RawConfigParser()
config.optionxform = str
config["Interface"] = dict(strip_section(interface_section))
config["Peer"] = dict(strip_section(peer_section))
with io.StringIO() as fp:
config.write(fp)
config_ini = fp.getvalue()
print("Server config:\n")
print(Fore.LIGHTWHITE_EX, Back.LIGHTBLACK_EX)
print(
yaml.dump({
"peers": [dict(strip_section({
"AllowedIPs": ",".join(addresses),
"PublicKey": public,
"PresharedKey": psk,
}))],
}),
)
print(Style.RESET_ALL)
print("Client config:\n")
if args.qr:
qr = qrcode.QRCode()
qr.add_data(config_ini)
print(Fore.BLACK, Back.LIGHTWHITE_EX)
qr.print_ascii()
print(Style.RESET_ALL)
else:
print(Fore.LIGHTWHITE_EX, Back.LIGHTBLACK_EX)
print(config_ini)
print(Style.RESET_ALL)
if __name__ == '__main__':
main()
@mosquito
Copy link
Author

Client config as QR Code example

python wireguard-gen.py -c wireguard-test.conf 2 --qr

QR Code example

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment