forked from hsand/pia-wg
-
Notifications
You must be signed in to change notification settings - Fork 7
/
configure-ros.py
executable file
·155 lines (131 loc) · 5.1 KB
/
configure-ros.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#!/usr/bin/env python
from pick import pick
from getpass import getpass
from datetime import datetime
from icmplib import multiping
from pprint import pprint
from routeros import login
import argparse, collections, os, re, sys, yaml, wgconfig
# comment to debug
sys.tracebacklimit = 0
def add_or_update(ros, section, key, value, **params):
existing = ros.query(section + "/print").equal(**{key: value})
ret = []
if existing:
ret = ros(section + "/set", **{".id": existing[0]['.id']}, **params)
if len(ret)==0 or ".id" in ret[0]:
print("- updated existing " + section)
else:
ret = ros(section + "/add", **{key: value}, **params)
if len(ret)==0 or "ret" in ret[0]:
print("- added new " + section)
if len(ret) and not (".id" in ret[0] or "ret" in ret[0]):
raise Exception("ROS {} update failed".format(section))
def remove(ros, section, key, value):
existing = ros.query(section + "/print").equal(**{key: value})
if existing:
ret = ros(section + "/remove", **{".id": existing[0]['.id']})
if len(ret)==0 or ".id" in ret[0]:
print("- removed entry in " + section)
else:
raise Exception("ROS {} remove failed".format(section))
else:
print("- entry not found in " + section)
def add_gw_dns(ros, ifname):
existing = ros.query("/ip/address/print").equal(interface=ifname)
if existing:
network = existing[0]["network"]
gw = re.sub("\.0$", ".1", network)
add_or_update(ros, "/ip/dns/static", "name", "gw-" + ifname,
**{"address": gw})
else:
print("- " + ifname + " address not found")
def delete_interface(ros, ifname):
remove(ros, "/interface/wireguard/peers", "interface", ifname)
remove(ros, "/ip/address", "interface", ifname)
remove(ros, "/interface/wireguard", "name", ifname)
remove(ros, "/ip/dns/static", "name", "gw-" + ifname)
def configure_routeros(ros, ifname, wgconf):
interface = wgconf.get_interface()
peers = wgconf.get_peers(keys_only=False)
# make sure we have everything we need
for k in ['Address', 'PrivateKey']:
if interface.get(k) == None:
raise Exception("Missing interface parameter: '{}'".format(k))
assert len(peers) == 1, "1 peer expected"
for p in peers:
peer = peers[p]
for k in ['Endpoint', 'PublicKey']:
if peer.get(k) == None:
raise Exception("Missing peer parameter: '{}'".format(k))
# add/update interface
add_or_update(ros, "/interface/wireguard", "name", ifname,
**{"private-key": interface["PrivateKey"]})
# add/update interface address
add_or_update(ros, "/ip/address", "interface", ifname,
address="{}/17".format(interface["Address"]))
# add/update peer
ip, port = peer['Endpoint'].split(':')
add_or_update(ros, "/interface/wireguard/peers", "interface", ifname,
**{
"endpoint-address": ip,
"endpoint-port": port,
"public-key": peer['PublicKey'],
"allowed-address": "0.0.0.0/0",
"persistent-keepalive": "25s",
})
# add a static dns entry for the gateway ip
add_gw_dns(ros, ifname)
def main():
# Parse arguments
parser = argparse.ArgumentParser(
description='Configure RouterOS with wireguard config'
)
parser.add_argument('-d', '--delete', action='store_true')
parser.add_argument('-i', '--interface', required=True)
parser.add_argument('-f', '--config',
required=not ('-d' in sys.argv or
'--delete' in sys.argv))
args = parser.parse_args()
# Load config
config = None
file = os.path.join(os.path.dirname(__file__), 'config.yaml')
file = os.path.normpath(file)
if os.path.exists(file):
print("- loading config from {}".format(file))
with open(file, 'r') as f:
config = yaml.safe_load(f)
# Get credentials
try:
ip = config['router']['ip']
username = config['router']['username']
password = config['router']['password']
except:
ip, username, password = None, None, None
# Login
while True:
if None in (ip, username, password):
ip = input("\nEnter router IP: ")
username = input("\nEnter username: ")
password = getpass()
routeros = login(username, password, ip)
try:
routeros('/interface/print')
break
except:
print("Error logging in, please try again...")
username = None
if args.delete:
# Delete the interface
delete_interface(routeros, args.interface)
else:
# Read wg config
wgconf = wgconfig.WGConfig(
os.path.join(os.path.dirname(__file__), args.config)
)
wgconf.read_file()
# Make change to router
configure_routeros(routeros, args.interface, wgconf)
routeros.close()
if __name__ == '__main__':
main()