| @ -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.