mirror of
https://github.com/towalink/wgfrontend.git
synced 2025-08-27 15:21:14 +00:00
179 lines
7.6 KiB
Python
179 lines
7.6 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
import getpass
|
|
import grp
|
|
import os
|
|
import pwd
|
|
import string
|
|
import wgconfig
|
|
import wgconfig.wgexec as wgexec
|
|
|
|
import config
|
|
|
|
|
|
def is_root():
|
|
"""Returns whether this script is run with user is 0 (root)"""
|
|
return os.getuid() == 0
|
|
|
|
def get_user():
|
|
"""Returns the effective user that executes this script"""
|
|
return getpass.getuser()
|
|
|
|
def check_user(username):
|
|
"""Returns a boolean that indicates if the given user exists on the system"""
|
|
try:
|
|
pwd.getpwnam(username)
|
|
return True
|
|
except KeyError:
|
|
return False
|
|
|
|
def create_user(username):
|
|
"""Create the given user on the system"""
|
|
if os.path.exists('/usr/sbin/useradd'):
|
|
os.system(f'useradd {username}')
|
|
else:
|
|
os.system(f'adduser {username} -D')
|
|
|
|
def ensure_user(username):
|
|
"""Ensures that the given user exists on the system"""
|
|
if not check_user(username):
|
|
create_user(username)
|
|
|
|
def check_wg():
|
|
"""Check whether the wg tool is present"""
|
|
return os.path.isfile('/usr/bin/wg')
|
|
|
|
def check_wgquick():
|
|
"""Check whether the wg-quick tool is present"""
|
|
return os.path.isfile('/usr/bin/wg-quick')
|
|
|
|
def touch_file(filename, perm=0o640):
|
|
"""Touch the given file with the provided permissions"""
|
|
if not os.path.exists(os.path.dirname(filename)):
|
|
os.makedirs(os.path.dirname(filename))
|
|
with os.fdopen(os.open(filename, os.O_WRONLY | os.O_CREAT, perm), 'w') as handle:
|
|
pass
|
|
|
|
def check_validcharacters(value, valid_chars=string.ascii_letters):
|
|
"""Checks whether the provided string only contains the listed characters"""
|
|
invalid = [ k for k in value if k not in valid_chars ]
|
|
if invalid:
|
|
return False
|
|
return True
|
|
|
|
def chown(username, path):
|
|
"""Change file/path ownership to given user"""
|
|
uid = pwd.getpwnam(username).pw_uid
|
|
os.chown(path, uid, -1)
|
|
|
|
def drop_privileges(uid_name='nobody', gid_name='nogroup'):
|
|
""""""
|
|
if not is_root():
|
|
raise ValueError('No privileges present to drop')
|
|
uid = pwd.getpwnam(uid_name).pw_uid
|
|
gid = grp.getgrnam(gid_name).gr_gid
|
|
# os.setgroups([]) # remove group privileges
|
|
os.setgid(gid)
|
|
os.setuid(uid)
|
|
|
|
def setup_environment():
|
|
"""Environment setup assistant"""
|
|
cfg = config.Configuration()
|
|
if is_root():
|
|
print('Welcome to Towalink WireGuard Frontend')
|
|
print('======================================')
|
|
print('You are executing "wgfrontend" as root user. We\'ll now make sure that everything is properly installed.')
|
|
if check_wg():
|
|
print(f'1a) Wireguard (wg) is available. Ok.')
|
|
else:
|
|
print(f'1a) Wireguard (wg) is not available. FAIL.')
|
|
if check_wg():
|
|
print(f'1b) Wireguard (wg-quick) is available. Ok.')
|
|
else:
|
|
print(f'1b) Wireguard (wg-quick) is not available. FAIL.')
|
|
if cfg.exists():
|
|
print(f'2) Config file {cfg.filename} already exists. Ok.')
|
|
else:
|
|
print(f'2) Config file {cfg.filename} does not yet exist. Let\'s create one...')
|
|
print(f' Press enter to select defaults.')
|
|
wg_configfile = input(f'2a) Please specify the WireGuard config file to be used [/etc/wireguard/wg_rw.conf]: ')
|
|
user = input(f'2b) Please specify the system user for the web frontend [wgfrontend]: ')
|
|
ok = False
|
|
while not ok:
|
|
username = input(f'2c) Please specify the username for your web frontend user [admin]: ')
|
|
if check_validcharacters(username, string.ascii_letters + '_'):
|
|
ok = True
|
|
else:
|
|
print(' Username must only contain letters and underscores. Please enter anew.')
|
|
ok = False
|
|
while not ok:
|
|
password = input(f'2d) Please specify the password for your web frontend user: ')
|
|
if len(password) >= 8:
|
|
ok = True
|
|
else:
|
|
print(' Password must have at least eight characters. Please enter anew.')
|
|
touch_file(cfg.filename, perm=0o640) # create without world read permissions
|
|
cfg.write_config(wg_configfile=wg_configfile, user=user, users={username: password})
|
|
print(' Config file written. Ok.')
|
|
print(f'3) Ensuring that system user "{cfg.user}" exists.')
|
|
ensure_user(cfg.user)
|
|
print(f'4) Ensuring ownership of config file {cfg.filename}.')
|
|
chown(cfg.user, cfg.filename)
|
|
if os.path.exists(cfg.libdir):
|
|
print(f'5) Directory {cfg.libdir} already exists. Ok.')
|
|
else:
|
|
print(f'5) Directory {cfg.libdir} does not yet exist. Let\'s create it...')
|
|
os.makedirs(cfg.libdir, mode=0o640, exist_ok=True)
|
|
print(' Directory created. Ok.')
|
|
print(f'6) Ensuring ownership of directory {cfg.libdir}.')
|
|
chown(cfg.user, cfg.libdir)
|
|
if os.path.exists(cfg.wg_configfile):
|
|
print(f'7) WireGuard config file {cfg.wg_configfile} already exists. Ok.')
|
|
else:
|
|
print(f'7) WireGuard config file {cfg.wg_configfile} does not yet exist. Let\'s create one...')
|
|
wg_listenport = input(f'7a) Please specify the listen port of the WireGuard interface [51820]: ')
|
|
if not wg_listenport.strip():
|
|
wg_listenport = 51820
|
|
ok = False
|
|
while not ok:
|
|
endpoint = input(f'7b) Please specify the endpoint hostname (and optionally port) to reach your WireGuard server: ')
|
|
if len(endpoint) > 0:
|
|
ok = True
|
|
else:
|
|
print(' You need to enter an endpoint hostname.')
|
|
wg_address = input(f'7c) Please specify the IP address of the WireGuard interface incl. prefix length [192.168.0.1/24]: ')
|
|
if not wg_address.strip():
|
|
wg_address = '192.168.0.1/24'
|
|
wg_networks = input(f'7d) Please specify the network ranges that the clients shall route to the WireGuard server [192.168.0.0/16]: ')
|
|
if not wg_networks.strip():
|
|
wg_networks = '192.168.0.0/16'
|
|
wc = wgconfig.WGConfig(cfg.wg_configfile)
|
|
wc.initialize_file('# This file has been created and is managed by wgfrontend. Only change manually if you know what you\'re doing.')
|
|
if not ':' in endpoint:
|
|
endpoint += ':51820'
|
|
wc.add_attr(None, 'ListenPort', wg_listenport, '# Endpoint = ' + endpoint, append_as_line=True)
|
|
wc.add_attr(None, 'PrivateKey', wgexec.generate_privatekey())
|
|
wc.add_attr(None, 'Address', wg_address, '# Networks = ' + wg_networks, append_as_line=True)
|
|
wc.write_file()
|
|
print(' Config file written. Ok.')
|
|
print(f'8a) Ensuring list permission of WireGuard config directory {os.path.dirname(cfg.wg_configfile)}.')
|
|
os.chmod(os.path.dirname(cfg.wg_configfile), 0o711)
|
|
print(f'8b) Ensuring ownership of WireGuard config file {cfg.wg_configfile}.')
|
|
chown(cfg.user, cfg.wg_configfile)
|
|
print(f'8c) Ensuring ownership of server certificate file {cfg.sslcertfile} in case it exists.')
|
|
if os.path.exists(cfg.sslcertfile):
|
|
chown(cfg.user, cfg.sslcertfile)
|
|
print(f'8d) Ensuring ownership of server private key file {cfg.sslkeyfile} in case it exists.')
|
|
if os.path.exists(cfg.sslkeyfile):
|
|
chown(cfg.user, cfg.sslkeyfile)
|
|
print(f'9) Dropping root privileges to user/group "{cfg.user}".')
|
|
drop_privileges(cfg.user, cfg.user)
|
|
print(f'Attempting to start web frontend...')
|
|
return cfg
|
|
|
|
|
|
if __name__ == '__main__':
|
|
setup_environment()
|