From 746cad7314081dbf5ea18c4a8ac65b67b95cd999 Mon Sep 17 00:00:00 2001 From: Mikhail Grebenkin Date: Mon, 11 Mar 2019 16:07:27 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20?= =?UTF-8?q?=D1=81=D0=BE=D0=B1=D1=81=D0=BD=D0=B0=20=D1=81=D0=B0=D0=BC=D0=BE?= =?UTF-8?q?=20=D1=81=D0=BE=D0=B4=D0=B5=D1=80=D0=B6=D0=B0=D0=BD=D0=B8=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manageDomains.py | 398 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 398 insertions(+) create mode 100755 manageDomains.py diff --git a/manageDomains.py b/manageDomains.py new file mode 100755 index 0000000..f844fa0 --- /dev/null +++ b/manageDomains.py @@ -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()