Skip to content

Commit a67d06c

Browse files
committed
Initial commit
0 parents  commit a67d06c

File tree

5 files changed

+192
-0
lines changed

5 files changed

+192
-0
lines changed

CHANGELOG.md

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# implexus CHANGELOG
2+
3+
## [Unreleased]
4+
5+
### Added
6+
7+
### Changed
8+
9+
### Fixed
10+
11+
### Removed
12+
13+
____
14+
## [0.1.0] - 2023-08-15
15+
16+
Initial release
17+
18+
____
19+
[Unreleased]: https://github.com/erykjj/nebulder
20+
[0.0.1]: https://github.com/erykjj/nebulder/releases/tag/v0.0.1

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2022 Eryk J.
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# nebulder
2+
3+
4+
## Purpose
5+
6+
____
7+
## Command-line usage
8+
```
9+
usage: nebulder.py [-h] [-v] [-o directory] Outline
10+
11+
Generate Nebula configs based on a network outline
12+
13+
positional arguments:
14+
Outline Network outline (YAML format)
15+
16+
options:
17+
-h, --help show this help message and exit
18+
-v, --version show program's version number and exit
19+
-o directory Output directory (working dir if not provided)
20+
```
21+
22+
____
23+
## Feedback
24+
25+
Feel free to [get in touch and post any issues and suggestions](https://github.com/erykjj/nebulder/issues).
26+
27+
[![RSS of releases](res/rss-36.png)](https://github.com/erykjj/nebulder/releases.atom)

nebulder.py

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
#!/usr/bin/env python3
2+
3+
"""
4+
File: nebulder
5+
6+
Description: Generate Nebula configs based on a network outline
7+
8+
MIT License: Copyright (c) 2023 Eryk J.
9+
10+
Permission is hereby granted, free of charge, to any person obtaining a copy
11+
of this software and associated documentation files (the "Software"), to deal
12+
in the Software without restriction, including without limitation the rights
13+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14+
copies of the Software, and to permit persons to whom the Software is
15+
furnished to do so, subject to the following conditions:
16+
17+
The above copyright notice and this permission notice shall be included in all
18+
copies or substantial portions of the Software.
19+
20+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26+
SOFTWARE.
27+
"""
28+
29+
APP = 'nebulder'
30+
VERSION = 'v0.0.1'
31+
32+
33+
import argparse, os, subprocess, yaml
34+
35+
36+
def sh(command, arguments='', inp=''):
37+
res = subprocess.run([command, arguments], stdout=subprocess.PIPE, stderr=subprocess.PIPE, input=inp.encode('utf-8'))
38+
if res.stderr.decode('utf-8'):
39+
print(res.stderr.decode('utf-8'))
40+
exit()
41+
return res.stdout.decode('utf-8')
42+
43+
44+
def create_deploy_script(name, port):
45+
if port:
46+
note = f'echo "You may need to add a rule to your firewall to allow traffic to the WireGuard interface\'s port:"\necho "sudo ufw allow {port}/udp"\necho "sudo ufw reload"\n\n'
47+
else:
48+
note = ''
49+
return f'#!/bin/bash\n\nsystemctl stop wg-quick@{name}\nsystemctl disable wg-quick@{name}\n\ncp --remove-destination {name}.conf /etc/wireguard/\nchown root:root /etc/wireguard/{name}.conf\nchmod 600 /etc/wireguard/{name}.conf\n\nsystemctl enable wg-quick@{name}\nsystemctl start wg-quick@{name}\n\nwg show {name}\n\n{note}exit 0'
50+
51+
def create_remove_script(name):
52+
return f'#!/bin/bash\n\nsystemctl stop wg-quick@{name}\nsystemctl disable wg-quick@{name}\n\nrm /etc/wireguard/{name}.conf\n\nwg show\n\necho "You may also need to check your firewall rules"\n\nexit 0'
53+
54+
55+
def process_config(config, dir):
56+
with open(config) as f:
57+
mesh = yaml.load(f, Loader=yaml.loader.SafeLoader)
58+
59+
dir += '/' + mesh['NetworkName']
60+
os.makedirs(dir, exist_ok=True)
61+
62+
for device in mesh.keys():
63+
if device == 'NetworkName':
64+
continue
65+
os.makedirs(dir + '/' + device, exist_ok=True)
66+
if 'PrivateKey' not in mesh[device].keys():
67+
mesh[device]['PrivateKey'] = sh('wg', 'genkey').rstrip('\n')
68+
mesh[device]['PublicKey'] = sh('wg', 'pubkey', mesh[device]['PrivateKey']).rstrip('\n')
69+
70+
for device in mesh.keys():
71+
if device == 'NetworkName':
72+
continue
73+
if 'AllowedIPs' in mesh[device].keys():
74+
subnet = '/24'
75+
routing = f"\n\n# IP forwarding\nPreUp = sysctl -w net.ipv4.ip_forward=1\n\n# IP masquerading\nPreUp = iptables -t mangle -A PREROUTING -i {mesh['NetworkName']} -j MARK --set-mark 0x30\nPreUp = iptables -t nat -A POSTROUTING ! -o {mesh['NetworkName']} -m mark --mark 0x30 -j MASQUERADE\nPostDown = iptables -t mangle -D PREROUTING -i {mesh['NetworkName']} -j MARK --set-mark 0x30\nPostDown = iptables -t nat -D POSTROUTING ! -o {mesh['NetworkName']} -m mark --mark 0x30 -j MASQUERADE"
76+
else:
77+
subnet = '/32'
78+
routing = ''
79+
conf = f"[Interface]\n# Name: {device}\nAddress = {mesh[device]['Address']}{subnet}\nPrivateKey = {mesh[device]['PrivateKey']}"
80+
if 'ListenPort' in mesh[device].keys():
81+
conf += f"\nListenPort = {mesh[device]['ListenPort']}{routing}"
82+
else:
83+
mesh[device]['ListenPort'] = False
84+
if 'DNS' in mesh[device].keys():
85+
conf += f"\nDNS = {mesh[device]['DNS']}"
86+
for peer in mesh.keys():
87+
if peer == 'NetworkName' or peer == device:
88+
continue
89+
if 'Endpoint' not in mesh[peer].keys() and 'AllowedIPs' not in mesh[device].keys():
90+
continue
91+
conf += f"\n\n[Peer]\n# Name: {peer}\nPublicKey = {mesh[peer]['PublicKey']}"
92+
if 'Endpoint' in mesh[peer].keys():
93+
conf += f"\nEndpoint = {mesh[peer]['Endpoint']}:{mesh[peer]['ListenPort']}"
94+
95+
if 'AllowedIPs' in mesh[peer].keys():
96+
conf += f"\nAllowedIPs = {mesh[peer]['AllowedIPs']}"
97+
else:
98+
conf += f"\nAllowedIPs = {mesh[peer]['Address']}/32"
99+
if 'PersistentKeepalive' in mesh[device].keys():
100+
conf += f"\nPersistentKeepalive = {mesh[device]['PersistentKeepalive']}"
101+
102+
file_dir = f"{dir}/{device}/"
103+
with open(f"{file_dir}{mesh['NetworkName']}.conf", 'w', encoding='UTF-8') as f:
104+
f.write(conf)
105+
os.chmod(f"{file_dir}{mesh['NetworkName']}.conf", mode=0o600)
106+
with open(file_dir + f"deploy_{device}.sh", 'w', encoding='UTF-8') as f:
107+
f.write(create_deploy_script(mesh['NetworkName'], mesh[device]['ListenPort']))
108+
os.chmod(f'{file_dir}deploy_{device}.sh', mode=0o740)
109+
with open(file_dir + f"remove_{device}.sh", 'w', encoding='UTF-8') as f:
110+
f.write(create_remove_script(mesh['NetworkName']))
111+
os.chmod(f'{file_dir}remove_{device}.sh', mode=0o740)
112+
print(f'Generated config and scripts for {device}')
113+
114+
115+
parser = argparse.ArgumentParser(description="Generate Nebula configs based on a network outline")
116+
parser.add_argument('-v', '--version', action='version', version=f"{APP} {VERSION}")
117+
parser.add_argument("Outline", help='Network outline (YAML format)')
118+
parser.add_argument('-o', metavar='directory', help='Output directory (working dir if not provided)')
119+
args = vars(parser.parse_args())
120+
if args['o']:
121+
dir = args['o'].rstrip('/')
122+
else:
123+
dir = '.'
124+
process_config(args['Outline'], dir)

res/rss-36.png

1.27 KB
Loading

0 commit comments

Comments
 (0)