Adding New WireGuard Clients

I like to run a few things from home, like a Nextcloud server and a Bitwarden server, for example. I also like for my home infrastructure to be secure though, so I don't allow access to these services from outside of my LAN. This can be a problem if I need something from one of my self-hosted services and am away from home, however, so in order to provide myself with secure access, I've got a WireGuard server running, which serves as the sole point of entry to my LAN from the outside world. This works great, except I can never remember quite how to grant additional clients access to my WireGuard server, seeing as it's not exactly something I do everyday. As such, this seemed like just the thing to automate away with a script so that I only need to remember a single command in the future. The script assumes a configuration directory as follows:

/etc/wireguard
├── wg0.conf
└── clients
    ├── client0.conf
    └── client1.conf

Below is the script, though note that you'll need to modify lines 22 and 24 to use the correct information for your WireGuard server.

#!/usr/bin/env python3

import os
from subprocess import run, PIPE

# 10.0.0.1 is the server, so we want num clients + 2 to get an available IP
# address
clients = len(os.listdir('clients')) + 2

client_name = input("Enter a name for the new client: ")
client_conf_file = os.path.join('clients', '{}.conf'.format(client_name))

private_key_result = run(['wg', 'genkey'], stdout=PIPE, stderr=PIPE)
private_key = private_key_result.stdout.decode('utf-8').strip()

client_config = '''[Interface] # {client_name}
PrivateKey = {private_key}
Address = 10.0.0.{clients}/24
#DNS = 192.168.86.48 # Uncomment this line to use your own DNS server

[Peer]
PublicKey = # Add your WireGuard server's public key here
AllowedIPs = 0.0.0.0/0
Endpoint = # Add your WireGuard server's public IP address and port here

'''.format(client_name=client_name, private_key=private_key, clients=clients)

with open(client_conf_file, 'w+') as conf:
    conf.write(client_config)

print('Scan the following barcode with your phone to import the client config:')

print(run(
    ['qrencode', '-t', 'ansiutf8', '-r', client_conf_file],
    stdout=PIPE,
    stderr=PIPE
    ).stdout.decode('utf=8'))

print('Alternatively, you can enter the config manually using this file:')
print(client_config)

public_key_result = run(
        ['wg', 'pubkey'],
        input=private_key_result.stdout,
        stdout=PIPE,
        stderr=PIPE
        )
public_key = public_key_result.stdout.decode('utf-8').strip()

with open('wg0.conf', 'a') as conf:
    conf.write('''[Peer] # {client_name}
PublicKey = {public_key}
AllowedIPs = 10.0.0.{clients}/32

'''.format(client_name=client_name, public_key=public_key, clients=clients))

In addition to automatically creating the client config and appending the corresponding info to the server's config, line 33 will also print out a QR code to easily import the config into a mobile app, and line 40 will print the config if you prefer to just copy/paste the data into the app. Line 28 will persist the client config to disk for later retrieval, so if you don't want that be sure to delete it. Hope that helps, and please reach out if you have suggestions on how to improve it!