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