initial code of openvpn manager

This commit is contained in:
mantaohuang 2020-04-03 15:50:29 -04:00
parent c566b4eb34
commit 21fc10405f
7 changed files with 318 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
# virtual env
env/
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
cache/

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"python.pythonPath": "env/bin/python3"
}

44
o_manager.py Normal file
View File

@ -0,0 +1,44 @@
import os
from openvpn import Openvpn
import asyncio
class OManager:
def __init__(self, base_folder, base_port=8001):
self.base_folder = base_folder
self.base_port = base_port
self.ops = []
self.idx = 0
def new_op(self, cfg_fp, name=None, additional_cfg={}):
interface = f"tun{self.idx}"
folder_path = os.path.join(self.base_folder, f"session{self.idx}")
if not os.path.isdir(folder_path):
os.makedirs(folder_path)
if not name:
name = f"openvpn-{self.idx}"
op = Openvpn(cfg_fp, interface, folder_path,
f"{self.base_port + self.idx}", name=name,
additional_cfg=additional_cfg)
self.ops.append(op)
self.idx += 1
return op
def start_op(self, idx):
self.ops[idx].start()
def get_all_ops(self):
return self.ops
def stop_all(self):
loop = asyncio.get_event_loop()
for op in self.ops:
loop.create_task(op.stop())
if __name__ == "__main__":
folder_fp = "/home/mantao/Desktop/t/"
cfg_fp = "/home/mantao/Desktop/t/TCP_Files/UK2-TCP.ovpn"
om = OManager(folder_fp)
om.new_op(cfg_fp, "op1", {
"auth-user-pass": "/home/mantao/Desktop/t/fast.txt"})

138
openvpn.py Normal file
View File

@ -0,0 +1,138 @@
"""
openvpn supervisor
"""
import asyncio
import tempfile
import os
import time
import openvpn_api
def generate_config(in_fp, cfg):
def change_item(config, item_key, item_content):
output_lines = []
changed = False
for i in config.splitlines():
if i.startswith(item_key+" ") or i == item_key:
output_lines.append(f"{item_key} {item_content}")
changed = True
else:
output_lines.append(i)
if not changed:
output_lines.append(f"{item_key} {item_content}")
return "\n".join(output_lines)
# _, path = tempfile.mkstemp()
with open(in_fp, "r") as in_f:
config = in_f.read()
for key, value in cfg.items():
config = change_item(config, key, value)
return config
IDLE = "idle"
RUNNING = "running"
class Openvpn:
def __init__(self, cfg_fp, interface, folder_path, management_port, name=None, additional_cfg={}, loop=None):
self.cfg_fp = cfg_fp
self.interface = interface
self.folder_path = folder_path
self.management_port = management_port
self.PID = 0
self.status = IDLE
self.proc = None
self.exit_future = None
self.additional_cfg = additional_cfg
self.run_task = None
self.openvpn_api = None
self.name = name
if loop:
self.loop = loop
else:
self.loop = asyncio.get_event_loop()
def get_cfg(self):
self.log_fp = os.path.join(self.folder_path, "log.txt")
cfg = {
"dev": self.interface,
"dev-type": "tun", # TODO: add code to read dev-type
"management": f"localhost {self.management_port}",
"log-append": self.log_fp
}
cfg.update(self.additional_cfg)
return cfg
def generate_config_file(self):
cfg = self.get_cfg()
self.config_fp = os.path.join(self.folder_path, "cfg.txt")
config = generate_config(self.cfg_fp, cfg)
with open(self.config_fp, "w") as config_f:
config_f.write(config)
return self.config_fp
def start(self):
if self.status == IDLE:
self.status = RUNNING
config_fp = self.generate_config_file()
cmd = " ".join(["openvpn", "--config", config_fp])
self.run_task = self.loop.create_task(self.run(cmd))
def get_log(self):
# regenerate log_fp
_ = self.get_cfg()
try:
with open(self.log_fp, "r") as log_f:
return log_f.read()
except:
return ""
def clear_log(self):
os.remove(self.log_fp)
async def stop(self):
if self.status == RUNNING:
try:
await self.proc.kill()
except:
print("kill failed")
self.status = IDLE
self.run_task.cancel()
async def restart(self):
await self.stop()
self.start()
async def run(self, cmd):
print(f"run: {cmd}")
self.exit_future = asyncio.Future()
while self.status == RUNNING:
print("create proc")
print(self.status)
proc = await asyncio.create_subprocess_shell(
cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE)
self.proc = proc
self.PID = proc.pid
self.openvpn_api = openvpn_api.VPN(
'localhost', int(self.management_port))
print(f"pid: {proc.pid}")
stdout, stderr = await proc.communicate()
print(f'[{cmd!r} exited with {proc.returncode}]')
if stdout:
print(f'[stdout]\n{stdout.decode()}')
if stderr:
print(f'[stderr]\n{stderr.decode()}')
await asyncio.sleep(5)
self.exit_future.set_result(True)
self.status = IDLE
if __name__ == "__main__":
folder_fp = "/home/mantao/Desktop/t/"
cfg_fp = "/home/mantao/Desktop/t/TCP_Files/UK2-TCP.ovpn"
o1 = Openvpn(cfg_fp, "tun0", folder_fp, 8001, additional_cfg={
"auth-user-pass": "/home/mantao/Desktop/t/fast.txt"})
o1.start()

