initial code of openvpn manager
This commit is contained in:
parent
c566b4eb34
commit
21fc10405f
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal 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
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"python.pythonPath": "env/bin/python3"
|
||||||
|
}
|
||||||
44
o_manager.py
Normal file
44
o_manager.py
Normal 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
138
openvpn.py
Normal 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
41
profile_scanner.py
Normal 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
1
profiles.json
Normal file
File diff suppressed because one or more lines are too long
82
test.py
Normal file
82
test.py
Normal 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()
|
||||||
Loading…
x
Reference in New Issue
Block a user