#!/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()