41
profile_scanner.py Normal file
View File

@ -0,0 +1,41 @@
import os
import json
import argparse
def get_all_files(path, extension=".ovpn"):
result = [os.path.join(dp, f) for dp, dn, filenames in os.walk(
path) for f in filenames if os.path.splitext(f)[-1] == extension]
return result
def gen_cfg(cfg_fp, auth_file):
filename = os.path.split(cfg_fp)[-1]
name = os.path.splitext(filename)[0]
return {
"cfg_fp": cfg_fp,
"name": name,
"additional_cfg": {
"auth-user-pass": auth_file
}
}
# Construct the argument parser
ap = argparse.ArgumentParser()
ap.add_argument("-c", "--config_folder", required=True,
help="path to the ovpn config files")
ap.add_argument("-a", "--auth_file", required=True,
help="path to the auth-user-pass file")
args = vars(ap.parse_args())
config_folder = args["config_folder"]
auth_file = args["auth_file"]
all_fps = get_all_files(config_folder)
cfgs = []
for cfg_fp in all_fps:
cfgs.append(gen_cfg(cfg_fp, auth_file))
cfgs.sort(key=lambda x: x["name"])
profile_fp = "profiles.json"
open(profile_fp, "w").write(json.dumps(cfgs))

1
profiles.json Normal file

File diff suppressed because one or more lines are too long

82
test.py Normal file
View File

@ -0,0 +1,82 @@
import tornado.ioloop
import tornado.web
import json
import argparse
from o_manager import OManager
ap = argparse.ArgumentParser()
ap.add_argument("-s", "--session_folder", required=True,
help="path to the session folder")
args = vars(ap.parse_args())
session_folder = args["session_folder"]
om = OManager(session_folder)
class MainHandler(tornado.web.RequestHandler):
def get(self):
buf = f"ops len: {len(om.ops)} <br/>\n"
if len(om.ops):
for idx, op in enumerate(om.ops):
buf += f"op #{idx}, name: {op.name} <br/>\n"
buf += f"status: {op.status} <br/>\n"
buf += f"pid: {op.PID} <br/>\n"
buf += "log: <br/>\n"
buf += "<pre>\n"
buf += op.get_log()
buf += "\n</pre>\n"
self.write(buf)
class ConfigsHandeler(tornado.web.RequestHandler):
def get(self):
cfgs = json.loads(open("profiles.json", "r").read())
buf = "Select one configuration to create. <br/>\n"
for idx, cfg in enumerate(cfgs):
buf += f"<a href='/create?i={idx}'>{cfg['name']}</a><br/>\n"
self.write(buf)
class CreateInstantceHandler(tornado.web.RequestHandler):
def get(self):
idx = self.get_query_argument("i", None)
if idx != None:
idx = int(idx)
cfgs = json.loads(open("profiles.json", "r").read())
cfg = cfgs[idx]
om.new_op(cfg["cfg_fp"], cfg["name"],
additional_cfg=cfg["additional_cfg"])
self.write("create sucess!")
else:
self.write("need idx")
class StartInstatnceHandler(tornado.web.RequestHandler):
def get(self):
idx = self.get_query_argument("i", None)
if idx != None:
om.start_op(int(idx))
self.write("start sucess!")
else:
self.write("need idx")
class StopAll(tornado.web.RequestHandler):
def get(self):
om.stop_all()
def make_app():
return tornado.web.Application([
(r"/", MainHandler),
(r"/select", ConfigsHandeler),
(r"/create", CreateInstantceHandler),
(r"/start", StartInstatnceHandler),
(r"/stop", StopAll)
])
if __name__ == "__main__":
app = make_app()
app.listen(8000)
tornado.ioloop.IOLoop.current().start()