mirror of
https://github.com/Kiritow/wg-ops.git
synced 2024-03-22 13:11:37 +08:00
314 lines
13 KiB
Python
314 lines
13 KiB
Python
import os
|
|
import sys
|
|
import time
|
|
import getopt
|
|
|
|
|
|
path_udp2raw = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), 'bin/udp2raw_amd64')
|
|
path_w2u = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), 'bin/w2u')
|
|
path_gost = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), 'bin/gost')
|
|
|
|
|
|
class Parser:
|
|
def __init__(self):
|
|
# input parts
|
|
self.input_interface = []
|
|
self.input_peer = []
|
|
|
|
# output
|
|
self.result_interface = []
|
|
self.result_postup = []
|
|
self.result_postdown = []
|
|
self.result_peers = []
|
|
|
|
# flags
|
|
self.flag_has_setup_tmux = False
|
|
self.flag_is_route_forward = False
|
|
self.flag_is_route_lookup = False
|
|
|
|
# vars
|
|
self.wg_name = '%i'
|
|
self.wg_port = 0
|
|
self.wg_mtu = 0
|
|
self.idx_tunnels = {}
|
|
self.lookup_table = ''
|
|
|
|
def enable_tmux(self):
|
|
if not self.flag_has_setup_tmux:
|
|
self.flag_has_setup_tmux = True
|
|
self.result_postup.append('''PostUp=/usr/bin/tmux new-session -s tunnel-{} -d 'watch -n 1 --color WG_COLOR_MODE=always wg show {}' '''.format(self.wg_name, self.wg_name))
|
|
self.result_postdown.append('PostDown=sleep 1; /usr/bin/tmux kill-session -t tunnel-{}'.format(self.wg_name))
|
|
|
|
def parse(self, content):
|
|
# parse input
|
|
input_mode = ''
|
|
current_peer = []
|
|
for line in content:
|
|
if line.startswith('[Interface]'):
|
|
input_mode = 'interface'
|
|
continue
|
|
|
|
if line.startswith('[Peer]'):
|
|
input_mode = 'peer'
|
|
if current_peer:
|
|
self.input_peer.append(current_peer)
|
|
current_peer = []
|
|
continue
|
|
|
|
if input_mode == 'interface':
|
|
self.input_interface.append(line)
|
|
elif input_mode == 'peer':
|
|
current_peer.append(line)
|
|
else:
|
|
sys.stderr.write('[WARN] Incorrect mode detected with line: {}\n'.format(line))
|
|
|
|
if current_peer:
|
|
self.input_peer.append(current_peer)
|
|
|
|
def compile_interface(self):
|
|
self.result_interface.append('[Interface]')
|
|
|
|
# compile interface
|
|
for line in self.input_interface:
|
|
if line.startswith('ListenPort'):
|
|
self.wg_port = int(line.split('=')[1])
|
|
if line.startswith('MTU'):
|
|
self.wg_mtu = int(line.split('=')[1])
|
|
|
|
if not line.startswith('#'):
|
|
self.result_interface.append(line)
|
|
continue
|
|
|
|
if line.startswith('#enable-bbr'):
|
|
self.result_postup.append('PostUp=sysctl net.core.default_qdisc=fq\nPostUp=sysctl net.ipv4.tcp_congestion_control=bbr')
|
|
elif line.startswith('#enable-forward'):
|
|
self.result_postup.append('PostUp=sysctl net.ipv4.ip_forward=1')
|
|
elif line.startswith('#iptables-forward'):
|
|
self.result_postup.append('PostUp=iptables -A FORWARD -i {} -j ACCEPT'.format(self.wg_name))
|
|
self.result_postdown.append('PostDown=iptables -D FORWARD -i {} -j ACCEPT'.format(self.wg_name))
|
|
elif line.startswith('#route-to'):
|
|
self.flag_is_route_forward = True
|
|
|
|
parts = line.split(' ')[1:]
|
|
table_name = parts[0]
|
|
|
|
self.result_postup.append('PostUp=ip route add 0.0.0.0/0 dev {} table {}'.format(self.wg_name, table_name))
|
|
sys.stderr.write('[WARN] Please ensure custom route table {} exists.\n'.format(table_name))
|
|
elif line.startswith('#route-from'):
|
|
self.flag_is_route_lookup = True
|
|
|
|
parts = line.split(' ')[1:]
|
|
table_name = parts[0]
|
|
|
|
self.lookup_table = table_name
|
|
sys.stderr.write('[WARN] Please ensure custom route table {} exists.\n'.format(table_name))
|
|
elif line.startswith('#udp2raw-server'):
|
|
parts = line.split(' ')[1:]
|
|
tunnel_name = parts[0]
|
|
tunnel_port = parts[1]
|
|
tunnel_passwd = parts[2]
|
|
|
|
self.enable_tmux()
|
|
|
|
self.result_postup.append('''PostUp=echo -e '-s\\n-l 0.0.0.0:{}\\n-r 127.0.0.1:{}\\n-k {}\\n--raw-mode faketcp\\n--fix-gro\\n-a' > /tmp/temp-udp2raw-{}.conf'''.format(
|
|
tunnel_port, self.wg_port, tunnel_passwd, tunnel_name
|
|
))
|
|
self.result_postup.append('''PostUp=/usr/bin/tmux new-window -t tunnel-{} -d '{} --conf-file /tmp/temp-udp2raw-{}.conf'; sleep 2'''.format(
|
|
self.wg_name, path_udp2raw, tunnel_name
|
|
))
|
|
self.result_postup.append('''PostUp=rm /tmp/temp-udp2raw-{}.conf'''.format(tunnel_name))
|
|
elif line.startswith('#udp2raw-client '):
|
|
parts = line.split(' ')[1:]
|
|
tunnel_name = parts[0]
|
|
tunnel_port = parts[1]
|
|
tunnel_remote = parts[2]
|
|
tunnel_passwd = parts[3]
|
|
|
|
self.idx_tunnels[tunnel_name] = tunnel_port
|
|
self.enable_tmux()
|
|
|
|
self.result_postup.append('''PostUp=echo -e '-c\\n-l 127.0.0.1:{}\\n-r {}\\n-k {}\\n--raw-mode faketcp\\n--fix-gro\\n-a' > /tmp/temp-udp2raw-{}.conf'''.format(
|
|
tunnel_port, tunnel_remote, tunnel_passwd, tunnel_name
|
|
))
|
|
self.result_postup.append('''PostUp=/usr/bin/tmux new-window -t tunnel-{} -n {}-win -d '{} --conf-file /tmp/temp-udp2raw-{}.conf'; sleep 2'''.format(
|
|
self.wg_name, tunnel_name, path_udp2raw, tunnel_name
|
|
))
|
|
self.result_postup.append('''PostUp=rm /tmp/temp-udp2raw-{}.conf'''.format(tunnel_name))
|
|
self.result_postdown.append('''PostDown=/usr/bin/tmux send-keys -t {}-win C-c '''.format(tunnel_name))
|
|
elif line.startswith('#udp2raw-client-mux '):
|
|
parts = line.split(' ')[1:]
|
|
tunnel_name = parts[0]
|
|
tunnel_mux = int(parts[1])
|
|
tunnel_port = int(parts[2])
|
|
tunnel_remote = parts[3]
|
|
tunnel_passwd = parts[4]
|
|
|
|
self.idx_tunnels[tunnel_name] = tunnel_port
|
|
self.enable_tmux()
|
|
|
|
self.result_postup.append('''PostUp=/usr/bin/tmux new-window -t tunnel-{} -d '{} -f {} -l {} -t {} -s {}' '''.format(
|
|
self.wg_name, path_w2u, self.wg_port, tunnel_port, tunnel_port + 1, tunnel_mux
|
|
))
|
|
for mux_idx in range(tunnel_mux):
|
|
self.result_postup.append('''PostUp=echo -e '-c\\n-l 127.0.0.1:{}\\n-r {}\\n-k {}\\n--raw-mode faketcp\\n--fix-gro\\n-a' > /tmp/temp-udp2raw-{}-{}.conf'''.format(
|
|
tunnel_port + 1 + mux_idx, tunnel_remote, tunnel_passwd, tunnel_name, mux_idx
|
|
))
|
|
self.result_postup.append('''PostUp=/usr/bin/tmux new-window -t tunnel-{} -n {}-win-{} -d '{} --conf-file /tmp/temp-udp2raw-{}-{}.conf'; sleep 2'''.format(
|
|
self.wg_name, tunnel_name, mux_idx, path_udp2raw, tunnel_name, mux_idx
|
|
))
|
|
self.result_postup.append('''PostUp=rm /tmp/temp-udp2raw-{}-{}.conf'''.format(tunnel_name, mux_idx))
|
|
|
|
self.result_postdown.append('''PostDown=/usr/bin/tmux send-keys -t {}-win-{} C-c '''.format(tunnel_name, mux_idx))
|
|
|
|
elif line.startswith('#gost-server '):
|
|
parts = line.split(' ')[1:]
|
|
tunnel_name = parts[0]
|
|
tunnel_port = parts[1]
|
|
|
|
self.enable_tmux()
|
|
|
|
self.result_postup.append('''PostUp=/usr/bin/tmux new-window -t tunnel-{} -d '{} -L=relay+tls://:{}/127.0.0.1:{}' '''.format(
|
|
self.wg_name, path_gost, tunnel_port, self.wg_port
|
|
))
|
|
elif line.startswith('#gost-client '):
|
|
parts = line.split(' ')[1:]
|
|
tunnel_name = parts[0]
|
|
tunnel_port = parts[1]
|
|
tunnel_remote = parts[2]
|
|
|
|
self.idx_tunnels[tunnel_name] = tunnel_port
|
|
self.enable_tmux()
|
|
|
|
self.result_postup.append('''PostUp=/usr/bin/tmux new-window -t tunnel-{} -d '{} -L udp://:{} -F relay+tls://{}' '''.format(
|
|
self.wg_name, path_gost, tunnel_port, tunnel_remote
|
|
))
|
|
elif line.startswith('#gost-client-mux '):
|
|
parts = line.split(' ')[1:]
|
|
tunnel_name = parts[0]
|
|
tunnel_mux = int(parts[1])
|
|
tunnel_port = int(parts[2])
|
|
tunnel_remote = parts[3]
|
|
|
|
self.idx_tunnels[tunnel_name] = tunnel_port
|
|
self.enable_tmux()
|
|
|
|
self.result_postup.append('''PostUp=/usr/bin/tmux new-window -t tunnel-{} -d '{} -f {} -l {} -t {} -s {}' '''.format(
|
|
self.wg_name, path_w2u, self.wg_port, tunnel_port, tunnel_port + 1, tunnel_mux
|
|
))
|
|
for mux_idx in range(tunnel_mux):
|
|
self.result_postup.append('''PostUp=/usr/bin/tmux new-window -t tunnel-{} -d '{} -L udp://:{} -F relay+tls://{}' '''.format(
|
|
self.wg_name, path_gost, tunnel_port + 1 + mux_idx, tunnel_remote
|
|
))
|
|
else:
|
|
sys.stderr.write('[WARN] comment or unknown hint: {}\n'.format(line))
|
|
|
|
if not self.wg_mtu:
|
|
sys.stderr.write('[WARN] MTU not detected, using suggested mtu value (1280).\n')
|
|
self.result_interface.append('MTU=1280')
|
|
|
|
def compile_peers(self):
|
|
if self.flag_is_route_forward and len(self.input_peer) > 1:
|
|
sys.stderr.write('[WARN] route-forward should used with ONLY one peer.')
|
|
|
|
for this_peer_idx, this_peer_lines in enumerate(self.input_peer):
|
|
current_pubkey = ''
|
|
current_allowed = ''
|
|
if self.flag_is_route_lookup:
|
|
current_lookup = self.lookup_table
|
|
else:
|
|
current_lookup = ''
|
|
|
|
self.result_peers.append('[Peer]')
|
|
|
|
for line in this_peer_lines:
|
|
if line.startswith('PublicKey'):
|
|
current_pubkey = '='.join(line.split('=')[1:])
|
|
if line.startswith('AllowedIPs'):
|
|
current_allowed = line.split('=')[1].strip().split(',')
|
|
|
|
if not line.startswith('#'):
|
|
self.result_peers.append(line)
|
|
continue
|
|
|
|
if line.startswith('#use-tunnel'):
|
|
parts = line.split(' ')[1:]
|
|
tunnel_name = parts[0]
|
|
|
|
tunnel_port = self.idx_tunnels[tunnel_name]
|
|
self.result_peers.append('Endpoint=127.0.0.1:{}'.format(tunnel_port))
|
|
elif line.startswith('#route-from'):
|
|
parts = line.split(' ')[1:]
|
|
table_name = parts[0]
|
|
|
|
if table_name != self.lookup_table:
|
|
current_lookup = table_name
|
|
sys.stderr.write('[WARN] Please ensure custom route table {} exists.\n'.format(table_name))
|
|
else:
|
|
sys.stderr.write('[WARN] comment or unknown hint: {}\n'.format(line))
|
|
|
|
if self.flag_is_route_forward and this_peer_idx == 0:
|
|
self.result_postup.insert(0, 'PostUp=wg set {} peer {} allowed-ips 0.0.0.0/0'.format(self.wg_name, current_pubkey))
|
|
|
|
if current_lookup:
|
|
for ip_cidr in current_allowed:
|
|
self.result_postup.append('PostUp=ip rule add from {} lookup {}'.format(ip_cidr, current_lookup))
|
|
self.result_postdown.append('PostDown=ip rule del from {} lookup {}'.format(ip_cidr, current_lookup))
|
|
|
|
def get_result(self):
|
|
current_time = time.strftime("%Y-%m-%d %H:%M:%S")
|
|
return '''# Generated by wg-ops at {}. DO NOT EDIT.
|
|
{}
|
|
{}
|
|
{}
|
|
{}
|
|
'''.format(current_time, '\n'.join(self.result_interface), '\n'.join(self.result_postup), '\n'.join(self.result_postdown), '\n'.join(self.result_peers))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
opts, args = getopt.getopt(sys.argv[1:], 'hko:')
|
|
opts = {p[0]: p[1] for p in opts}
|
|
|
|
if '-h' in opts:
|
|
print('''wg-ops: WireGuard configuration extended generator
|
|
OPTIONS
|
|
-h Display this help and quit.
|
|
-k Output generated config to standard output
|
|
-o <filename> Output generated config to file. Default is {source_filename}.gen
|
|
TAGS
|
|
#enable-bbr
|
|
#enable-forward
|
|
#iptables-forward
|
|
#route-to table
|
|
#route-from table
|
|
#udp2raw-server name port password
|
|
#udp2raw-client name port remote password
|
|
#udp2raw-client-mux name mux port remote password
|
|
#gost-server name port
|
|
#gost-client name port remote
|
|
#gost-client-mux name mux port remote
|
|
#use-tunnel name
|
|
''')
|
|
exit(0)
|
|
|
|
filepath = args[0]
|
|
filename = os.path.basename(filepath)
|
|
|
|
with open(filepath, 'r') as f:
|
|
content = f.read().split('\n')
|
|
|
|
parser = Parser()
|
|
parser.parse(content)
|
|
parser.compile_interface()
|
|
parser.compile_peers()
|
|
|
|
if '-k' in opts or ('-o' in opts and opts['-o'] == '-'):
|
|
print(parser.get_result())
|
|
elif '-o' in opts:
|
|
sys.stderr.write('Saving to {}...\n'.format(opts['-o']))
|
|
with open(opts['-o'], 'w') as f:
|
|
f.write(parser.get_result())
|
|
else:
|
|
sys.stderr.write('Saving to {}.gen...\n'.format(filename))
|
|
with open('{}.gen'.format(filename), 'w') as f:
|
|
f.write(parser.get_result())
|