187 lines
8.1 KiB
Python
187 lines
8.1 KiB
Python
from lib import config as config_module
|
|
from lib import logging as logging_lib
|
|
from lib import utils
|
|
|
|
import os
|
|
import aiofiles.os
|
|
|
|
config = config_module.config
|
|
log = logging_lib.log
|
|
|
|
CLIENT_CONFIGS_LOCATION = "/etc/openvpn/client"
|
|
PARTNER_CONFIG_NAME = "clore_partner.conf"
|
|
|
|
def generate_openvpn_config(
|
|
local_ip='10.1.0.2',
|
|
server_ip='10.1.0.1',
|
|
server_hostname='example.com',
|
|
udp_port=1194,
|
|
vpn_secret_key='YOUR_VPN_SECRET_KEY'
|
|
):
|
|
openvpn_config = f"""nobind
|
|
proto udp4
|
|
remote {server_hostname} {udp_port}
|
|
resolv-retry infinite
|
|
|
|
auth SHA256
|
|
cipher AES-256-CBC
|
|
|
|
dev {config.openvpn_forwarding_tun_device}
|
|
ifconfig {local_ip} {server_ip}
|
|
|
|
<secret>
|
|
-----BEGIN OpenVPN Static key V1-----
|
|
{vpn_secret_key}
|
|
-----END OpenVPN Static key V1-----
|
|
</secret>
|
|
|
|
fragment 1300
|
|
mssfix 1300
|
|
sndbuf 524288
|
|
rcvbuf 524288
|
|
|
|
user nobody
|
|
group nogroup
|
|
|
|
ping 15
|
|
ping-restart 45
|
|
ping-timer-rem
|
|
persist-tun
|
|
persist-key
|
|
|
|
verb 0"""
|
|
|
|
return openvpn_config
|
|
|
|
async def get_iptables_forward_rules():
|
|
code, stdout, stderr = await utils.async_run_command(
|
|
f"LC_ALL=C {'sudo ' if config.run_iptables_with_sudo else ''}iptables -t nat -L PREROUTING -n -v --line-numbers"
|
|
)
|
|
rules = []
|
|
if code == 0:
|
|
collumns = []
|
|
for idx, line in enumerate(stdout.split('\n')):
|
|
if "num" in collumns and "target" in collumns and "in" in collumns:
|
|
items = line.split(maxsplit=len(collumns)+1)
|
|
rule = {}
|
|
for idx, name in enumerate(collumns):
|
|
rule[name]=items[idx]
|
|
rule["desc"] = items[len(collumns)+1]
|
|
rules.append(rule)
|
|
else:
|
|
collumns = line.split()
|
|
return rules
|
|
|
|
async def remove_iptables_rule(rule_dict):
|
|
cmd = f"{'sudo ' if config.run_iptables_with_sudo else ''}iptables"
|
|
|
|
if rule_dict.get('target') == 'DNAT':
|
|
cmd += " -t nat"
|
|
cmd += " -D PREROUTING"
|
|
if rule_dict.get('prot') and rule_dict['prot'] != '--':
|
|
cmd += f" -p {rule_dict['prot']}"
|
|
if rule_dict.get('in') and rule_dict['in'] != '*':
|
|
cmd += f" -i {rule_dict['in']}"
|
|
if rule_dict.get('out') and rule_dict['out'] != '*':
|
|
cmd += f" -o {rule_dict['out']}"
|
|
if rule_dict.get('source') and rule_dict['source'] != '0.0.0.0/0':
|
|
cmd += f" -s {rule_dict['source']}"
|
|
if rule_dict.get('destination') and rule_dict['destination'] != '0.0.0.0/0':
|
|
cmd += f" -d {rule_dict['destination']}"
|
|
if rule_dict.get('target') == 'DNAT':
|
|
if 'dports' in rule_dict.get('desc', ''):
|
|
port_info = rule_dict['desc'].split('dports ')[1].split(' ')[0]
|
|
if ':' in port_info:
|
|
cmd += f" -m multiport --dports {port_info}"
|
|
else:
|
|
cmd += f" --dport {port_info}"
|
|
if 'to:' in rule_dict.get('desc', ''):
|
|
dest_ip = rule_dict['desc'].split('to:')[1].split()[0]
|
|
cmd += f" -j DNAT --to-destination {dest_ip}"
|
|
await utils.async_run_command(cmd)
|
|
|
|
|
|
async def clore_partner_configure(clore_partner_config):
|
|
try:
|
|
if clore_partner_config:
|
|
docker_restart_required = False
|
|
|
|
needed_openvpn_config = generate_openvpn_config(
|
|
local_ip=clore_partner_config["provider"],
|
|
server_ip=clore_partner_config["forwarding"],
|
|
server_hostname=clore_partner_config["openvpn_host"],
|
|
udp_port=clore_partner_config["openvpn_port"],
|
|
vpn_secret_key=clore_partner_config["secret"]
|
|
)
|
|
|
|
saved_config=''
|
|
|
|
config_exists = await aiofiles.os.path.exists(os.path.join(CLIENT_CONFIGS_LOCATION, PARTNER_CONFIG_NAME))
|
|
if config_exists:
|
|
async with aiofiles.open(os.path.join(CLIENT_CONFIGS_LOCATION, PARTNER_CONFIG_NAME), mode='r') as file:
|
|
saved_config = await file.read()
|
|
|
|
if saved_config != needed_openvpn_config:
|
|
async with aiofiles.open(os.path.join(CLIENT_CONFIGS_LOCATION, PARTNER_CONFIG_NAME), mode='w') as file:
|
|
await file.write(needed_openvpn_config)
|
|
|
|
is_active_code, is_active_stdout, is_active_stderr = await utils.async_run_command(
|
|
f"systemctl is-active openvpn-client@{PARTNER_CONFIG_NAME.replace('.conf','')}"
|
|
)
|
|
|
|
if is_active_code == 0 and saved_config != needed_openvpn_config:
|
|
code, stdout, stderr = await utils.async_run_command(
|
|
f"systemctl restart openvpn-client@{PARTNER_CONFIG_NAME.replace('.conf','')}"
|
|
)
|
|
docker_restart_required = False if code != 0 else True
|
|
elif is_active_code != 0:
|
|
code, stdout, stderr = await utils.async_run_command(
|
|
f"systemctl start openvpn-client@{PARTNER_CONFIG_NAME.replace('.conf','')}"
|
|
)
|
|
docker_restart_required = False if code != 0 else True
|
|
|
|
code, stdout, stderr = await utils.async_run_command(
|
|
f"{'sudo ' if config.run_iptables_with_sudo else ''}ip route show table {str(config.forwarding_ip_route_table_id)}"
|
|
)
|
|
ip_route_configured = False
|
|
if code == 0:
|
|
for line in stdout.split('\n'):
|
|
items = line.split(' ')
|
|
if clore_partner_config["provider"] in items and config.openvpn_forwarding_tun_device in items:
|
|
ip_route_configured = True
|
|
break
|
|
if not ip_route_configured:
|
|
code, stdout, stderr = await utils.async_run_command(
|
|
f"{'sudo ' if config.run_iptables_with_sudo else ''}ip route add 0.0.0.0/0 dev {config.openvpn_forwarding_tun_device} src {clore_partner_config['provider']} table {config.forwarding_ip_route_table_id} && ip rule add from {clore_partner_config['provider']} table {config.forwarding_ip_route_table_id}"
|
|
)
|
|
ip_tables_configured = False
|
|
|
|
rules = await get_iptables_forward_rules()
|
|
|
|
for rule in rules:
|
|
try:
|
|
if rule["in"] == config.openvpn_forwarding_tun_device and rule["target"].lower()=="dnat" and f"{clore_partner_config['ports'][0]}:{clore_partner_config['ports'][1]}" in rule["desc"] and f"to:{clore_partner_config['provider']}" in rule["desc"]:
|
|
ip_tables_configured = True
|
|
elif rule["in"] == config.openvpn_forwarding_tun_device and rule["target"].lower()=="dnat" and "to:10." in rule["desc"] and "dports " in rule["desc"]:
|
|
print("REMOVE RULE", rule)
|
|
await remove_iptables_rule(rule)
|
|
except Exception as ei:
|
|
log.error(f"clore_partner_configure() | ei | {ei}")
|
|
|
|
if ip_tables_configured == False:
|
|
code, stdout, stderr = await utils.async_run_command(
|
|
f"{'sudo ' if config.run_iptables_with_sudo else ''}iptables -t nat -A PREROUTING -i {config.openvpn_forwarding_tun_device} -p tcp -m multiport --dports {clore_partner_config['ports'][0]}:{clore_partner_config['ports'][1]} -j DNAT --to-destination {clore_partner_config['provider']} && {'sudo ' if config.run_iptables_with_sudo else ''}iptables -t nat -A PREROUTING -i {config.openvpn_forwarding_tun_device} -p udp -m multiport --dports {clore_partner_config['ports'][0]}:{clore_partner_config['ports'][1]} -j DNAT --to-destination {clore_partner_config['provider']}"
|
|
)
|
|
|
|
if docker_restart_required:
|
|
async with aiofiles.open(config.restart_docker_flag_file, mode='w') as file:
|
|
await file.write("")
|
|
os._exit(0) # We close clore hosting, because it's mandatory to restart docker after starting the up to date version of VPN, docker will be restarted on next start of clore hosting
|
|
else:
|
|
code, stdout, stderr = await utils.async_run_command(
|
|
f"systemctl stop openvpn-client@{PARTNER_CONFIG_NAME.replace('.conf','')}"
|
|
)
|
|
return True
|
|
except Exception as e:
|
|
log.error(f"FAIL | openvpn.clore_partner_configure | {e}")
|
|
return False |