@ -0,0 +1,398 @@ | |||||
#!/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.