make a useful cli

This commit is contained in:
xenia 2021-06-15 00:01:58 -04:00
parent 291a287141
commit dfb2b9d956
4 changed files with 116 additions and 23 deletions

View File

@ -1,5 +1,7 @@
import asyncio
import binascii import binascii
from typing import Optional import secrets
from typing import Optional, Any
from pyroute2 import IPRoute, WireGuard from pyroute2 import IPRoute, WireGuard
@ -9,9 +11,28 @@ from .database import Database, Node, SERVER_NODE_ID
IFNAME = 'leyline-wg' IFNAME = 'leyline-wg'
DEFAULT_PORT = 31337 DEFAULT_PORT = 31337
API_PORT = 31338
db = Database("leylines.db") db = Database("leylines.db")
async def client_connected(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
try:
while True:
line = reader.readline()
if line.strip() != db.get_token():
raise Exception("unauthorized")
# TODO
finally:
writer.close()
await writer.wait_closed()
async def main() -> None:
sync_interface()
server = await asyncio.create_server(client_connected, host="127.0.0.1", port=API_PORT)
await server.serve_forever()
def net_init() -> None: def net_init() -> None:
with open("/proc/sys/net/ipv4/ip_forward", "w") as f: with open("/proc/sys/net/ipv4/ip_forward", "w") as f:
f.write("1") f.write("1")
@ -62,6 +83,7 @@ def add_node_to_wg(wg: WireGuard, node: Node) -> None:
def create_or_get_interface() -> int: def create_or_get_interface() -> int:
net_init()
with IPRoute() as ipr: with IPRoute() as ipr:
lookup = ipr.link_lookup(ifname=IFNAME) lookup = ipr.link_lookup(ifname=IFNAME)
if len(lookup) > 0: if len(lookup) > 0:

View File

@ -1,5 +1,65 @@
from leylines import net_init, create_or_get_interface, sync_interface import argparse
import ipaddress
net_init() from leylines import main, db, sync_interface, seckey_to_pubkey, generate_node_config
create_or_get_interface() from leylines.database import SERVER_NODE_ID
sync_interface()
def nop():
# needed because setup.py is weird
pass
parser = argparse.ArgumentParser(description="wireguard management system for dragons")
cmd = parser.add_subparsers(dest="cmd")
cmd.required = True
cmd_daemon = cmd.add_parser("daemon")
cmd_status = cmd.add_parser("status")
cmd_init = cmd.add_parser("init")
cmd_init.add_argument("-n", "--name", type=str, help="Name of the server node", required=True)
cmd_init.add_argument("-i", "--ip", type=str, help="Public IP of the server node", required=True)
cmd_add = cmd.add_parser("add")
cmd_add.add_argument("-n", "--name", type=str, help="Name of the node to add", required=True)
cmd_add.add_argument("-i", "--ip", type=str, help="Public IP of the node, if any", required=False)
cmd_add.add_argument("-k", "--ssh-key", type=argparse.FileType("r"),
help="SSH private keyfile for the node, if any", required=False)
cmd_get_conf = cmd.add_parser("get-conf")
cmd_get_conf.add_argument("id", type=int, help="Node ID")
cmd_sync = cmd.add_parser("sync")
args = parser.parse_args()
if args.cmd == "daemon":
asyncio.run(main())
elif args.cmd == "status":
token = db.get_token()
subnet = db.get_subnet()
nodes = db.get_nodes()
server_node = db.get_server_node()
print("TOKEN:", token)
print("SUBNET:", subnet)
if server_node is None:
print("SERVER: <not defined. run leylines init>")
else:
print("SERVER:", server_node.name, server_node.ip, server_node.public_ip,
seckey_to_pubkey(server_node.seckey))
for node in nodes:
if node.id == SERVER_NODE_ID:
continue
print(f"NODE {node.id}:", node.name, node.public_ip, node.ip,
seckey_to_pubkey(node.seckey), "<Have SSH Key>" if node.ssh_key is not None else "")
elif args.cmd == "init":
db.init_server(args.name, ipaddress.IPv4Address(args.ip))
elif args.cmd == "add":
db.add_node(args.name, args.ip, args.ssh_key.read() if args.ssh_key is not None else None)
sync_interface()
elif args.cmd == "get-conf":
print(generate_node_config(args.id))
elif args.cmd == "sync":
sync_interface()

View File

@ -1,5 +1,6 @@
import binascii import binascii
import ipaddress import ipaddress
import secrets
import sqlite3 import sqlite3
from typing import Iterable, List, NamedTuple, Optional, Set from typing import Iterable, List, NamedTuple, Optional, Set
@ -38,6 +39,9 @@ class Database:
"CREATE TABLE IF NOT EXISTS settings (name TEXT PRIMARY KEY, value TEXT NOT NULL)") "CREATE TABLE IF NOT EXISTS settings (name TEXT PRIMARY KEY, value TEXT NOT NULL)")
self.conn.execute( self.conn.execute(
"INSERT OR IGNORE INTO settings (name, value) VALUES ('subnet', ?)", (DEFAULT_SUBNET,)) "INSERT OR IGNORE INTO settings (name, value) VALUES ('subnet', ?)", (DEFAULT_SUBNET,))
self.conn.execute(
"INSERT OR IGNORE INTO settings (name, value) VALUES ('token', ?)",
(secrets.token_hex(32),))
self.conn.commit() self.conn.commit()
@ -51,6 +55,11 @@ class Database:
cur.execute("SELECT value FROM settings WHERE name='subnet'") cur.execute("SELECT value FROM settings WHERE name='subnet'")
return ipaddress.IPv4Interface(cur.fetchone()[0]) return ipaddress.IPv4Interface(cur.fetchone()[0])
def get_token(self) -> str:
cur = self.conn.cursor()
cur.execute("SELECT value FROM settings WHERE name='token'")
return cur.fetchone()[0]
def _get_free_ip(self) -> ipaddress.IPv4Address: def _get_free_ip(self) -> ipaddress.IPv4Address:
subnet = self.get_subnet().network subnet = self.get_subnet().network
subnets = [self.get_subnet().network] subnets = [self.get_subnet().network]

View File

@ -1,6 +1,7 @@
from setuptools import setup from setuptools import setup
setup(name='leylines', setup(
name='leylines',
version='0.1', version='0.1',
description='Sample text', description='Sample text',
url='https://awoo.systems', url='https://awoo.systems',
@ -9,12 +10,13 @@ setup(name='leylines',
license='AGPLv3', license='AGPLv3',
packages=['leylines'], packages=['leylines'],
install_requires=[ install_requires=[
# requirements "leylines-monocypher",
"pyroute2"
], ],
include_package_data=True, include_package_data=True,
entry_points={ entry_points={
'console_scripts': [ 'console_scripts': [
# bins "leylines=leylines.__main__:nop"
] ]
}, },
zip_safe=True) zip_safe=True)