#!/usr/bin/python3.6
|
|
# coding: utf8
|
|
|
|
|
|
import locale
|
|
from dialog import Dialog
|
|
import os
|
|
import pynginxconfig
|
|
import re
|
|
from goto import with_goto
|
|
# import sys
|
|
# import getopt
|
|
import subprocess
|
|
import shlex
|
|
|
|
|
|
nginx_dir = "/etc/nginx"
|
|
nginx_available_sites = nginx_dir + "/sites_available/"
|
|
nginx_enabled_sites = nginx_dir + "/sites_enabled/"
|
|
nginx_templates = nginx_dir + "/templates/"
|
|
template_host = 'host.conf'
|
|
template_ssl = 'host_ssl.conf'
|
|
|
|
|
|
errcode = list()
|
|
errcode.append(False)
|
|
errcode.append(True)
|
|
errcode.append("string can't be empty")
|
|
errcode.append('English letters only. To issue for an Internationalized Domain Name, use Punycode.')
|
|
errcode.append('string must be numerical')
|
|
errcode.append('')
|
|
|
|
|
|
def get_available():
|
|
f = os.listdir(nginx_available_sites)
|
|
return f
|
|
|
|
|
|
def check_if_enabled(vhost_file):
|
|
if os.path.exists(nginx_enabled_sites + vhost_file):
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
|
|
def check_cert(domain):
|
|
if os.path.isfile('/etc/letsencrypt/live/' + domain.split(' ')[0] + '/cert.pem'):
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
|
|
def make_list(delete=False):
|
|
t = tuple()
|
|
x = get_available()
|
|
a = list()
|
|
for vhost in x:
|
|
if delete is not True:
|
|
t = (vhost, get_server_name(vhost), check_if_enabled(vhost))
|
|
a.append(t)
|
|
else:
|
|
t = (vhost, get_server_name(vhost), False)
|
|
a.append(t)
|
|
s = sorted(a, key=lambda dom: dom[1])
|
|
return s
|
|
|
|
|
|
def enable_vhost(vhost):
|
|
try:
|
|
os.symlink(nginx_available_sites + vhost, nginx_enabled_sites + vhost)
|
|
except FileExistsError:
|
|
pass
|
|
return True
|
|
|
|
|
|
def renew_enabled(vhosts):
|
|
a = get_available()
|
|
for vhost in a:
|
|
if vhost in vhosts:
|
|
if check_if_enabled(vhost):
|
|
pass
|
|
else:
|
|
enable_vhost(vhost)
|
|
else:
|
|
if check_if_enabled(vhost):
|
|
os.unlink(nginx_enabled_sites + vhost)
|
|
else:
|
|
pass
|
|
restart_nginx()
|
|
return True
|
|
|
|
|
|
def get_server_name(vhost_file):
|
|
i = int()
|
|
if vhost_file == 'default.conf':
|
|
ret = 'default.conf'
|
|
else:
|
|
vhost = pynginxconfig.NginxConfig()
|
|
vhost.loadf(nginx_available_sites + vhost_file)
|
|
try:
|
|
ret = vhost.get([('server',), 'server_name'])[1]
|
|
except:
|
|
ret = "None"
|
|
return ret
|
|
|
|
|
|
def enable_redirect(cfg):
|
|
filename = cfg
|
|
vhost = pynginxconfig.NginxConfig()
|
|
vhost.loadf(nginx_available_sites + filename)
|
|
vhost[0]['value'].insert(7, ('rewrite', '^ https://$http_host$request_uri? permanent'))
|
|
vhost.savef(nginx_available_sites + filename)
|
|
return
|
|
|
|
|
|
def is_ascii(s):
|
|
pattern = r'[^.a-zA-Z0-9- ]'
|
|
if len(s) != 0:
|
|
if re.search(pattern, s):
|
|
return 3
|
|
else:
|
|
return 1
|
|
else:
|
|
return 2
|
|
|
|
|
|
def is_num(s):
|
|
pattern = r'[^0-9]'
|
|
if len(s) != 0:
|
|
if re.search(pattern, s):
|
|
return 4
|
|
else:
|
|
return 1
|
|
else:
|
|
return 2
|
|
|
|
|
|
def check_data(data):
|
|
msg = list()
|
|
ret = True
|
|
rc = is_ascii(data['Domain'])
|
|
if rc != True:
|
|
ret = False
|
|
msg.append("Domain: " + str(errcode[rc]))
|
|
|
|
rc = is_ascii(data['SubDomain'])
|
|
if data['SubDomain'] != '':
|
|
if rc != True:
|
|
ret = False
|
|
msg.append("SubDomain: " + str(errcode[rc]))
|
|
|
|
rc = is_ascii(data['Backend_Addr'])
|
|
if rc != True:
|
|
ret = False
|
|
msg.append("Backend_Addr: " + str(errcode[rc]))
|
|
|
|
rc = is_num(data['Backend_Port'])
|
|
if rc != True:
|
|
ret = False
|
|
msg.append("Backend_Port: " + str(errcode[rc]))
|
|
|
|
return ret, msg
|
|
|
|
|
|
def delete_vhosts(vhosts):
|
|
for vhost in vhosts:
|
|
try:
|
|
os.unlink(nginx_enabled_sites + vhost)
|
|
except:
|
|
pass
|
|
try:
|
|
os.unlink(nginx_available_sites + vhost)
|
|
except:
|
|
pass
|
|
restart_nginx()
|
|
|
|
|
|
def form_one():
|
|
locale.setlocale(locale.LC_ALL, '')
|
|
d = Dialog(dialog="dialog")
|
|
d.set_background_title("NGINX domains managment")
|
|
|
|
ret, tags = d.menu("Select action?", width=60, height=10,
|
|
choices=[("Add", "Add new domain"),
|
|
("Enable/Disable", "Enable/Disable selected domains"),
|
|
("Delete", "Delete selected domains")],
|
|
title="")
|
|
return ret, tags
|
|
|
|
|
|
def edit_form(domain='', subdomain='', backend_addr='', backend_port='80'):
|
|
locale.setlocale(locale.LC_ALL, '')
|
|
d = Dialog(dialog="dialog")
|
|
ret, val = d.mixedform("Fill the form:\n \n Enter additional domians separate by space\n (for example: www.domain.ru xxx.domain.ru)", height=14, width=60,
|
|
elements=[("Domain name", 1, 3, domain, 1, 22, 30, 255, 0),
|
|
("Additional domains", 2, 3, subdomain, 2, 22, 30, 65535, 0),
|
|
("Backend addr", 3, 3, backend_addr, 3, 22, 30, 255, 0),
|
|
("Backend port", 4, 3, backend_port, 4, 22, 30, 5, 0)]
|
|
)
|
|
|
|
if ret == 'ok':
|
|
ret = show_yesno('Do you want to do automatically redirect from HTTP to HTTPS?')
|
|
if ret == 'ok':
|
|
values = {'Domain': val[0], 'SubDomain': val[1], 'Backend_Addr': val[2], 'Backend_Port': val[3], 'redirect': True}
|
|
else:
|
|
values = {'Domain': val[0], 'SubDomain': val[1], 'Backend_Addr': val[2], 'Backend_Port': val[3], 'redirect': False}
|
|
return ret, values
|
|
else:
|
|
return ret, ""
|
|
|
|
|
|
def select_form():
|
|
vlist = make_list()
|
|
locale.setlocale(locale.LC_ALL, '')
|
|
d = Dialog(dialog="dialog")
|
|
ret, dom_list = d.checklist("Enable or disable sites", height=30, width=120, list_height=30, choices=vlist)
|
|
if ret == "ok":
|
|
show_info("Please wait. Work in progress")
|
|
renew_enabled(dom_list)
|
|
return ret
|
|
|
|
|
|
def delete_form():
|
|
vlist = make_list(True)
|
|
locale.setlocale(locale.LC_ALL, '')
|
|
d = Dialog(dialog="dialog")
|
|
ret, dom_list = d.checklist("Select to delete:", height=30, width=120, list_height=30, choices=vlist)
|
|
if ret == "ok":
|
|
if show_yesno('Are you sure?\n\nThese domains will be deleted:\n\n' + '\n'.join(dom_list)) == "ok":
|
|
show_info("Please wait. Work in progress")
|
|
delete_vhosts(dom_list)
|
|
else:
|
|
return ret
|
|
|
|
|
|
def show_yesno(msg):
|
|
locale.setlocale(locale.LC_ALL, '')
|
|
d = Dialog(dialog="dialog")
|
|
ret = d.yesno(msg, height=10, width=60)
|
|
return ret
|
|
|
|
|
|
def create_domain(vhost, ssl=False):
|
|
if ssl is not True:
|
|
filename = vhost['Domain'] + '.conf'
|
|
template = nginx_templates + template_host
|
|
else:
|
|
filename = vhost['Domain'] + '_ssl.conf'
|
|
template = nginx_templates + template_ssl
|
|
|
|
domains = vhost['Domain'] + ' '
|
|
for dom in vhost['SubDomain']:
|
|
domains += dom
|
|
vconfig = pynginxconfig.NginxConfig()
|
|
vconfig.loadf(template)
|
|
server_str = vconfig.get([('server',), 'server_name'])[1].replace('[domain_names]', domains)
|
|
log_str = vconfig.get([('server',), 'error_log'])[1].replace('[domain_name]', vhost['Domain'])
|
|
if ssl:
|
|
cert_str = vconfig.get([('server',), 'ssl_certificate'])[1].replace('[domain_name]', vhost['Domain'])
|
|
cert_key_str = vconfig.get([('server',), 'ssl_certificate_key'])[1].replace('[domain_name]', vhost['Domain'])
|
|
vconfig.set([('server',), 'ssl_certificate'], cert_str)
|
|
vconfig.set([('server',), 'ssl_certificate_key'], cert_key_str)
|
|
backend_str = vconfig.get([('server',), 'set'])[1].replace('[host_name]', vhost['Backend_Addr']).replace('[host_port]', vhost['Backend_Port'])
|
|
vconfig.set([('server',), 'server_name'], server_str)
|
|
vconfig.set([('server',), 'error_log'], log_str)
|
|
vconfig.set([('server',), 'set'], backend_str)
|
|
#if vhost['redirect'] is not True:
|
|
# vconfig.remove([('server',), 'rewrite'])
|
|
vconfig.savef(nginx_available_sites + filename)
|
|
return domains, filename
|
|
|
|
|
|
def create_cert(domains):
|
|
if check_cert(domains) is not True:
|
|
cmd = 'certbot certonly --agree-tos --email admin@shopband.ru --webroot -w /var/www/lets -d ' + domains.rstrip().replace(' ', ' -d ')
|
|
ret = subprocess.run(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
if ret.returncode != 0:
|
|
msg = "Error getting ssl certs\nThe SSL configuration is written to " + nginx_available_sites + ", " \
|
|
"but not enabled.\nGet SSL certs manualy and make symlink to " + nginx_enabled_sites
|
|
show_error(msg + "\nLog saved into " + domains.split(' ')[0] + ".log")
|
|
f = open('./' + domains.split(' ')[0] + ".log", 'w')
|
|
f.write(ret.stderr.decode("utf-8"))
|
|
f.close()
|
|
return False
|
|
elif ret.returncode == 0:
|
|
return True
|
|
else:
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
|
|
def restart_nginx():
|
|
ret = subprocess.run(shlex.split('/usr/sbin/nginx -t -c /etc/nginx/nginx.conf'), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
if ret.returncode != 0:
|
|
show_error(ret.stderr.decode("utf-8"))
|
|
return False
|
|
elif ret.returncode == 0:
|
|
ret = subprocess.run(shlex.split('service nginx restart'), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
if ret.returncode != 0:
|
|
show_error(ret.stderr.decode("utf-8"))
|
|
return False
|
|
return True
|
|
|
|
|
|
def show_info(msg):
|
|
s_width = len(msg)
|
|
if s_width > 56:
|
|
s_width = 56
|
|
d = Dialog(dialog="dialog")
|
|
d.infobox(msg, height=3, width=s_width+4)
|
|
return
|
|
|
|
|
|
def show_error(msg):
|
|
txt = ''
|
|
if isinstance(msg, list):
|
|
for err in msg:
|
|
txt = txt + err + '\n'
|
|
else:
|
|
txt = msg
|
|
locale.setlocale(locale.LC_ALL, '')
|
|
d = Dialog(dialog="dialog")
|
|
d.msgbox(text=txt, width=80, height=20)
|
|
return txt
|
|
|
|
|
|
def enable_site(cfg, domains='', ssl=False):
|
|
if ssl is True:
|
|
if create_cert(domains):
|
|
enable_vhost(cfg)
|
|
else:
|
|
return False
|
|
else:
|
|
enable_vhost(cfg)
|
|
|
|
if restart_nginx():
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
# if ssl:
|
|
# ret_code = create_cert(domains)
|
|
# if ret_code:
|
|
# enable_vhost(filename)
|
|
# else:
|
|
# show_error(msg)
|
|
# else:
|
|
# enable_vhost(filename)
|
|
pass
|
|
|
|
|
|
@with_goto
|
|
def start_it():
|
|
label .start
|
|
vals = {'Domain': '', 'SubDomain': '', 'Backend_Addr': '', 'Backend_Port': '80'}
|
|
rc, action = form_one()
|
|
if rc == 'ok':
|
|
if action == "Add":
|
|
label .add
|
|
ret, vals = edit_form(vals['Domain'], vals['SubDomain'], vals['Backend_Addr'], vals['Backend_Port'])
|
|
if ret is not 'ok':
|
|
goto .start
|
|
else:
|
|
ret_code, msg = check_data(vals)
|
|
if ret_code is not True:
|
|
show_error(msg)
|
|
goto .add
|
|
else:
|
|
# Create HTTP config
|
|
domains, cfg = create_domain(vals)
|
|
show_info("Please wait. Work in progress")
|
|
if enable_site(cfg) is not True:
|
|
goto .start
|
|
else:
|
|
http_config = cfg
|
|
|
|
# Create HTTPS config
|
|
domains, cfg = create_domain(vals, True)
|
|
show_info("Please wait. Work in progress")
|
|
if enable_site(cfg, domains, True) is not True:
|
|
print("false")
|
|
goto .start
|
|
else:
|
|
if vals['redirect'] is True:
|
|
enable_redirect(http_config)
|
|
restart_nginx()
|
|
goto .start
|
|
elif action == "Enable/Disable":
|
|
label .endis
|
|
ret = select_form()
|
|
goto .start
|
|
elif action == "Delete":
|
|
label .delete
|
|
delete_form()
|
|
goto .start
|
|
|
|
start_it()
|
Powered by TurnKey Linux.