# coding: utf-8 # ------------------------------------------------------------------- # YakPanel # ------------------------------------------------------------------- # Copyright (c) 2015-2017 YakPanel(www.yakpanel.com) All rights reserved. # ------------------------------------------------------------------- # Author: hwliang # ------------------------------------------------------------------- # ------------------------------ # 网站管理类 #------------------------------ import io,re,public,os,sys,shutil,json,hashlib,socket,time try: import OpenSSL except: os.system("btpip install pyOpenSSL -I") import OpenSSL import base64 try: from YakPanel import session except: pass from panelRedirect import panelRedirect import site_dir_auth import one_key_wp from ssl_manage import SSLManger class panelSite(panelRedirect): siteName = None # 网站名称 sitePath = None # 根目录 sitePort = None # 端口 phpVersion = None # PHP版本 setupPath = None # 安装路径 isWriteLogs = None # 是否写日志 nginx_conf_bak = '/tmp/backup_nginx.conf' apache_conf_bak = '/tmp/backup_apache.conf' is_ipv6 = False def __init__(self): self.setupPath = public.get_setup_path() path = self.setupPath + '/panel/vhost/nginx' if not os.path.exists(path): public.ExecShell("mkdir -p " + path + " && chmod -R 644 " + path) path = self.setupPath + '/panel/vhost/apache' if not os.path.exists(path): public.ExecShell("mkdir -p " + path + " && chmod -R 644 " + path) path = self.setupPath + '/panel/vhost/rewrite' if not os.path.exists(path): public.ExecShell("mkdir -p " + path + " && chmod -R 644 " + path) path = self.setupPath + '/stop' if not os.path.exists(path + '/index.html'): public.ExecShell('mkdir -p ' + path) public.ExecShell('wget -O ' + path + '/index.html ' + public.get_url() + '/stop_en.html &') self.__proxyfile = '{}/data/proxyfile.json'.format(public.get_panel_path()) self.OldConfigFile() if os.path.exists(self.nginx_conf_bak): os.remove(self.nginx_conf_bak) if os.path.exists(self.apache_conf_bak): os.remove(self.apache_conf_bak) self.is_ipv6 = os.path.exists(self.setupPath + '/panel/data/ipv6.pl') sys.setrecursionlimit(1000000) self._proxy_path = '/www/server/proxy_project' self._proxy_config_path = self._proxy_path + '/sites' # 默认配置文件 def check_default(self): nginx = self.setupPath + '/panel/vhost/nginx' httpd = self.setupPath + '/panel/vhost/apache' httpd_default = ''' ServerAdmin webmaster@example.com DocumentRoot "/www/server/apache/htdocs" ServerName bt.default.com SetOutputFilter DEFLATE Options FollowSymLinks AllowOverride All Order allow,deny Allow from all DirectoryIndex index.html ''' listen_ipv6 = '' if self.is_ipv6: listen_ipv6 = "\n listen [::]:80;" nginx_default = '''server { listen 80;%s server_name _; index index.html; root /www/server/nginx/html; }''' % listen_ipv6 if not os.path.exists(httpd + '/0.default.conf') and not os.path.exists( httpd + '/default.conf'): public.writeFile(httpd + '/0.default.conf', httpd_default) if not os.path.exists(nginx + '/0.default.conf') and not os.path.exists( nginx + '/default.conf'): public.writeFile(nginx + '/0.default.conf', nginx_default) # 添加apache端口 def apacheAddPort(self, port): port = str(port) filename = self.setupPath + '/apache/conf/extra/httpd-ssl.conf' if os.path.exists(filename): ssl_conf = public.readFile(filename) if ssl_conf: if ssl_conf.find('Listen 443') != -1: ssl_conf = ssl_conf.replace('Listen 443', '') public.writeFile(filename, ssl_conf) filename = self.setupPath + '/apache/conf/httpd.conf' if not os.path.exists(filename): return allConf = public.readFile(filename) rep = r"Listen\s+([0-9]+)\n" tmp = re.findall(rep, allConf) if not tmp: return False for key in tmp: if key == port: return False listen = "\nListen " + tmp[0] + "\n" listen_ipv6 = '' # if self.is_ipv6: listen_ipv6 = "\nListen [::]:" + port allConf = allConf.replace(listen, listen + "Listen " + port + listen_ipv6 + "\n") public.writeFile(filename, allConf) return True # 添加到apache def apacheAdd(self): import time listen = '' if self.sitePort != '80': self.apacheAddPort(self.sitePort) acc = public.md5(str(time.time()))[0:8] try: httpdVersion = public.readFile(self.setupPath + '/apache/version.pl').strip() except: httpdVersion = "" if httpdVersion == '2.2': vName = '' if self.sitePort != '80' and self.sitePort != '443': vName = "NameVirtualHost *:" + self.sitePort + "\n" phpConfig = "" apaOpt = "Order allow,deny\n\t\tAllow from all" else: vName = "" phpConfig = ''' #PHP SetHandler "proxy:%s" ''' % (public.get_php_proxy(self.phpVersion, 'apache'),) apaOpt = 'Require all granted' conf = r'''%s ServerAdmin webmaster@example.com DocumentRoot "%s" ServerName %s.%s ServerAlias %s #errorDocument 404 /404.html ErrorLog "%s-error_log" CustomLog "%s-access_log" combined #DENY FILES Order allow,deny Deny from all %s #PATH SetOutputFilter DEFLATE Options FollowSymLinks AllowOverride All %s DirectoryIndex index.php index.html index.htm default.php default.html default.htm ''' % (vName, self.sitePort, self.sitePath, acc, self.siteName, self.siteName, public.GetConfigValue('logs_path') + '/' + self.siteName, public.GetConfigValue('logs_path') + '/' + self.siteName, phpConfig, self.sitePath, apaOpt) htaccess = self.sitePath + '/.htaccess' if not os.path.exists(htaccess): public.writeFile(htaccess, ' ') public.ExecShell('chmod -R 644 ' + htaccess) public.ExecShell('chown -R www:www ' + htaccess) filename = self.setupPath + '/panel/vhost/apache/' + self.siteName + '.conf' public.writeFile(filename, conf) return True # 添加到nginx def nginxAdd(self): listen_ipv6 = '' if self.is_ipv6: listen_ipv6 = "\n listen [::]:%s;" % self.sitePort conf = r'''server {{ listen {listen_port};{listen_ipv6} server_name {site_name}; index index.php index.html index.htm default.php default.htm default.html; root {site_path}; #SSL-START {ssl_start_msg} #error_page 404/404.html; #SSL-END #ERROR-PAGE-START {err_page_msg} error_page 404 /404.html; error_page 502 /502.html; #ERROR-PAGE-END #PHP-INFO-START {php_info_start} include enable-php-{php_version}.conf; #PHP-INFO-END #REWRITE-START {rewrite_start_msg} include {setup_path}/panel/vhost/rewrite/{site_name}.conf; #REWRITE-END {description} location ~ ^/(\.user.ini|\.htaccess|\.git|\.env|\.svn|\.project|LICENSE|README.md) {{ return 404; }} {description1} location ~ \.well-known{{ allow all; }} #Prohibit putting sensitive files in certificate verification directory if ( $uri ~ "^/\.well-known/.*\.(php|jsp|py|js|css|lua|ts|go|zip|tar\.gz|rar|7z|sql|bak)$" ) {{ return 403; }} location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$ {{ expires 30d; error_log /dev/null; access_log /dev/null; }} location ~ .*\\.(js|css)?$ {{ expires 12h; error_log /dev/null; access_log /dev/null; }} access_log {log_path}/{site_name}.log; error_log {log_path}/{site_name}.error.log; }}'''.format( listen_port=self.sitePort, listen_ipv6=listen_ipv6, site_path=self.sitePath, ssl_start_msg=public.lang("SSL related configuration, do NOT delete or modify the next line of commented-out 404 rules"), err_page_msg=public.lang("Error page configuration, allowed to be commented, deleted or modified"), php_info_start=public.lang("PHP reference configuration, allowed to be commented, deleted or modified"), php_version=self.phpVersion, setup_path=self.setupPath, rewrite_start_msg=public.lang("URL rewrite rule reference, any modification will invalidate the rewrite rules set by the panel"), description=("# Forbidden files or directories"), description1=("# Directory verification related settings for one-click application for SSL certificate"), log_path=public.GetConfigValue('logs_path'), site_name=self.siteName ) # 写配置文件 filename = self.setupPath + '/panel/vhost/nginx/' + self.siteName + '.conf' public.writeFile(filename, conf) # 生成伪静态文件 urlrewritePath = self.setupPath + '/panel/vhost/rewrite' urlrewriteFile = urlrewritePath + '/' + self.siteName + '.conf' if not os.path.exists(urlrewritePath): os.makedirs(urlrewritePath) open(urlrewriteFile, 'w+').close() if not os.path.exists(urlrewritePath): public.writeFile(urlrewritePath, '') return True # 重新生成nginx配置文件 def rep_site_config(self, get): self.siteName = get.siteName siteInfo = public.M('sites').where('name=?', (self.siteName,)).field('id,path,port').find() siteInfo['domains'] = public.M('domains').where('pid=?', (siteInfo['id'],)).field('name,port').select() siteInfo['binding'] = public.M('binding').where('pid=?', (siteInfo['id'],)).field('domain,path').select() # openlitespeed def openlitespeed_add_site(self, get, init_args=None): # 写主配置httpd_config.conf # 操作默认监听配置 if not self.sitePath: return public.return_msg_gettext(False, public.lang("Not specify parameter [sitePath]")) if init_args: self.siteName = init_args['sitename'] self.phpVersion = init_args['phpv'] self.sitePath = init_args['rundir'] conf_dir = self.setupPath + '/panel/vhost/openlitespeed/' if not os.path.exists(conf_dir): os.makedirs(conf_dir) file = conf_dir + self.siteName + '.conf' v_h = """ #VHOST_TYPE YP_SITENAME START virtualhost YP_SITENAME { vhRoot YP_RUN_PATH configFile /www/server/panel/vhost/openlitespeed/detail/YP_SITENAME.conf allowSymbolLink 1 enableScript 1 restrained 1 setUIDMode 0 } #VHOST_TYPE YP_SITENAME END """ self.old_name = self.siteName if hasattr(get, "dirName"): self.siteName = self.siteName + "_" + get.dirName # sub_dir = self.sitePath + "/" + get.dirName v_h = v_h.replace("VHOST_TYPE", "SUBDIR") v_h = v_h.replace("YP_SITENAME", self.siteName) v_h = v_h.replace("YP_RUN_PATH", self.sitePath) # extp_name = self.siteName + "_" + get.dirName else: self.openlitespeed_domain(get) v_h = v_h.replace("VHOST_TYPE", "VHOST") v_h = v_h.replace("YP_SITENAME", self.siteName) v_h = v_h.replace("YP_RUN_PATH", self.sitePath) # extp_name = self.siteName public.writeFile(file, v_h, "a+") # 写vhost conf = '''docRoot $VH_ROOT vhDomain $VH_NAME adminEmails example@example.com enableGzip 1 enableIpGeo 1 index { useServer 0 indexFiles index.php,index.html } errorlog /www/wwwlogs/$VH_NAME_ols.error_log { useServer 0 logLevel ERROR rollingSize 10M } accesslog /www/wwwlogs/$VH_NAME_ols.access_log { useServer 0 logFormat '%{X-Forwarded-For}i %h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i"' logHeaders 5 rollingSize 10M keepDays 10 compressArchive 1 } scripthandler { add lsapi:YP_EXTP_NAME php } extprocessor YP_OLS_NAME { type lsapi address UDS://tmp/lshttpd/YP_EXTP_NAME.sock maxConns 20 env LSAPI_CHILDREN=20 initTimeout 600 retryTimeout 0 persistConn 1 pcKeepAliveTimeout 1 respBuffer 0 autoStart 1 path /usr/local/lsws/lsphpYP_PHP_V/bin/lsphp extUser www extGroup www memSoftLimit 2047M memHardLimit 2047M procSoftLimit 400 procHardLimit 500 } phpIniOverride { php_admin_value open_basedir "/tmp/:YP_RUN_PATH" } expires { enableExpires 1 expiresByType image/*=A43200,text/css=A43200,application/x-javascript=A43200,application/javascript=A43200,font/*=A43200,application/x-font-ttf=A43200 } rewrite { enable 1 autoLoadHtaccess 1 include /www/server/panel/vhost/openlitespeed/proxy/YP_OLS_NAME/urlrewrite/*.conf include /www/server/panel/vhost/apache/redirect/YP_OLS_NAME/*.conf include /www/server/panel/vhost/openlitespeed/redirect/YP_OLS_NAME/*.conf } include /www/server/panel/vhost/openlitespeed/proxy/YP_OLS_NAME/*.conf ''' open_base_path = self.sitePath if self.sitePath[-1] != '/': open_base_path = self.sitePath + '/' conf = conf.replace('YP_RUN_PATH', open_base_path) conf = conf.replace('YP_EXTP_NAME', self.siteName) conf = conf.replace('YP_PHP_V', self.phpVersion) conf = conf.replace('YP_OLS_NAME', self.siteName) # 写配置文件 conf_dir = self.setupPath + '/panel/vhost/openlitespeed/detail/' if not os.path.exists(conf_dir): os.makedirs(conf_dir) file = conf_dir + self.siteName + '.conf' # if hasattr(get,"dirName"): # file = conf_dir + self.siteName +'_'+get.dirName+ '.conf' public.writeFile(file, conf) # 生成伪静态文件 # urlrewritePath = self.setupPath + '/panel/vhost/rewrite' # urlrewriteFile = urlrewritePath + '/' + self.siteName + '.conf' # if not os.path.exists(urlrewritePath): os.makedirs(urlrewritePath) # open(urlrewriteFile, 'w+').close() return True # 上传CSV文件 # def upload_csv(self, get): # import files # f = files.files() # get.f_path = '/tmp/multiple_website.csv' # result = f.upload(get) # return result # 处理CSV内容 def __process_cvs(self, key): import csv with open('/tmp/multiple_website.csv')as f: f_csv = csv.reader(f) # result = [i for i in f_csv] return [dict(zip(key, i)) for i in [i for i in f_csv if "FTP" not in i]] # 批量创建网站 def __create_website_mulitiple(self, websites_info, site_path, get): create_successfully = {} create_failed = {} for data in websites_info: if not data: continue try: domains = data['website'].split(',') website_name = domains[0].split(':')[0] data['port'] = '80' if len(domains[0].split(':')) < 2 else domains[0].split(':')[1] get.webname = json.dumps({"domain": website_name, "domainlist": domains[1:], "count": 0}) get.path = data['path'] if 'path' in data and data['path'] != '0' and data[ 'path'] != '1' else site_path + '/' + website_name get.version = data['version'] if 'version' in data and data['version'] != '0' else '00' get.ftp = 'true' if 'ftp' in data and data['ftp'] == '1' else False get.sql = 'true' if 'sql' in data and data['sql'] == '1' else False get.port = data['port'] if 'port' in data else '80' get.codeing = 'utf8' get.type = 'PHP' get.type_id = '0' get.ps = '' create_other = {} create_other['db_status'] = False create_other['ftp_status'] = False if get.sql == 'true': create_other['db_pass'] = get.datapassword = public.gen_password(16) create_other['db_user'] = get.datauser = website_name.replace('.', '_') create_other['db_status'] = True if get.ftp == 'true': create_other['ftp_pass'] = get.ftp_password = public.gen_password(16) create_other['ftp_user'] = get.ftp_username = website_name.replace('.', '_') create_other['ftp_status'] = True result = self.AddSite(get, multiple=1) if 'status' in result: create_failed[domains[0]] = result['msg'] continue create_successfully[domains[0]] = create_other except: create_failed[domains[0]] = public.lang("There was an error creating, please try again.") return {'status': True, 'msg': public.lang('Create the website [ {} ] successfully', ','.join(create_successfully)), 'error': create_failed, 'success': create_successfully} # 批量创建网站 def create_website_multiple(self, get): ''' @name 批量创建网站 @author zhwen<2020-11-26> @param create_type txt/csv txt格式为 “网站名|网站路径|是否创建FTP|是否创建数据库|PHP版本” 每个网站一行 "aaa.com:88,bbb.com|/www/wwwserver/aaa.com/或1|1/0|1/0|0/73" csv格式为 “网站名|网站端口|网站路径|PHP版本|是否创建数据库|是否创建FTP” @param websites_content "[[aaa.com|80|/www/wwwserver/aaa.com/|1|1|73]...." ''' key = ['website', 'path', 'ftp', 'sql', 'version'] site_path = public.M('config').getField('sites_path') if get.create_type == 'txt': websites_info = [dict(zip(key, i)) for i in [i.strip().split('|') for i in json.loads(get.websites_content)]] else: websites_info = self.__process_cvs(key) res = self.__create_website_mulitiple(websites_info, site_path, get) public.serviceReload() return res # 检测enable-php-00.conf def check_php_conf(self): try: file = '/www/server/nginx/conf/enable-php.conf' if public.get_webserver() != "nginx": return if os.path.exists(file): return php_v = os.listdir('/www/server/php') if not php_v: return conf = public.readFile('/www/server/nginx/conf/enable-php-{}.conf'.format(php_v[0])) public.writeFile(file,conf) except: pass # 添加站点 def AddSite(self, get, multiple=None): if not get.path: return public.return_msg_gettext(False, public.lang("Please fill in the website path")) if get.path == "/": return public.return_msg_gettext(False, public.lang("The website path cannot be the root directory [/]")) rep_email = r"[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?" if hasattr(get, 'email'): if not re.search(rep_email,get.email): return public.return_msg_gettext(False, public.lang("Please check if the [Email] format correct")) if hasattr(get,'password') and hasattr(get,'pw_weak'): l = public.check_password(get.password) if l == 0 and get.pw_weak == 'off': return public.return_msg_gettext(False, public.lang("Password very weak, if you are sure to use it, please tick [ Allow weak passwords ]")) #判断Mysql PHP 没有安装不能继续 if not os.path.exists("/www/server/mysql") or not os.path.exists("/www/server/php"): return public.return_msg_gettext(False, public.lang("Please install Mysql and PHP first!")) self.check_default() self.check_php_conf() isError = public.checkWebConfig() if isError != True: return public.return_msg_gettext(False, 'ERROR: %s

' % public.lang( 'An error was detected in the configuration file. Please solve it before proceeding') + isError.replace("\n", '
') + '
') import json,files get.path = self.__get_site_format_path(get.path) if not public.check_site_path(get.path): a,c = public.get_sys_path() return public.return_msg_gettext(False, public.lang("Please do not set the website root directory to the system main directory:
{}", "
".join(a+c))) try: siteMenu = json.loads(get.webname) except: return public.return_msg_gettext(False, public.lang("The format of the webname parameter is incorrect, it should be a parseable JSON string")) self.siteName = self.ToPunycode(siteMenu['domain'].strip().split(':')[0]).strip().lower() self.sitePath = self.ToPunycodePath(self.GetPath(get.path.replace(' ', ''))).strip() self.sitePort = get.port.strip().replace(' ', '') if self.sitePort == "": get.port = "80" if not public.checkPort(self.sitePort): return public.return_msg_gettext(False, public.lang("Port range is incorrect! should be between 100-65535")) for domain in siteMenu['domainlist']: if not len(domain.split(':')) == 2: continue if not public.checkPort(domain.split(':')[1]): return public.return_msg_gettext(False, public.lang("Port range is incorrect! should be between 100-65535")) if hasattr(get, 'version'): self.phpVersion = get.version.replace(' ', '') else: self.phpVersion = '00' if not self.phpVersion: self.phpVersion = '00' php_version = self.GetPHPVersion(get) is_phpv = False for php_v in php_version: if self.phpVersion == php_v['version']: is_phpv = True break if not is_phpv: return public.return_msg_gettext(False, public.lang("Requested PHP version does NOT exist!")) domain = None # if siteMenu['count']: # domain = get.domain.replace(' ','') #表单验证 if not self.__check_site_path(self.sitePath): return public.return_msg_gettext(False, public.lang("System critical directory cannot be used as site directory")) if len(self.phpVersion) < 2: return public.return_msg_gettext(False, public.lang("PHP version cannot be empty")) reg = r"^([\w\-\*]{1,100}\.){1,24}([\w\-]{1,24}|[\w\-]{1,24}\.[\w\-]{1,24})$" if not re.match(reg, self.siteName): return public.return_msg_gettext(False, public.lang("Format of primary domain is incorrect")) if self.siteName.find('*') != -1: return public.return_msg_gettext(False, public.lang("Primary domain cannot be wildcard DNS record")) if self.sitePath[-1] == '.': return public.return_msg_gettext(False, 'DIR_END_WITH', ("'.'",)) if not domain: domain = self.siteName # 是否重复 sql = public.M('sites') if sql.where("name=?", (self.siteName,)).count(): return public.return_msg_gettext(False, public.lang("The site you tried to add already exists!")) opid = public.M('domain').where("name=?", (self.siteName,)).getField('pid') if opid: if public.M('sites').where('id=?', (opid,)).count(): return public.return_msg_gettext(False, public.lang("The domain you tried to add already exists!")) public.M('domain').where('pid=?', (opid,)).delete() if public.M('binding').where('domain=?', (self.siteName,)).count(): return public.return_msg_gettext(False, public.lang("The domain you tried to add already exists!")) # 创建根目录 if not os.path.exists(self.sitePath): try: os.makedirs(self.sitePath) except Exception as ex: return public.return_msg_gettext(False, 'Failed to create site document root, {}', (ex,)) public.ExecShell('chmod -R 755 ' + self.sitePath) public.ExecShell('chown -R www:www ' + self.sitePath) # 创建basedir self.DelUserInI(self.sitePath) userIni = self.sitePath + '/.user.ini' if not os.path.exists(userIni): public.writeFile(userIni, 'open_basedir=' + self.sitePath + '/:/tmp/') public.ExecShell('chmod 644 ' + userIni) public.ExecShell('chown root:root ' + userIni) public.ExecShell('chattr +i ' + userIni) ngx_open_basedir_path = self.setupPath + '/panel/vhost/open_basedir/nginx' if not os.path.exists(ngx_open_basedir_path): os.makedirs(ngx_open_basedir_path, 384) ngx_open_basedir_file = ngx_open_basedir_path + '/{}.conf'.format(self.siteName) ngx_open_basedir_body = '''set $bt_safe_dir "open_basedir"; set $bt_safe_open "{}/:/tmp/";'''.format(self.sitePath) public.writeFile(ngx_open_basedir_file, ngx_open_basedir_body) # 创建默认文档 index = self.sitePath + '/index.html' if not os.path.exists(index): public.writeFile(index, public.readFile('data/defaultDoc.html')) public.ExecShell('chmod -R 644 ' + index) public.ExecShell('chown -R www:www ' + index) # 创建自定义404页 doc404 = self.sitePath + '/404.html' if not os.path.exists(doc404): public.writeFile(doc404, public.readFile('data/404.html')) public.ExecShell('chmod -R 644 ' + doc404) public.ExecShell('chown -R www:www ' + doc404) # 创建自定义502页面 doc502 = self.sitePath + '/502.html' if not os.path.exists(doc502) and os.path.exists('data/502.html'): public.writeFile(doc502, public.readFile('data/502.html')) public.ExecShell('chmod -R 644 ' + doc502) public.ExecShell('chown -R www:www ' + doc502) # 写入配置 result = self.nginxAdd() result = self.apacheAdd() result = self.openlitespeed_add_site(get) # 检查处理结果 if not result: return public.return_msg_gettext(False, public.lang("Failed to add, write configuraton ERROR!")) ps = public.xssencode2(get.ps) # 添加放行端口 if self.sitePort != '80': import firewalls get.port = self.sitePort get.ps = self.siteName firewalls.firewalls().AddAcceptPort(get) if not hasattr(get,'type_id'): get.type_id = 0 if not hasattr(get,'project_type'): get.project_type = "PHP" public.check_domain_cloud(self.siteName) # 统计wordpress安装次数 if get.project_type == 'WP': public.count_wp() #写入数据库 get.pid = sql.table('sites').add('name,path,status,ps,type_id,addtime,project_type',(self.siteName,self.sitePath,'1',ps,get.type_id,public.getDate(),get.project_type)) #添加更多域名 for domain in siteMenu['domainlist']: get.domain = domain get.webname = self.siteName get.id = str(get.pid) self.AddDomain(get, multiple) sql.table('domain').add('pid,name,port,addtime', (get.pid, self.siteName, self.sitePort, public.getDate())) data = {} data['siteStatus'] = True data['siteId'] = get.pid # 添加FTP data['ftpStatus'] = False if 'ftp' not in get: get.ftp = False if get.ftp == 'true': import ftp get.ps = self.siteName result = ftp.ftp().AddUser(get) if result['status']: data['ftpStatus'] = True data['ftpUser'] = get.ftp_username data['ftpPass'] = get.ftp_password # 添加数据库 data['databaseStatus'] = False if 'sql' not in get: get.sql = False if get.sql == 'true' or get.sql == 'MySQL': import database if len(get.datauser) > 16: get.datauser = get.datauser[:16] get.name = get.datauser get.db_user = get.datauser get.password = get.datapassword get.address = '127.0.0.1' get.ps = self.siteName result = database.database().AddDatabase(get) if result['status']: data['databaseStatus'] = True data['databaseUser'] = get.datauser data['databasePass'] = get.datapassword data['d_id'] = str(public.M('databases').where('pid=?',(get.pid,)).field('id').find()['id']) if not multiple: public.serviceReload() data = self._set_ssl(get, data, siteMenu) data = self._set_redirect(get, data) public.write_log_gettext('Site manager', 'Successfully added site [{}]!', (self.siteName,)) return data def _set_redirect(self, get, data): try: if not hasattr(get, 'redirect') and not get.redirect: data['redirect'] = False return data import panelRedirect get.redirectdomain = json.dumps([get.redirect]) get.sitename = get.webname get.redirectname = 'Default' get.redirecttype = '301' get.holdpath = '1' get.type = '1' get.domainorpath = 'domain' get.redirectpath = '' if data['ssl']: get.tourl = 'https://{}'.format(get.tourl) else: get.tourl = 'http://{}'.format(get.tourl) panelRedirect.panelRedirect().CreateRedirect(get) data['redirect'] = True except: data['redirect'] = str(public.get_error_info()) data['redirect'] = True return data def _set_ssl(self, get, data, siteMenu): try: if get.set_ssl != '1': data['ssl'] = False return data import acme_v2 ssl_domain = siteMenu['domainlist'] ssl_domain.append(self.siteName) get.id = str(get.pid) get.auth_to = str(get.pid) get.auth_type = 'http' get.auto_wildcard = '' get.domains = json.dumps(ssl_domain) result = acme_v2.acme_v2().apply_cert_api(get) get.type = '1' get.siteName = self.siteName get.key = result['private_key'] get.csr = result['cert'] + result['root'] self.SetSSL(get) data['ssl'] = True if hasattr(get, 'force_ssl') and get.force_ssl == '1': get.siteName = self.siteName self.HttpToHttps(get) except: data['ssl'] = str(public.get_error_info()) return data def __get_site_format_path(self, path): path = path.replace('//', '/') if path[-1:] == '/': path = path[:-1] return path def __check_site_path(self, path): path = self.__get_site_format_path(path) other_path = public.M('config').where("id=?", ('1',)).field('sites_path,backup_path').find() if path == other_path['sites_path'] or path == other_path['backup_path']: return False return True def delete_website_multiple(self, get): ''' @name 批量删除网站 @author zhwen<2020-11-17> @param sites_id "1,2" @param ftp 0/1 @param database 0/1 @param path 0/1 ''' sites_id = get.sites_id.split(',') del_successfully = [] del_failed = {} for site_id in sites_id: get.id = site_id get.webname = public.M('sites').where("id=?", (site_id,)).getField('name') if not get.webname: continue try: self.DeleteSite(get, multiple=1) del_successfully.append(get.webname) except: del_failed[get.webname] = public.lang("There was an error deleting, please try again.") pass public.serviceReload() return {'status': True, 'msg': public.lang('Delete website [{}] successfully', ','.join(del_successfully)), 'error': del_failed, 'success': del_successfully} # 删除站点 def DeleteSite(self, get: public.dict_obj, multiple=None): # 请求参数校验 get.validate([ public.validate.Param('id').Require().Integer(), public.validate.Param('webname').Require().SafePath(), public.validate.Param('path').Integer(), ], [public.validate.trim_filter()]) proxyconf = self.__read_config(self.__proxyfile) id = get.id if public.M('sites').where('id=?', (id,)).count() < 1: return public.return_msg_gettext(False, public.lang("Specified site does NOT exist")) siteName = get.webname get.siteName = siteName self.CloseTomcat(get) # 删除反向代理 for i in range(len(proxyconf) - 1, -1, -1): if proxyconf[i]["sitename"] == siteName: del proxyconf[i] self.__write_config(self.__proxyfile, proxyconf) m_path = self.setupPath + '/panel/vhost/nginx/proxy/' + siteName if os.path.exists(m_path): public.ExecShell("rm -rf %s" % m_path) m_path = self.setupPath + '/panel/vhost/apache/proxy/' + siteName if os.path.exists(m_path): public.ExecShell("rm -rf %s" % m_path) # 删除目录保护 _dir_aith_file = "%s/panel/data/site_dir_auth.json" % self.setupPath _dir_aith_conf = public.readFile(_dir_aith_file) if _dir_aith_conf: try: _dir_aith_conf = json.loads(_dir_aith_conf) if siteName in _dir_aith_conf: del (_dir_aith_conf[siteName]) except: pass self.__write_config(_dir_aith_file, _dir_aith_conf) dir_aith_path = self.setupPath + '/panel/vhost/nginx/dir_auth/' + siteName if os.path.exists(dir_aith_path): public.ExecShell("rm -rf %s" % dir_aith_path) dir_aith_path = self.setupPath + '/panel/vhost/apache/dir_auth/' + siteName if os.path.exists(dir_aith_path): public.ExecShell("rm -rf %s" % dir_aith_path) # 删除重定向 __redirectfile = "%s/panel/data/redirect.conf" % self.setupPath redirectconf = self.__read_config(__redirectfile) for i in range(len(redirectconf) - 1, -1, -1): if redirectconf[i]["sitename"] == siteName: del redirectconf[i] self.__write_config(__redirectfile, redirectconf) m_path = self.setupPath + '/panel/vhost/nginx/redirect/' + siteName if os.path.exists(m_path): public.ExecShell("rm -rf %s" % m_path) m_path = self.setupPath + '/panel/vhost/apache/redirect/' + siteName if os.path.exists(m_path): public.ExecShell("rm -rf %s" % m_path) # 删除配置文件 confPath = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf' if os.path.exists(confPath): os.remove(confPath) confPath = self.setupPath + '/panel/vhost/apache/' + siteName + '.conf' if os.path.exists(confPath): os.remove(confPath) open_basedir_file = self.setupPath + '/panel/vhost/open_basedir/nginx/' + siteName + '.conf' if os.path.exists(open_basedir_file): os.remove(open_basedir_file) # 删除openlitespeed配置 vhost_file = "/www/server/panel/vhost/openlitespeed/{}.conf".format(siteName) if os.path.exists(vhost_file): public.ExecShell('rm -f {}*'.format(vhost_file)) vhost_detail_file = "/www/server/panel/vhost/openlitespeed/detail/{}.conf".format(siteName) if os.path.exists(vhost_detail_file): public.ExecShell('rm -f {}*'.format(vhost_detail_file)) vhost_ssl_file = "/www/server/panel/vhost/openlitespeed/detail/ssl/{}.conf".format(siteName) if os.path.exists(vhost_ssl_file): public.ExecShell('rm -f {}*'.format(vhost_ssl_file)) vhost_sub_file = "/www/server/panel/vhost/openlitespeed/detail/{}_sub.conf".format(siteName) if os.path.exists(vhost_sub_file): public.ExecShell('rm -f {}*'.format(vhost_sub_file)) vhost_redirect_file = "/www/server/panel/vhost/openlitespeed/redirect/{}".format(siteName) if os.path.exists(vhost_redirect_file): public.ExecShell('rm -rf {}*'.format(vhost_redirect_file)) vhost_proxy_file = "/www/server/panel/vhost/openlitespeed/proxy/{}".format(siteName) if os.path.exists(vhost_proxy_file): public.ExecShell('rm -rf {}*'.format(vhost_proxy_file)) # 删除openlitespeed监听配置 self._del_ols_listen_conf(siteName) # 删除伪静态文件 # filename = confPath+'/rewrite/'+siteName+'.conf' filename = '/www/server/panel/vhost/rewrite/' + siteName + '.conf' if os.path.exists(filename): os.remove(filename) public.ExecShell("rm -f " + confPath + '/rewrite/' + siteName + "_*") # 删除日志文件 filename = public.GetConfigValue('logs_path') + '/' + siteName + '*' public.ExecShell("rm -f " + filename) # 删除证书 # crtPath = '/etc/letsencrypt/live/'+siteName # if os.path.exists(crtPath): # import shutil # shutil.rmtree(crtPath) # 删除日志 public.ExecShell("rm -f " + public.GetConfigValue('logs_path') + '/' + siteName + "-*") # 删除备份 # public.ExecShell("rm -f "+session['config']['backup_path']+'/site/'+siteName+'_*') # 删除根目录 if 'path' in get: if get.path == '1': import files get.path = self.__get_site_format_path(public.M('sites').where("id=?",(id,)).getField('path')) if self.__check_site_path(get.path): if public.M('sites').where("path=?", (get.path,)).count() < 2: files.files().DeleteDir(get) get.path = '1' # 重载配置 if not multiple: public.serviceReload() # 从数据库删除 public.M('sites').where("id=?", (id,)).delete() public.M('binding').where("pid=?", (id,)).delete() public.M('domain').where("pid=?", (id,)).delete() public.M('wordpress_onekey').where("s_id=?", (id,)).delete() public.write_log_gettext('Site manager', 'Successfully deleted site!', (siteName,)) # 是否删除关联数据库 if hasattr(get, 'database'): if get.database == '1': find = public.M('databases').where("pid=?", (id,)).field('id,name').find() if find: import database get.name = find['name'] get.id = find['id'] database.database().DeleteDatabase(get) # 是否删除关联FTP if hasattr(get, 'ftp'): if get.ftp == '1': find = public.M('ftps').where("pid=?", (id,)).field('id,name').find() if find: import ftp get.username = find['name'] get.id = find['id'] ftp.ftp().DeleteUser(get) return public.return_msg_gettext(True, public.lang("Successfully deleted site!")) def _del_ols_listen_conf(self, sitename): conf_dir = '/www/server/panel/vhost/openlitespeed/listen/' if not os.path.exists(conf_dir): return False for i in os.listdir(conf_dir): file_name = conf_dir + i if os.path.isdir(file_name): continue conf = public.readFile(file_name) if not conf: continue map_rep = r'map\s+{}.*'.format(sitename) conf = re.sub(map_rep, '', conf) if "map" not in conf: public.ExecShell('rm -f {}*'.format(file_name)) continue public.writeFile(file_name, conf) # 域名编码转换 def ToPunycode(self, domain): import re if sys.version_info[0] == 2: domain = domain.encode('utf8') tmp = domain.split('.') newdomain = '' for dkey in tmp: if dkey == '*': continue # 匹配非ascii字符 match = re.search(u"[\x80-\xff]+", dkey) if not match: match = re.search(u"[\u4e00-\u9fa5]+", dkey) if not match: newdomain += dkey + '.' else: if sys.version_info[0] == 2: newdomain += 'xn--' + dkey.decode('utf-8').encode('punycode') + '.' else: newdomain += 'xn--' + dkey.encode('punycode').decode('utf-8') + '.' if tmp[0] == '*': newdomain = "*." + newdomain return newdomain[0:-1] # 中文路径处理 def ToPunycodePath(self, path): if sys.version_info[0] == 2: path = path.encode('utf-8') if os.path.exists(path): return path import re match = re.search(u"[\x80-\xff]+", path) if not match: match = re.search(u"[\u4e00-\u9fa5]+", path) if not match: return path npath = '' for ph in path.split('/'): npath += '/' + self.ToPunycode(ph) return npath.replace('//', '/') def export_domains(self, args): ''' @name 导出域名列表 @author hwliang<2020-10-27> @param args{ siteName: string<网站名称> } @return string ''' pid = public.M('sites').where('name=?', args.siteName).getField('id') domains = public.M('domain').where('pid=?', pid).field('name,port').select() text_data = [] for domain in domains: text_data.append("{}:{}".format(domain['name'], domain['port'])) data = "\n".join(text_data) return public.send_file(data, '{}_domains'.format(args.siteName)) def import_domains(self, args): ''' @name 导入域名 @author hwliang<2020-10-27> @param args{ siteName: string<网站名称> domains: string<域名列表> 每行一个 格式: 域名:端口 } @return string ''' domains_tmp = args.domains.split("\n") get = public.dict_obj() get.webname = args.siteName get.id = public.M('sites').where('name=?', args.siteName).getField('id') domains = [] for domain in domains_tmp: if public.M('domain').where('name=?', domain.split(':')[0]).count(): continue domains.append(domain) get.domain = ','.join(domains) return self.AddDomain(get) # 添加域名 def AddDomain(self, get, multiple=None): # 检查配置文件 isError = public.checkWebConfig() if isError != True: return public.return_msg_gettext(False, 'ERROR: %s

' % public.get_msg_gettext( 'An error was detected in the configuration file. Please solve it before proceeding') + isError.replace("\n", '
') + '
') if not 'domain' in get: return public.return_msg_gettext(False, public.lang("Please enter the host domain name")) if len(get.domain) < 3: return public.return_msg_gettext(False, public.lang("Domain cannot be empty!")) domains = get.domain.replace(' ', '').split(',') for domain in domains: if domain == "": continue domain = domain.strip().split(':') get.domain = self.ToPunycode(domain[0]).lower() get.port = '80' # 判断通配符域名格式 if get.domain.find('*') != -1 and get.domain.find('*.') == -1: return public.return_msg_gettext(False, public.lang("Domain name format is incorrect!")) # 判断域名格式 reg = r"^([\w\-\*]{1,100}\.){1,24}([\w\-]{1,24}|[\w\-]{1,24}\.[\w\-]{1,24})$" if not re.match(reg, get.domain): return public.return_msg_gettext(False, public.lang("Format of domain is invalid!")) # 获取自定义端口 if len(domain) == 2: get.port = domain[1] if get.port == "": get.port = "80" # 判断端口是否合法 if not public.checkPort(get.port): return public.return_msg_gettext(False, public.lang("Port range is incorrect! should be between 100-65535")) # 检查域名是否存在 sql = public.M('domain') opid = sql.where("name=? AND (port=? OR pid=?)", (get.domain, get.port, get.id)).getField('pid') if opid: siteName = public.M('sites').where('id=?',(opid,)).getField('name') if siteName: return public.return_msg_gettext(False, public.lang("The specified domain name has been bound by the website [{}]", siteName)) sql.where('pid=?', (opid,)).delete() opid = public.M('binding').where('domain=?', (get.domain,)).getField('pid') if opid: siteName = public.M('sites').where('id=?',(opid,)).getField('name') return public.returnMsg(False, public.lang("The specified domain name has been bound by a subdirectory of the website [{}]!", siteName)) # 写配置文件 self.NginxDomain(get) try: self.ApacheDomain(get) self.openlitespeed_domain(get) if self._check_ols_ssl(get.webname): get.port = '443' self.openlitespeed_domain(get) get.port = '80' except: pass # 检查实际端口 if len(domain) == 2: get.port = domain[1] # 添加放行端口 if get.port != '80': import firewalls get.ps = get.domain firewalls.firewalls().AddAcceptPort(get) # 重载webserver服务 if not multiple: public.serviceReload() full_domain = get.domain if not get.port in ['80','443']: full_domain += ':' + get.port public.check_domain_cloud(full_domain) public.write_log_gettext('Site manager', 'Site [{}] added domain [{}] successfully!', (get.webname, get.domain)) sql.table('domain').add('pid,name,port,addtime', (get.id, get.domain, get.port, public.getDate())) return public.return_msg_gettext(True, public.lang("Successfully added site!")) # 判断ols_ssl是否已经设置 def _check_ols_ssl(self, webname): conf = public.readFile('/www/server/panel/vhost/openlitespeed/listen/443.conf') if conf and webname in conf: return True return False # 添加openlitespeed 80端口监听 def openlitespeed_set_80_domain(self, get, conf): rep = r'map\s+{}.*'.format(get.webname) domains = get.webname.strip().split(',') if conf: map_tmp = re.search(rep, conf) if map_tmp: map_tmp = map_tmp.group() domains = map_tmp.strip().split(',') if not public.inArray(domains, get.domain): new_map = '{},{}'.format(conf, get.domain) conf = re.sub(rep, new_map, conf) else: map_tmp = '\tmap\t{d} {d}\n'.format(d=domains[0]) listen_rep = r"secure\s*0" conf = re.sub(listen_rep, "secure 0\n" + map_tmp, conf) return conf else: rep_default = 'listener\\s+Default\\{(\n|[\\s\\w\\*\\:\\#\\.\\,])*' tmp = re.search(rep_default, conf) # domains = get.webname.strip().split(',') if tmp: tmp = tmp.group() new_map = '\tmap\t{d} {d}\n'.format(d=domains[0]) tmp += new_map conf = re.sub(rep_default, tmp, conf) return conf # openlitespeed写域名配置 def openlitespeed_domain(self, get): listen_dir = '/www/server/panel/vhost/openlitespeed/listen/' if not os.path.exists(listen_dir): os.makedirs(listen_dir) listen_file = listen_dir + get.port + ".conf" listen_conf = public.readFile(listen_file) try: get.webname = json.loads(get.webname) get.domain = get.webname['domain'].replace('\r', '') get.webname = get.domain + "," + ",".join(get.webname["domainlist"]) if get.webname[-1] == ',': get.webname = get.webname[:-1] except: pass if listen_conf: # 添加域名 rep = r'map\s+{}.*'.format(get.webname) map_tmp = re.search(rep, listen_conf) if map_tmp: map_tmp = map_tmp.group() domains = map_tmp.strip().split(',') if not public.inArray(domains, get.domain): new_map = '{},{}'.format(map_tmp, get.domain) listen_conf = re.sub(rep, new_map, listen_conf) else: domains = get.webname.strip().split(',') map_tmp = '\tmap\t{d} {d}'.format(d=domains[0]) listen_rep = r"secure\s*0" listen_conf = re.sub(listen_rep, "secure 0\n" + map_tmp, listen_conf) else: listen_conf = """ listener Default%s{ address *:%s secure 0 map %s %s } """ % (get.port, get.port, get.webname, get.domain) # 保存配置文件 public.writeFile(listen_file, listen_conf) return True # Nginx写域名配置 def NginxDomain(self, get): file = self.setupPath + '/panel/vhost/nginx/' + get.webname + '.conf' conf = public.readFile(file) if not conf: return # 添加域名 rep = r"server_name\s*(.*);" tmp = re.search(rep, conf).group() domains = tmp.replace(';', '').strip().split(' ') if not public.inArray(domains, get.domain): newServerName = tmp.replace(';', ' ' + get.domain + ';') conf = conf.replace(tmp, newServerName) # 添加端口 rep = r"listen\s+[\[\]\:]*([0-9]+).*;" tmp = re.findall(rep, conf) if not public.inArray(tmp, get.port): listen = re.search(rep, conf).group() listen_ipv6 = '' if self.is_ipv6: listen_ipv6 = "\n\t\tlisten [::]:" + get.port + ';' conf = conf.replace(listen, listen + "\n\t\tlisten " + get.port + ';' + listen_ipv6) # 保存配置文件 public.writeFile(file, conf) return True # Apache写域名配置 def ApacheDomain(self, get): file = self.setupPath + '/panel/vhost/apache/' + get.webname + '.conf' conf = public.readFile(file) if not conf: return port = get.port siteName = get.webname newDomain = get.domain find = public.M('sites').where("id=?", (get.id,)).field('id,name,path').find() sitePath = find['path'] siteIndex = 'index.php index.html index.htm default.php default.html default.htm' # 添加域名 if conf.find('') != -1: repV = r"(.|\n)*" domainV = re.search(repV, conf).group() rep = r"ServerAlias\s*(.*)\n" tmp = re.search(rep, domainV).group(0) domains = tmp.strip().split(' ') if not public.inArray(domains, newDomain): rs = tmp.replace("\n", "") newServerName = rs + ' ' + newDomain + "\n" myconf = domainV.replace(tmp, newServerName) conf = re.sub(repV, myconf, conf) if conf.find('') != -1: repV = r"(.|\n)*" domainV = re.search(repV, conf).group() rep = r"ServerAlias\s*(.*)\n" tmp = re.search(rep, domainV).group(0) domains = tmp.strip().split(' ') if not public.inArray(domains, newDomain): rs = tmp.replace("\n", "") newServerName = rs + ' ' + newDomain + "\n" myconf = domainV.replace(tmp, newServerName) conf = re.sub(repV, myconf, conf) else: try: httpdVersion = public.readFile(self.setupPath + '/apache/version.pl').strip() except: httpdVersion = "" if httpdVersion == '2.2': vName = '' if self.sitePort != '80' and self.sitePort != '443': vName = "NameVirtualHost *:" + port + "\n" phpConfig = "" apaOpt = "Order allow,deny\n\t\tAllow from all" else: vName = "" # rep = r"php-cgi-([0-9]{2,3})\.sock" # version = re.search(rep,conf).groups()[0] version = public.get_php_version_conf(conf) if len(version) < 2: return public.return_msg_gettext(False, public.lang("Failed to get PHP version!")) phpConfig = ''' #PHP SetHandler "proxy:%s" ''' % (public.get_php_proxy(version, 'apache'),) apaOpt = 'Require all granted' newconf = r''' ServerAdmin webmaster@example.com DocumentRoot "%s" ServerName %s.%s ServerAlias %s #errorDocument 404 /404.html ErrorLog "%s-error_log" CustomLog "%s-access_log" combined %s #DENY FILES Order allow,deny Deny from all #PATH SetOutputFilter DEFLATE Options FollowSymLinks AllowOverride All %s DirectoryIndex %s ''' % (port, sitePath, siteName, port, newDomain, public.GetConfigValue('logs_path') + '/' + siteName, public.GetConfigValue('logs_path') + '/' + siteName, phpConfig, sitePath, apaOpt, siteIndex) conf += "\n\n" + newconf # 添加端口 if port != '80' and port != '888': self.apacheAddPort(port) # 保存配置文件 public.writeFile(file, conf) return True def delete_domain_multiple(self, get): ''' @name 批量删除网站 @author zhwen<2020-11-17> @param id "1" @param domains_id 1,2,3 ''' domains_id = get.domains_id.split(',') get.webname = public.M('sites').where("id=?", (get.id,)).getField('name') del_successfully = [] del_failed = {} for domain_id in domains_id: get.domain = public.M('domain').where("id=? and pid=?", (domain_id, get.id)).getField('name') get.port = str(public.M('domain').where("id=? and pid=?", (domain_id, get.id)).getField('port')) if not get.webname: continue try: result = self.DelDomain(get, multiple=1) tmp = get.domain + ':' + get.port if not result['status']: del_failed[tmp] = result['msg'] continue del_successfully.append(tmp) except: tmp = get.domain + ':' + get.port del_failed[tmp] = public.lang("There was an error deleting, please try again.") pass public.serviceReload() return {'status': True, 'msg': public.get_msg_gettext('Delete domain [{}] successfully', (','.join(del_successfully),)), 'error': del_failed, 'success': del_successfully} # 删除域名 def DelDomain(self, get, multiple=None): if not 'id' in get: return public.return_msg_gettext(False, public.lang("Please choose a domain name")) if not 'port' in get: return public.return_msg_gettext(False, public.lang("Please choose a port")) sql = public.M('domain') id = get['id'] port = get.port domain_data = sql.where("pid=? AND name=?", (get.id, get.domain)).field('id,name').find() if isinstance(domain_data, list): if not domain_data: return public.return_message(-1, 0, public.lang("Domain record not found")) domain_data = domain_data[0] if not isinstance(domain_data, dict) or not domain_data.get('id'): return public.return_message(-1, 0, public.lang("Domain record not found")) domain_count = sql.table('domain').where("pid=?", (id,)).count() if domain_count <= 1: return public.return_message(-1, 0, public.lang("Last domain cannot be deleted!")) domain_count = sql.table('domain').where("pid=?", (id,)).count() if domain_count == 1: return public.return_msg_gettext(False, public.lang("Last domain cannot be deleted!")) # nginx file = self.setupPath + '/panel/vhost/nginx/' + get['webname'] + '.conf' conf = public.readFile(file) if conf: # 删除域名 rep = r"server_name\s+(.+);" match = re.search(rep, conf) if match: tmp = match.group() newServerName = tmp.replace(' ' + get['domain'] + ';', ';') newServerName = newServerName.replace(' ' + get['domain'] + ' ', ' ') conf = conf.replace(tmp, newServerName) else: public.WriteLog("Site manager", f"No server_name found in the Nginx configuration, the domain {get.domain} is only removed from the database") # 删除端口 rep = r"listen.*[\s:]+(\d+).*;" tmp = re.findall(rep, conf) port_count = sql.table('domain').where('pid=? AND port=?', (get.id, get.port)).count() if public.inArray(tmp, port) == True and port_count < 2: rep = r"\n*\s+listen.*[\s:]+" + port + r"\s*;" conf = re.sub(rep, '', conf) # 保存配置 public.writeFile(file, conf) # apache file = self.setupPath + '/panel/vhost/apache/' + get['webname'] + '.conf' conf = public.readFile(file) if conf: # 删除域名 try: rep = r"\n*(.|\n)*" tmp = re.search(rep, conf).group() rep1 = "ServerAlias\\s+(.+)\n" tmp1 = re.findall(rep1, tmp) tmp2 = tmp1[0].split(' ') if len(tmp2) < 2: conf = re.sub(rep, '', conf) rep = r"NameVirtualHost.+\:" + port + "\n" conf = re.sub(rep, '', conf) else: newServerName = tmp.replace(' ' + get['domain'] + "\n", "\n") newServerName = newServerName.replace(' ' + get['domain'] + ' ', ' ') conf = conf.replace(tmp, newServerName) # 保存配置 public.writeFile(file, conf.strip()) except: pass # openlitespeed self._del_ols_domain(get) sql.table('domain').where("id=?", (domain_data['id'],)).delete() public.write_log_gettext('Site manager', 'Site [{}] deleted domain [{}] successfully!', (get.webname, get.domain)) if not multiple: public.serviceReload() return public.return_msg_gettext(True, public.lang("Successfully deleted")) # openlitespeed删除域名 def _del_ols_domain(self, get): conf_dir = '/www/server/panel/vhost/openlitespeed/listen/' if not os.path.exists(conf_dir): return False for i in os.listdir(conf_dir): file_name = conf_dir + i if os.path.isdir(file_name): continue conf = public.readFile(file_name) map_rep = r'map\s+{}\s+(.*)'.format(get.webname) domains = re.search(map_rep, conf) if domains: domains = domains.group(1).split(',') if get.domain in domains: domains.remove(get.domain) if len(domains) == 0: os.remove(file_name) continue else: domains = ",".join(domains) map_c = "map\t{} ".format(get.webname) + domains conf = re.sub(map_rep, map_c, conf) public.writeFile(file_name, conf) # 检查域名是否解析 def CheckDomainPing(self, get): try: epass = public.GetRandomString(32) spath = get.path + '/.well-known/pki-validation' if not os.path.exists(spath): public.ExecShell("mkdir -p '" + spath + "'") public.writeFile(spath + '/fileauth.txt', epass) result = public.httpGet( 'http://' + get.domain.replace('*.', '') + '/.well-known/pki-validation/fileauth.txt') if result == epass: return True return False except: return False def analyze_ssl(self, csr): issuer_dic = {} try: from cryptography import x509 from cryptography.hazmat.backends import default_backend cert = x509.load_pem_x509_certificate(csr.encode("utf-8"), default_backend()) issuer = cert.issuer for i in issuer: issuer_dic[i.oid._name] = i.value except: pass return issuer_dic # 保存第三方证书 def SetSSL(self, get): import ssl_info ssl_info = ssl_info.ssl_info() get.key = get.key.strip() get.csr = get.csr.strip() issuer = self.analyze_ssl(get.csr) if issuer.get("organizationName") == "Let's Encrypt": get.csr += "\n" siteName = get.siteName path = '/www/server/panel/vhost/cert/' + siteName csrpath = path + "/fullchain.pem" keypath = path + "/privkey.pem" if (get.key.find('KEY') == -1): return public.return_msg_gettext(False, public.lang("Private Key ERROR, please check!")) if (get.csr.find('CERTIFICATE') == -1): return public.return_msg_gettext(False, public.lang("Certificate ERROR, please check!")) public.writeFile('/tmp/cert.pl', get.csr) if not public.CheckCert('/tmp/cert.pl'): return public.return_msg_gettext(False, public.lang("Error getting certificate")) #验证格式 # format_status, format_message = ssl_info.verify_format('key',get.key) # if not format_status: # return public.returnMsg(False, format_message) # format_status, format_message = ssl_info.verify_format('cert',get.csr) # if not format_status: # return public.returnMsg(False, format_message) # 验证证书和密钥是否匹配格式是否为pem check_flag, check_msg = ssl_info.verify_certificate_and_key_match(get.key, get.csr) if not check_flag: return public.returnMsg(False, check_msg) # 验证证书链是否完整 check_chain_flag, check_chain_msg = ssl_info.verify_certificate_chain(get.csr) if not check_chain_flag: return public.returnMsg(False, check_chain_msg) backup_cert = '/tmp/backup_cert_' + siteName import shutil if os.path.exists(backup_cert): shutil.rmtree(backup_cert) if os.path.exists(path): shutil.move(path, backup_cert) if os.path.exists(path): shutil.rmtree(path) public.ExecShell('mkdir -p ' + path) public.writeFile(keypath, get.key) public.writeFile(csrpath, get.csr) # 写入配置文件 result = self.SetSSLConf(get) if not result['status']: return result isError = public.checkWebConfig() if (type(isError) == str): if os.path.exists(path): shutil.rmtree(backup_cert) if os.path.exists(backup_cert): shutil.move(backup_cert, path) return public.return_msg_gettext(False, public.lang('ERROR:
' + isError.replace("\n", '
') + '
')) public.serviceReload() if os.path.exists(path + '/partnerOrderId'): os.remove(path + '/partnerOrderId') if os.path.exists(path + '/certOrderId'): os.remove(path + '/certOrderId') p_file = '/etc/letsencrypt/live/' + get.siteName if os.path.exists(p_file): shutil.rmtree(p_file) public.write_log_gettext('Site manager', 'Certificate saved!') # 清理备份证书 if os.path.exists(backup_cert): shutil.rmtree(backup_cert) return public.return_msg_gettext(True, public.lang("Certificate saved!")) # 获取运行目录 def GetRunPath(self, get): if not hasattr(get, 'id'): if hasattr(get, 'siteName'): get.id = public.M('sites').where('name=?', (get.siteName,)).getField('id') else: get.id = public.M('sites').where('path=?', (get.path,)).getField('id') if not get.id: return False if type(get.id) == list: get.id = get.id[0]['id'] result = self.GetSiteRunPath(get) if 'runPath' in result: return result['runPath'] return False # 创建Let's Encrypt免费证书 def CreateLet(self, get): domains = json.loads(get.domains) if not len(domains): return public.return_msg_gettext(False, public.lang("Please choose a domain name")) file_auth = True if hasattr(get, 'dnsapi'): file_auth = False if not hasattr(get, 'dnssleep'): get.dnssleep = 10 email = public.M('users').getField('email') if hasattr(get, 'email'): if get.email.find('@') == -1: get.email = email else: get.email = get.email.strip() public.M('users').where('id=?', (1,)).setField('email', get.email) else: get.email = email for domain in domains: if public.checkIp(domain): continue if domain.find('*.') >= 0 and file_auth: return public.return_msg_gettext(False, public.lang("A generic domain name cannot be used to apply for a certificate using [File Validation]!")) if file_auth: get.sitename = get.siteName if self.GetRedirectList(get): return public.return_msg_gettext(False, public.lang("Your site has 301 Redirect on,Please turn it off first!")) if self.GetProxyList(get): return public.return_msg_gettext(False, public.lang("Sites that have reverse proxy turned on cannot request SSL!")) data = self.get_site_info(get.siteName) get.id = data['id'] runPath = self.GetRunPath(get) if runPath != '/': if runPath[:1] != '/': runPath = '/' + runPath else: runPath = '' get.site_dir = data['path'] + runPath else: dns_api_list = self.GetDnsApi(get) get.dns_param = None for dns in dns_api_list: if dns['name'] == get.dnsapi: param = [] if not dns['data']: continue for val in dns['data']: param.append(val['value']) get.dns_param = '|'.join(param) n_list = ['dns', 'dns_bt'] if not get.dnsapi in n_list: if len(get.dns_param) < 16: return public.return_msg_gettext(False, 'No valid DNSAPI key information found', (get.dnsapi,)) if get.dnsapi == 'dns_bt': if not os.path.exists('plugin/dns/dns_main.py'): return public.return_msg_gettext(False, public.lang("Please go to the software store to install [Cloud Resolution] and complete the domain name NS binding.")) self.check_ssl_pack() try: import panelLets public.mod_reload(panelLets) except Exception as ex: if str(ex).find('No module named requests') != -1: public.ExecShell("pip install requests &") return public.return_msg_gettext(False, public.lang("Missing requests component, please try to repair the panel!")) return public.return_msg_gettext(False, str(ex)) lets = panelLets.panelLets() result = lets.apple_lest_cert(get) if result['status'] and not 'code' in result: get.onkey = 1 path = '/www/server/panel/cert/' + get.siteName if os.path.exists(path + '/certOrderId'): os.remove(path + '/certOrderId') result = self.SetSSLConf(get) return result def get_site_info(self, siteName): data = public.M("sites").where('name=?', siteName).field('id,path,name').find() return data # 检测依赖库 def check_ssl_pack(self): try: import requests except: public.ExecShell('btpip install requests') try: import OpenSSL except: public.ExecShell('btpip install pyOpenSSL') # 判断DNS-API是否设置 def Check_DnsApi(self, dnsapi): dnsapis = self.GetDnsApi(None) for dapi in dnsapis: if dapi['name'] == dnsapi: if not dapi['data']: return True for d in dapi['data']: if d['key'] == '': return False return True # 获取DNS-API列表 def GetDnsApi(self, get): api_path = './config/dns_api.json' api_init = './config/dns_api_init.json' if not os.path.exists(api_path): if os.path.exists(api_init): import shutil shutil.copyfile(api_init, api_path) apis = json.loads(public.ReadFile(api_path)) path = '/root/.acme.sh' if not os.path.exists(path + '/account.conf'): path = "/.acme.sh" account = public.readFile(path + '/account.conf') if not account: account = '' is_write = False for i in range(len(apis)): if not apis[i]['data']: continue for j in range(len(apis[i]['data'])): if apis[i]['data'][j]['value']: continue match = re.search(apis[i]['data'][j]['key'] + r"\s*=\s*'(.+)'", account) if match: apis[i]['data'][j]['value'] = match.groups()[0] if apis[i]['data'][j]['value']: is_write = True if is_write: public.writeFile('./config/dns_api.json', json.dumps(apis)) # aa dns api support info from sslModel.dnsapiModel import main supports_info = main().dns_support_info() result = [] for support in supports_info: for api in apis: if api['title'] == 'CloudFlare': if os.path.exists('/www/server/panel/data/cf_limit_api.pl'): api['API_Limit'] = True else: api['API_Limit'] = False if api['name'] == support['name']: support['data'] = api.get('data') result.append(support) break else: result.append(support) result.sort(key=lambda x: x['title']) apis.sort(key=lambda x: x['title']) if result != apis: public.writeFile('./config/dns_api.json', json.dumps(result)) for index, item in enumerate(apis): if item.get("title", "") == "Manual resolution": target_dict = apis.pop(index) apis.insert(0, target_dict) break return apis # 设置DNS-API def SetDnsApi(self, get): pdata = json.loads(get.pdata) cf_limit_api = "/www/server/panel/data/cf_limit_api.pl" if 'API_Limit' in pdata and pdata['API_Limit'] == True and not os.path.exists(cf_limit_api): os.mknod(cf_limit_api) if 'API_Limit' in pdata and pdata['API_Limit']== False: if os.path.exists(cf_limit_api):os.remove(cf_limit_api) apis = json.loads(public.ReadFile('./config/dns_api.json')) is_write = False for key in pdata.keys(): for i in range(len(apis)): if not apis[i]['data']: continue for j in range(len(apis[i]['data'])): if apis[i]['data'][j]['key'] != key: continue apis[i]['data'][j]['value'] = pdata[key] is_write = True if is_write: public.writeFile('./config/dns_api.json', json.dumps(apis)) return public.return_msg_gettext(True, public.lang("Setup successfully!")) # 获取站点所有域名 def GetSiteDomains(self, get): data = {} domains = public.M('domain').where('pid=?', (get.id,)).field('name,id').select() binding = public.M('binding').where('pid=?', (get.id,)).field('domain,id').select() if type(binding) == str: return binding for b in binding: tmp = {} tmp['name'] = b['domain'] tmp['id'] = b['id'] tmp['binding'] = True domains.append(tmp) data['domains'] = domains data['email'] = public.M('users').where('id=?', (1,)).getField('email') if data['email'] == '287962566@qq.com': data['email'] = '' return data def GetFormatSSLResult(self, result): try: import re rep = "\\s*Domain:.+\n\\s+Type:.+\n\\s+Detail:.+" tmps = re.findall(rep, result) statusList = [] for tmp in tmps: arr = tmp.strip().split('\n') status = {} for ar in arr: tmp1 = ar.strip().split(':') status[tmp1[0].strip()] = tmp1[1].strip() if len(tmp1) > 2: status[tmp1[0].strip()] = tmp1[1].strip() + ':' + tmp1[2] statusList.append(status) return statusList except: return None # 获取TLS1.3标记 def get_tls13(self): nginx_bin = '/www/server/nginx/sbin/nginx' nginx_v = public.ExecShell(nginx_bin + ' -V 2>&1')[0] nginx_v_re = re.findall(r"nginx/(\d\.\d+).+OpenSSL\s+(\d\.\d+)",nginx_v,re.DOTALL) if nginx_v_re: if nginx_v_re[0][0] in ['1.8','1.9','1.7','1.6','1.5','1.4']: return '' if float(nginx_v_re[0][0]) >= 1.15 and float(nginx_v_re[0][-1]) >= 1.1: return ' TLSv1.3' else: _v = re.search(r'nginx/1\.1(5|6|7|8|9).\d',nginx_v) if not _v: _v = re.search(r'nginx/1\.2\d\.\d',nginx_v) openssl_v = public.ExecShell(nginx_bin + ' -V 2>&1|grep OpenSSL')[0].find('OpenSSL 1.1.') != -1 if _v and openssl_v: return ' TLSv1.3' return '' # 获取apache反向代理 def get_apache_proxy(self, conf): rep = "\n*#Referenced reverse proxy rule, if commented, the configured reverse proxy will be invalid\n+\\s+IncludeOptiona.*" proxy = re.search(rep, conf) if proxy: return proxy.group() return "" def _get_site_domains(self, sitename): site_id = public.M('sites').where('name=?', (sitename,)).field('id').find() domains = public.M('domain').where('pid=?', (site_id['id'],)).field('name').select() domains = [d['name'] for d in domains] return domains # 设置OLS ssl def set_ols_ssl(self, get, siteName): listen_conf = self.setupPath + '/panel/vhost/openlitespeed/listen/443.conf' conf = public.readFile(listen_conf) ssl_conf = """ vhssl { keyFile /www/server/panel/vhost/cert/YP_SSL_DOMAIN/privkey.pem certFile /www/server/panel/vhost/cert/YP_SSL_DOMAIN/fullchain.pem certChain 1 sslProtocol 24 ciphers EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:ECDHE-RSA-AES128-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA128:DHE-RSA-AES128-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA128:ECDHE-RSA-AES128-SHA384:ECDHE-RSA-AES128-SHA128:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA128:DHE-RSA-AES128-SHA128:DHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA384:AES128-GCM-SHA128:AES128-SHA128:AES128-SHA128:AES128-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4 enableECDHE 1 renegProtection 1 sslSessionCache 1 enableSpdy 15 enableStapling 1 ocspRespMaxAge 86400 } """ ssl_dir = self.setupPath + '/panel/vhost/openlitespeed/detail/ssl/' if not os.path.exists(ssl_dir): os.makedirs(ssl_dir) ssl_file = ssl_dir + '{}.conf'.format(siteName) if not os.path.exists(ssl_file): ssl_conf = ssl_conf.replace('YP_SSL_DOMAIN', siteName) public.writeFile(ssl_file, ssl_conf, "a+") include_ssl = '\ninclude {}'.format(ssl_file) detail_file = self.setupPath + '/panel/vhost/openlitespeed/detail/{}.conf'.format(siteName) public.writeFile(detail_file, include_ssl, 'a+') if not conf: conf = """ listener SSL443 { map YP_OLS_NAME YP_SSL_DOMAIN address *:443 secure 1 keyFile /www/server/panel/vhost/cert/YP_OLS_NAME/privkey.pem certFile /www/server/panel/vhost/cert/YP_OLS_NAME/fullchain.pem certChain 1 sslProtocol 24 ciphers EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:ECDHE-RSA-AES128-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA128:DHE-RSA-AES128-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA128:ECDHE-RSA-AES128-SHA384:ECDHE-RSA-AES128-SHA128:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA128:DHE-RSA-AES128-SHA128:DHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA384:AES128-GCM-SHA128:AES128-SHA128:AES128-SHA128:AES128-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4 enableECDHE 1 renegProtection 1 sslSessionCache 1 enableSpdy 15 enableStapling 1 ocspRespMaxAge 86400 } """ else: rep = r'listener\s*SSL443\s*{' map = '\n map {s} {s}'.format(s=siteName) conf = re.sub(rep, 'listener SSL443 {' + map, conf) domain = ",".join(self._get_site_domains(siteName)) conf = conf.replace('YP_OLS_NAME', siteName).replace('YP_SSL_DOMAIN', domain) public.writeFile(listen_conf, conf) def _get_ap_static_security(self, ap_conf): if not ap_conf: return '' ap_static_security = re.search('#SECURITY-START(.|\n)*#SECURITY-END', ap_conf) if ap_static_security: return ap_static_security.group() return '' def write_json_conf(self, siteName,status): conf_path = "{path}/{site_name}/{site_name}.json".format( path=self._proxy_config_path, site_name=siteName ) try: proxy_json_conf = json.loads(public.readFile(conf_path)) proxy_json_conf['ssl_info']['ssl_status']=status #将proxy_json_conf 写入文件 public.WriteFile except Exception as e: proxy_json_conf = {} return public.return_message(0,0,proxy_json_conf) # 添加SSL配置 def SetSSLConf(self, get): """ @name 兼容批量设置 @auther hezhihong """ siteName = get.siteName if not 'first_domain' in get: get.first_domain = siteName if 'isBatch' in get and siteName !=get.first_domain:get.first_domain=siteName # Nginx配置 file = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf' # Node项目 if not os.path.exists(file): file = self.setupPath + '/panel/vhost/nginx/node_' + siteName + '.conf' ng_file = file conf = public.readFile(file) # 是否为子目录设置SSL # if hasattr(get,'binding'): # allconf = conf; # conf = re.search("#BINDING-"+get.binding+"-START(.|\n)*#BINDING-"+get.binding+"-END",conf).group() if conf: if conf.find('ssl_certificate') == -1: sslStr = """#error_page 404/404.html; ssl_certificate /www/server/panel/vhost/cert/%s/fullchain.pem; ssl_certificate_key /www/server/panel/vhost/cert/%s/privkey.pem; ssl_protocols TLSv1.1 TLSv1.2%s; ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; add_header Strict-Transport-Security "max-age=31536000"; error_page 497 https://$host$request_uri; """ % (get.first_domain, get.first_domain,self.get_tls13()) if (conf.find('ssl_certificate') != -1): if 'isBatch' not in get: public.serviceReload() return public.return_msg_gettext(True, public.lang("SSL turned on!")) else: return True conf = conf.replace('#error_page 404/404.html;', sslStr) conf = re.sub(r"\s+\#SSL\-END","\n\t\t#SSL-END",conf) # 添加端口 rep = r"listen.*[\s:]+(\d+).*;" tmp = re.findall(rep, conf) if not public.inArray(tmp, '443'): listen_re = re.search(rep,conf) if not listen_re: conf = re.sub(r"server\s*{\s*","server\n{\n\t\tlisten 80;\n\t\t",conf) listen_re = re.search(rep,conf) listen = listen_re.group() versionStr = public.readFile('/www/server/nginx/version.pl') http2 = '' if versionStr: if versionStr.find('1.8.1') == -1 and versionStr.find('1.25') == -1 and versionStr.find('1.26') == -1: http2 = ' http2' default_site = '' if conf.find('default_server') != -1: default_site = ' default_server' listen_ipv6 = ';' if self.is_ipv6: listen_ipv6 = ";\n\t\tlisten [::]:443 ssl"+http2+default_site+";" conf = conf.replace(listen,listen + "\n\t\tlisten 443 ssl"+http2 + default_site + listen_ipv6) shutil.copyfile(file, self.nginx_conf_bak) public.writeFile(file, conf) # Apache配置 file = self.setupPath + '/panel/vhost/apache/' + siteName + '.conf' # if not os.path.exists(file): file = self.setupPath + '/panel/vhost/apache/node_' + siteName + '.conf' is_node_apache = False if not os.path.exists(file): is_node_apache = True file = self.setupPath + '/panel/vhost/apache/node_' + siteName + '.conf' conf = public.readFile(file) ap_static_security = self._get_ap_static_security(conf) if conf: ap_proxy = self.get_apache_proxy(conf) if conf.find('SSLCertificateFile') == -1 and conf.find('VirtualHost') != -1: find = public.M('sites').where("name=?", (siteName,)).field('id,path').find() tmp = public.M('domain').where('pid=?', (find['id'],)).field('name').select() domains = '' for key in tmp: domains += key['name'] + ' ' path = (find['path'] + '/' + self.GetRunPath(get)).replace('//', '/') index = 'index.php index.html index.htm default.php default.html default.htm' try: httpdVersion = public.readFile(self.setupPath + '/apache/version.pl').strip() except: httpdVersion = "" if httpdVersion == '2.2': vName = "" phpConfig = "" apaOpt = "Order allow,deny\n\t\tAllow from all" else: vName = "" # rep = r"php-cgi-([0-9]{2,3})\.sock" # version = re.search(rep, conf).groups()[0] version = public.get_php_version_conf(conf) if len(version) < 2: if 'isBatch' not in get: return public.return_msg_gettext(False, public.lang("Failed to get PHP version!")) else: return False phpConfig = ''' #PHP SetHandler "proxy:%s" ''' % (public.get_php_proxy(version, 'apache'),) apaOpt = 'Require all granted' sslStr = r'''%s ServerAdmin webmaster@example.com DocumentRoot "%s" ServerName SSL.%s ServerAlias %s #errorDocument 404 /404.html ErrorLog "%s-error_log" CustomLog "%s-access_log" combined %s #SSL SSLEngine On SSLCertificateFile /www/server/panel/vhost/cert/%s/fullchain.pem SSLCertificateKeyFile /www/server/panel/vhost/cert/%s/privkey.pem SSLCipherSuite EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5 SSLProtocol All -SSLv2 -SSLv3 -TLSv1 SSLHonorCipherOrder On %s %s #DENY FILES Order allow,deny Deny from all #PATH SetOutputFilter DEFLATE Options FollowSymLinks AllowOverride All %s DirectoryIndex %s ''' % (vName, path, siteName, domains, public.GetConfigValue('logs_path') + '/' + siteName, public.GetConfigValue('logs_path') + '/' + siteName, ap_proxy, get.first_domain, get.first_domain, ap_static_security, phpConfig, path, apaOpt, index) conf = conf + "\n" + sslStr self.apacheAddPort('443') shutil.copyfile(file, self.apache_conf_bak) public.writeFile(file, conf) if is_node_apache: # 兼容Nodejs项目 from projectModel.nodejsModel import main m = main() project_find = m.get_project_find(siteName) m.set_apache_config(project_find) # OLS self.set_ols_ssl(get, siteName) isError = public.checkWebConfig() if (isError != True): if os.path.exists(self.nginx_conf_bak): shutil.copyfile(self.nginx_conf_bak, ng_file) if os.path.exists(self.apache_conf_bak): shutil.copyfile(self.apache_conf_bak, file) public.ExecShell("rm -f /tmp/backup_*.conf") if 'isBatch' not in get: return public.return_msg_gettext(False, public.lang("Certificate ERROR, please check!") + ':
' + isError.replace( "\n", '
') + '
') else: return False sql = public.M('firewall') import firewalls get.port = '443' get.ps = 'HTTPS' if not public.M('firewall').where('port=?', ('443',)).count(): firewalls.firewalls().AddAcceptPort(get) public.serviceReload() if 'isBatch' not in get:firewalls.firewalls().AddAcceptPort(get) if 'isBatch' not in get:public.serviceReload() self.save_cert(get) public.write_log_gettext('Site manager', 'Site [{}] turned on SSL successfully!', (siteName,)) result = public.return_msg_gettext(True, 'SSL turned on!') result['csr'] = public.readFile('/www/server/panel/vhost/cert/' + get.siteName + '/fullchain.pem') result['key'] = public.readFile('/www/server/panel/vhost/cert/' + get.siteName + '/privkey.pem') if 'isBatch' not in get:return result else:return True def save_cert(self, get): # try: import panelSSL ss = panelSSL.panelSSL() get.keyPath = '/www/server/panel/vhost/cert/' + get.siteName + '/privkey.pem' get.certPath = '/www/server/panel/vhost/cert/' + get.siteName + '/fullchain.pem' return ss.SaveCert(get) return True # except: # return False; # HttpToHttps def HttpToHttps(self, get): siteName = get.siteName #Nginx配置 file = self.setupPath + '/panel/vhost/nginx/'+siteName+'.conf' if not os.path.exists(file): file = self.setupPath + '/panel/vhost/nginx/node_'+siteName+'.conf' conf = public.readFile(file) if conf: if conf.find('ssl_certificate') == -1: return public.return_msg_gettext(False, public.lang("SSL is NOT currently enabled")) to = """#error_page 404/404.html; #HTTP_TO_HTTPS_START if ($server_port !~ 443){ rewrite ^(/.*)$ https://$host$1 permanent; } #HTTP_TO_HTTPS_END""" conf = conf.replace('#error_page 404/404.html;',to) public.writeFile(file,conf) file = self.setupPath + '/panel/vhost/apache/'+siteName+'.conf' if not os.path.exists(file): file = self.setupPath + '/panel/vhost/apache/node_'+siteName+'.conf' conf = public.readFile(file) if conf: httpTohttos = '''combined #HTTP_TO_HTTPS_START RewriteEngine on RewriteCond %{SERVER_PORT} !^443$ RewriteRule (.*) https://%{SERVER_NAME}$1 [L,R=301] #HTTP_TO_HTTPS_END''' conf = re.sub('combined', httpTohttos, conf, 1) public.writeFile(file, conf) # OLS conf_dir = '{}/panel/vhost/openlitespeed/redirect/{}/'.format(self.setupPath, siteName) if not os.path.exists(conf_dir): os.makedirs(conf_dir) file = conf_dir + 'force_https.conf' ols_force_https = ''' #HTTP_TO_HTTPS_START RewriteEngine on RewriteCond %{SERVER_PORT} !^443$ RewriteRule (.*) https://%{SERVER_NAME}$1 [L,R=301] #HTTP_TO_HTTPS_END''' public.writeFile(file, ols_force_https) public.serviceReload() return public.return_msg_gettext(True, public.lang("Setup successfully!")) # CloseToHttps def CloseToHttps(self, get): siteName = get.siteName file = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf' if not os.path.exists(file): file = self.setupPath + '/panel/vhost/nginx/node_'+siteName+'.conf' conf = public.readFile(file) if conf: rep = "\n\\s*#HTTP_TO_HTTPS_START(.|\n){1,300}#HTTP_TO_HTTPS_END" conf = re.sub(rep, '', conf) rep = "\\s+if.+server_port.+\n.+\n\\s+\\s*}" conf = re.sub(rep, '', conf) public.writeFile(file, conf) file = self.setupPath + '/panel/vhost/apache/' + siteName + '.conf' conf = public.readFile(file) if conf: rep = "\n\\s*#HTTP_TO_HTTPS_START(.|\n){1,300}#HTTP_TO_HTTPS_END" conf = re.sub(rep, '', conf) public.writeFile(file, conf) # OLS file = '{}/panel/vhost/openlitespeed/redirect/{}/force_https.conf'.format(self.setupPath, siteName) public.ExecShell('rm -f {}*'.format(file)) public.serviceReload() return public.return_msg_gettext(True, public.lang("Setup successfully!")) # 是否跳转到https def IsToHttps(self, siteName): file = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf' if not os.path.exists(file): file = self.setupPath + '/panel/vhost/nginx/node_'+siteName+'.conf' if not os.path.exists(file): return False conf = public.readFile(file) if conf: if conf.find('HTTP_TO_HTTPS_START') != -1: return True if conf.find('$server_port !~ 443') != -1: return True return False # 清理SSL配置 def CloseSSLConf(self, get): siteName = get.siteName file = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf' if not os.path.exists(file): file = self.setupPath + '/panel/vhost/nginx/node_' + siteName + '.conf' conf = public.readFile(file) if conf: rep = "\n\\s*#HTTP_TO_HTTPS_START(.|\n){1,300}#HTTP_TO_HTTPS_END" conf = re.sub(rep, '', conf) rep = r"\s+ssl_certificate\s+.+;\s+ssl_certificate_key\s+.+;" conf = re.sub(rep, '', conf) rep = "\\s+ssl_protocols\\s+.+;\n" conf = re.sub(rep, '', conf) rep = "\\s+ssl_ciphers\\s+.+;\n" conf = re.sub(rep, '', conf) rep = "\\s+ssl_prefer_server_ciphers\\s+.+;\n" conf = re.sub(rep, '', conf) rep = "\\s+ssl_session_cache\\s+.+;\n" conf = re.sub(rep, '', conf) rep = "\\s+ssl_session_timeout\\s+.+;\n" conf = re.sub(rep, '', conf) rep = "\\s+ssl_ecdh_curve\\s+.+;\n" conf = re.sub(rep, '', conf) rep = "\\s+ssl_session_tickets\\s+.+;\n" conf = re.sub(rep, '', conf) rep = "\\s+ssl_stapling\\s+.+;\n" conf = re.sub(rep, '', conf) rep = "\\s+ssl_stapling_verify\\s+.+;\n" conf = re.sub(rep, '', conf) rep = "\\s+add_header\\s+.+;\n" conf = re.sub(rep, '', conf) rep = "\\s+add_header\\s+.+;\n" conf = re.sub(rep, '', conf) rep = r"\s+ssl\s+on;" conf = re.sub(rep, '', conf) rep = r"\s+error_page\s497.+;" conf = re.sub(rep, '', conf) rep = "\\s+if.+server_port.+\n.+\n\\s+\\s*}" conf = re.sub(rep, '', conf) rep = r"\s+listen\s+443.*;" conf = re.sub(rep, '', conf) rep = r"\s+listen\s+\[::\]:443.*;" conf = re.sub(rep, '', conf) public.writeFile(file, conf) file = self.setupPath + '/panel/vhost/apache/' + siteName + '.conf' if not os.path.exists(file): file = self.setupPath + '/panel/vhost/apache/node_' + siteName + '.conf' conf = public.readFile(file) if conf: rep = "\n(.|\n)*<\\/VirtualHost>" conf = re.sub(rep, '', conf) rep = "\n\\s*#HTTP_TO_HTTPS_START(.|\n){1,250}#HTTP_TO_HTTPS_END" conf = re.sub(rep, '', conf) rep = "NameVirtualHost *:443\n" conf = conf.replace(rep, '') public.writeFile(file, conf) # OLS ssl_file = self.setupPath + '/panel/vhost/openlitespeed/detail/ssl/{}.conf'.format(siteName) detail_file = self.setupPath + '/panel/vhost/openlitespeed/detail/' + siteName + '.conf' force_https = self.setupPath + '/panel/vhost/openlitespeed/redirect/' + siteName string = 'rm -f {}/force_https.conf*'.format(force_https) public.ExecShell(string) detail_conf = public.readFile(detail_file) if detail_conf: detail_conf = detail_conf.replace('\ninclude ' + ssl_file, '') public.writeFile(detail_file, detail_conf) public.ExecShell('rm -f {}*'.format(ssl_file)) self._del_ols_443_domain(siteName) partnerOrderId = '/www/server/panel/vhost/cert/' + siteName + '/partnerOrderId' if os.path.exists(partnerOrderId): public.ExecShell('rm -f ' + partnerOrderId) p_file = '/etc/letsencrypt/live/' + siteName + '/partnerOrderId' if os.path.exists(p_file): public.ExecShell('rm -f ' + p_file) public.write_log_gettext('Site manager', 'Site [{}] turned off SSL successfully!', (siteName,)) public.serviceReload() return public.return_msg_gettext(True, public.lang("SSL turned off!")) def _del_ols_443_domain(self, sitename): file = "/www/server/panel/vhost/openlitespeed/listen/443.conf" conf = public.readFile(file) if conf: rep = '\n\\s*map\\s*{}.*'.format(sitename) conf = re.sub(rep, '', conf) if not "map " in conf: public.ExecShell('rm -f {}*'.format(file)) return public.writeFile(file, conf) # 取SSL状态 def GetSSL(self, get): siteName = get.siteName path = os.path.join('/www/server/panel/vhost/cert/', siteName) if not os.path.isfile(os.path.join(path, "fullchain.pem")) and not os.path.isfile( os.path.join(path, "privkey.pem")): path = os.path.join('/etc/letsencrypt/live/', siteName) type = 0 if os.path.exists(path + '/README'): type = 1 if os.path.exists(path + '/partnerOrderId'): type = 2 if os.path.exists(path + '/certOrderId'): type = 3 csrpath = path + "/fullchain.pem" # 生成证书路径 keypath = path + "/privkey.pem" # 密钥文件路径 key = public.readFile(keypath) csr = public.readFile(csrpath) file = self.setupPath + '/panel/vhost/' + public.get_webserver() + '/' + siteName + '.conf' # 是否为node项目 if not os.path.exists(file): file = self.setupPath + '/panel/vhost/' + public.get_webserver() + '/node_' + siteName + '.conf' if not os.path.exists(file): file = self.setupPath + '/panel/vhost/' + public.get_webserver() + '/java_' + siteName + '.conf' if not os.path.exists(file): file = self.setupPath + '/panel/vhost/' + public.get_webserver() + '/go_' + siteName + '.conf' if not os.path.exists(file): file = self.setupPath + '/panel/vhost/' + public.get_webserver() + '/other_' + siteName + '.conf' if not os.path.exists(file): file = self.setupPath + '/panel/vhost/' + public.get_webserver() + '/python_' + siteName + '.conf' if not os.path.exists(file): file = self.setupPath + '/panel/vhost/' + public.get_webserver() + '/net_' + siteName + '.conf' if not os.path.exists( file): file = self.setupPath + '/panel/vhost/' + public.get_webserver() + '/html_' + siteName + '.conf' if public.get_webserver() == "openlitespeed": file = self.setupPath + '/panel/vhost/' + public.get_webserver() + '/detail/' + siteName + '.conf' conf = public.readFile(file) if not conf: return public.return_msg_gettext(False, public.lang("The specified website profile does not exist")) if public.get_webserver() == 'nginx': keyText = 'ssl_certificate' elif public.get_webserver() == 'apache': keyText = 'SSLCertificateFile' else: keyText = 'openlitespeed/detail/ssl' status = True if (conf.find(keyText) == -1): status = False type = -1 toHttps = self.IsToHttps(siteName) id = public.M('sites').where("name=?", (siteName,)).getField('id') domains = public.M('domain').where("pid=?", (id,)).field('name').select() cert_data = {} if csr: get.certPath = csrpath import panelSSL cert_data = panelSSL.panelSSL().GetCertName(get) if not cert_data: cert_data = { 'certificate':0 } if os.path.isfile(csrpath) and os.path.isfile(keypath): if key and csr: cert_hash = SSLManger().ssl_hash(certificate=csr, ignore_errors=True) if cert_hash is None: cert_data["id"], cert_data["ps"] = 0, '' else: cert_data["id"], cert_data["ps"] = SSLManger().get_cert_info_by_hash(cert_hash) # 调用save_by_file方法保存证书信息 if cert_data["id"] == -1: try: save_result = SSLManger().save_by_file(csrpath, keypath) cert_data["id"], cert_data["ps"] = SSLManger().get_cert_info_by_hash(cert_hash) except: cert_data["id"], cert_data["ps"] = 0, '' email = public.M('users').where('id=?', (1,)).getField('email') if email == '287962566@qq.com': email = '' index = '' auth_type = 'http' if status == True: if type != 1: import acme_v2 acme = acme_v2.acme_v2() index = acme.check_order_exists(csrpath) if index: if index.find('/') == -1: auth_type = acme._config['orders'][index]['auth_type'] type = 1 else: crontab_file = 'vhost/cert/crontab.json' tmp = public.readFile(crontab_file) if tmp: crontab_config = json.loads(tmp) if siteName in crontab_config: if 'dnsapi' in crontab_config[siteName]: auth_type = 'dns' if os.path.exists(path + '/certOrderId'): type = 3 oid = -1 if type == 3: oid = int(public.readFile(path + '/certOrderId')) return { 'status': status, 'oid': oid, 'domain': domains, 'key': key, 'csr': csr, 'type': type, 'httpTohttps': toHttps, 'cert_data': cert_data, 'email': email, "index": index, 'auth_type': auth_type, 'tls_versions': self.get_ssl_protocol(get), 'push': self.get_ssl_push_status(None, siteName, 'ssl', status) } def get_ssl_push_status(self, get, siteName=None, stype=None, ssl_status=None): if get: siteName = get.siteName result = {'status': False} selected_data = { 'task_data': {}, 'title': "", 'sender': "", 'status': bool(0), 'id': "" } task = {} try: try: data = json.loads(public.readFile('{}/data/mod_push_data/task.json'.format(public.get_panel_path()))) except: return result for i in data: if i['source'] == 'site_ssl': task_data = i.get('task_data', {}) project = task_data.get('project') if project == siteName: task = i break if project == "all": task = i except Exception as e: return result if task.get('id'): selected_data = { 'task_data': task.get('task_data', {}), 'title': task.get('title', ''), 'sender': task.get('sender', []), 'status': task.get('status'), 'id': task.get('id', "") } return selected_data def get_site_push_status(self, get, siteName=None, stype=None): """ @获取网站ssl告警通知状态 @param get: @param siteName 网站名称 @param stype 类型 ssl """ import panelPush if get: siteName = get.siteName stype = get.stype result = {} result['status'] = False try: data = {} try: data = json.loads(public.readFile('{}/class/push/push.json'.format(public.get_panel_path()))) except: pass if not 'site_push' in data: return result ssl_data = data['site_push'] for key in ssl_data.keys(): if ssl_data[key]['type'] != stype: continue project = ssl_data[key]['project'] if project in [siteName, 'all']: ssl_data[key]['id'] = key ssl_data[key]['s_module'] = 'site_push' if project == siteName: result = ssl_data[key] break if project == 'all': result = ssl_data[key] except: pass p_obj = panelPush.panelPush() return p_obj.get_push_user(result) def set_site_status_multiple(self,get): ''' @name 批量设置网站状态 @author zhwen<2020-11-17> @param sites_id "1,2" @param status 0/1 ''' sites_id = get.sites_id.split(',') sites_name = [] errors = {} day_time = time.time() for site_id in sites_id: get.id = site_id find = public.M('sites').where("id=?", (site_id,)).find() get.name = find['name'] if get.status == '1': if find['edate'] != '0000-00-00' and public.to_date("%Y-%m-%d",find['edate']) < day_time: errors[get.name] = "failed, site has expired" continue sites_name.append(get.name) if get.status == '1': self.SiteStart(get, multiple=1) else: self.SiteStop(get, multiple=1) public.serviceReload() if get.status == '1': return {'status': True, 'msg': public.get_msg_gettext('Enable website [{}] successfully', (','.join(sites_name),)), 'error': {}, 'success': sites_name} else: return {'status': True, 'msg': public.get_msg_gettext('Disable website [{}] successfully', (','.join(sites_name),)), 'error': {}, 'success': sites_name} # 启动站点 def SiteStart(self, get, multiple=None): id = get.id Path = self.setupPath + '/stop' sitePath = public.M('sites').where("id=?", (id,)).getField('path') # nginx file = self.setupPath + '/panel/vhost/nginx/' + get.name + '.conf' conf = public.readFile(file) if conf: conf = conf.replace(Path, sitePath) conf = conf.replace("#include", "include") public.writeFile(file, conf) # apache file = self.setupPath + '/panel/vhost/apache/' + get.name + '.conf' conf = public.readFile(file) if conf: conf = conf.replace(Path, sitePath) conf = conf.replace("#IncludeOptional", "IncludeOptional") public.writeFile(file, conf) # OLS file = self.setupPath + '/panel/vhost/openlitespeed/' + get.name + '.conf' conf = public.readFile(file) if conf: rep = r'vhRoot\s*{}'.format(Path) new_content = 'vhRoot {}'.format(sitePath) conf = re.sub(rep, new_content, conf) public.writeFile(file, conf) public.M('sites').where("id=?", (id,)).setField('status', '1') if not multiple: public.serviceReload() public.write_log_gettext('Site manager', 'Site [{}] started!', (get.name,)) return public.return_msg_gettext(True, public.lang("Site started")) def _process_has_run_dir(self, website_name, website_path, stop_path): ''' @name 当网站存在允许目录时停止网站需要做处理 @author zhwen<2020-11-17> @param site_id 1 @param names test,baohu ''' conf = public.readFile(self.setupPath + '/panel/vhost/nginx/' + website_name + '.conf') if not conf: return False try: really_path = re.search(r'root\s+(.*);', conf).group(1) tmp = stop_path + '/' + really_path.replace(website_path + '/', '') public.ExecShell('mkdir {t} && ln -s {s}/index.html {t}/index.html'.format(t=tmp, s=stop_path)) except: pass # 停止站点 def SiteStop(self, get, multiple=None): path = self.setupPath + '/stop' id = get.id site_status = public.M('sites').where("id=?", (id,)).getField('status') if str(site_status) != '1': return public.returnMsg(True, public.lang("Site stopped")) if not os.path.exists(path): os.makedirs(path) public.downloadFile('https://node.yakpanel.com/stop_en.html', path + '/index.html') # if 'This site has been closed by administrator' not in public.readFile(path + '/index.html'): # public.downloadFile('https://www.yakpanel.com/stop_en.html', path + '/index.html') binding = public.M('binding').where('pid=?', (id,)).field('id,pid,domain,path,port,addtime').select() for b in binding: bpath = path + '/' + b['path'] if not os.path.exists(bpath): public.ExecShell('mkdir -p ' + bpath) public.ExecShell('ln -sf ' + path + '/index.html ' + bpath + '/index.html') sitePath = public.M('sites').where("id=?", (id,)).getField('path') self._process_has_run_dir(get.name, sitePath, path) # nginx file = self.setupPath + '/panel/vhost/nginx/' + get.name + '.conf' conf = public.readFile(file) if conf: src_path = 'root ' + sitePath dst_path = 'root ' + path if conf.find(src_path) != -1: conf = conf.replace(src_path, dst_path) else: conf = conf.replace(sitePath, path) conf = conf.replace("include", "#include") public.writeFile(file, conf) # apache file = self.setupPath + '/panel/vhost/apache/' + get.name + '.conf' conf = public.readFile(file) if conf: conf = conf.replace(sitePath, path) conf = conf.replace("IncludeOptional", "#IncludeOptional") public.writeFile(file, conf) # OLS file = self.setupPath + '/panel/vhost/openlitespeed/' + get.name + '.conf' conf = public.readFile(file) if conf: rep = r'vhRoot\s*{}'.format(sitePath) new_content = 'vhRoot {}'.format(path) conf = re.sub(rep, new_content, conf) public.writeFile(file, conf) public.M('sites').where("id=?", (id,)).setField('status', '0') if not multiple: public.serviceReload() public.write_log_gettext('Site manager', 'Site [{}] stopped!', (get.name,)) return public.return_msg_gettext(True, public.lang("Site stopped")) # 取流量限制值 def GetLimitNet(self, get): id = get.id # 取回配置文件 siteName = public.M('sites').where("id=?", (id,)).getField('name') filename = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf' # 站点总并发 data = {} conf = public.readFile(filename) try: rep = r"\s+limit_conn\s+perserver\s+([0-9]+);" tmp = re.search(rep, conf).groups() data['perserver'] = int(tmp[0]) # IP并发限制 rep = r"\s+limit_conn\s+perip\s+([0-9]+);" tmp = re.search(rep, conf).groups() data['perip'] = int(tmp[0]) # 请求并发限制 rep = r"\s+limit_rate\s+([0-9]+)\w+;" tmp = re.search(rep, conf).groups() data['limit_rate'] = int(tmp[0]) except: data['perserver'] = 0 data['perip'] = 0 data['limit_rate'] = 0 return data # 设置流量限制 def SetLimitNet(self, get): if (public.get_webserver() != 'nginx'): return public.return_msg_gettext(False, public.lang("Site Traffic Control only supports Nginx Web Server!")) id = get.id if int(get.perserver) < 1 or int(get.perip) < 1 or int(get.perip) < 1: return public.return_msg_gettext(False, public.lang("Concurrency restrictions, IP restrictions, traffic restrictions must be greater than 0")) perserver = 'limit_conn perserver ' + get.perserver + ';' perip = 'limit_conn perip ' + get.perip + ';' limit_rate = 'limit_rate ' + get.limit_rate + 'k;' # 取回配置文件 siteName = public.M('sites').where("id=?", (id,)).getField('name') filename = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf' conf = public.readFile(filename) # 设置共享内存 oldLimit = self.setupPath + '/panel/vhost/nginx/limit.conf' if (os.path.exists(oldLimit)): os.remove(oldLimit) limit = self.setupPath + '/nginx/conf/nginx.conf' nginxConf = public.readFile(limit) limitConf = "limit_conn_zone $binary_remote_addr zone=perip:10m;\n\t\tlimit_conn_zone $server_name zone=perserver:10m;" nginxConf = nginxConf.replace("#limit_conn_zone $binary_remote_addr zone=perip:10m;", limitConf) public.writeFile(limit, nginxConf) if (conf.find('limit_conn perserver') != -1): # 替换总并发 rep = r"limit_conn\s+perserver\s+([0-9]+);" conf = re.sub(rep, perserver, conf) # 替换IP并发限制 rep = r"limit_conn\s+perip\s+([0-9]+);" conf = re.sub(rep, perip, conf) # 替换请求流量限制 rep = r"limit_rate\s+([0-9]+)\w+;" conf = re.sub(rep, limit_rate, conf) else: conf = conf.replace('#error_page 404/404.html;', "#error_page 404/404.html;\n " + perserver + "\n " + perip + "\n " + limit_rate) import shutil shutil.copyfile(filename, self.nginx_conf_bak) public.writeFile(filename, conf) isError = public.checkWebConfig() if (isError != True): if os.path.exists(self.nginx_conf_bak): shutil.copyfile(self.nginx_conf_bak, filename) return public.return_msg_gettext(False, public.lang('ERROR:
' + isError.replace("\n", '
') + '
')) public.serviceReload() public.write_log_gettext('Site manager', 'Site [{}] traffic control turned on!', (siteName,)) return public.return_msg_gettext(True, public.lang("Setup successfully!")) # 关闭流量限制 def CloseLimitNet(self, get): id = get.id # 取回配置文件 siteName = public.M('sites').where("id=?", (id,)).getField('name') filename = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf' conf = public.readFile(filename) # 清理总并发 rep = r"\s+limit_conn\s+perserver\s+([0-9]+);" conf = re.sub(rep, '', conf) # 清理IP并发限制 rep = r"\s+limit_conn\s+perip\s+([0-9]+);" conf = re.sub(rep, '', conf) # 清理请求流量限制 rep = r"\s+limit_rate\s+([0-9]+)\w+;" conf = re.sub(rep, '', conf) public.writeFile(filename, conf) public.serviceReload() public.write_log_gettext('Site manager', 'Site Traffic Control has been turned off!', (siteName,)) return public.return_msg_gettext(True, public.lang("Site Traffic Control has been turned off!")) # 取301配置状态 def Get301Status(self, get): siteName = get.siteName result = {} domains = '' id = public.M('sites').where("name=?", (siteName,)).getField('id') tmp = public.M('domain').where("pid=?", (id,)).field('name').select() node = public.M('sites').where('id=? and project_type=?', (id, 'Node')).count() if node: node = 'node_' else: node = '' for key in tmp: domains += key['name'] + ',' try: if (public.get_webserver() == 'nginx'): conf = public.readFile(self.setupPath + '/panel/vhost/nginx/' + node + siteName + '.conf') if conf.find('301-START') == -1: result['domain'] = domains[:-1] result['src'] = "" result['status'] = False result['url'] = "http://" return result rep = r"return\s+301\s+((http|https)\://.+);" arr = re.search(rep, conf).groups()[0] rep = r"'\^(([\w-]+\.)+[\w-]+)'" tmp = re.search(rep, conf) src = '' if tmp: src = tmp.groups()[0] elif public.get_webserver() == 'apache': conf = public.readFile(self.setupPath + '/panel/vhost/apache/' + node + siteName + '.conf') if conf.find('301-START') == -1: result['domain'] = domains[:-1] result['src'] = "" result['status'] = False result['url'] = "http://" return result rep = r"RewriteRule\s+.+\s+((http|https)\://.+)\s+\[" arr = re.search(rep, conf).groups()[0] rep = r"\^((\w+\.)+\w+)\s+\[NC" tmp = re.search(rep, conf) src = '' if tmp: src = tmp.groups()[0] else: conf = public.readFile( self.setupPath + '/panel/vhost/openlitespeed/redirect/{s}/{s}.conf'.format(s=siteName)) if not conf: result['domain'] = domains[:-1] result['src'] = "" result['status'] = False result['url'] = "http://" return result rep = r"RewriteRule\s+.+\s+((http|https)\://.+)\s+\[" arr = re.search(rep, conf).groups()[0] rep = r"\^((\w+\.)+\w+)\s+\[NC" tmp = re.search(rep, conf) src = '' if tmp: src = tmp.groups()[0] except: src = '' arr = 'http://' result['domain'] = domains[:-1] result['src'] = src.replace("'", '') result['status'] = True if (len(arr) < 3): result['status'] = False result['url'] = arr return result # 设置301配置 def Set301Status(self, get): siteName = get.siteName srcDomain = get.srcDomain toDomain = get.toDomain type = get.type rep = r"(http|https)\://.+" if not re.match(rep, toDomain): return public.return_msg_gettext(False, public.lang("URL address is invalid!")) # nginx filename = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf' mconf = public.readFile(filename) if mconf == False: return public.return_msg_gettext(False, public.lang("Configuration file not exist")) if (srcDomain == 'all'): conf301 = "\t#301-START\n\t\treturn 301 " + toDomain + "$request_uri;\n\t#301-END" else: conf301 = "\t#301-START\n\t\tif ($host ~ '^" + srcDomain + "'){\n\t\t\treturn 301 " + toDomain + "$request_uri;\n\t\t}\n\t#301-END" if type == '1': mconf = mconf.replace("#error_page 404/404.html;", "#error_page 404/404.html;\n" + conf301) else: rep = "\\s+#301-START(.|\n){1,300}#301-END" mconf = re.sub(rep, '', mconf) public.writeFile(filename, mconf) # apache filename = self.setupPath + '/panel/vhost/apache/' + siteName + '.conf' mconf = public.readFile(filename) if mconf == False: return public.return_msg_gettext(False, public.lang("Configuration file not exist")) if type == '1': if (srcDomain == 'all'): conf301 = "\n\t#301-START\n\t\n\t\tRewriteEngine on\n\t\tRewriteRule ^(.*)$ " + toDomain + "$1 [L,R=301]\n\t\n\t#301-END\n" else: conf301 = "\n\t#301-START\n\t\n\t\tRewriteEngine on\n\t\tRewriteCond %{HTTP_HOST} ^" + srcDomain + " [NC]\n\t\tRewriteRule ^(.*) " + toDomain + "$1 [L,R=301]\n\t\n\t#301-END\n" rep = "combined" mconf = mconf.replace(rep, rep + "\n\t" + conf301) else: rep = "\n\\s+#301-START(.|\n){1,300}#301-END\n*" mconf = re.sub(rep, '\n\n', mconf, 1) mconf = re.sub(rep, '\n\n', mconf, 1) public.writeFile(filename, mconf) # OLS conf_dir = self.setupPath + '/panel/vhost/openlitespeed/redirect/{}/'.format(siteName) if not os.path.exists(conf_dir): os.makedirs(conf_dir) file = conf_dir + siteName + '.conf' if type == '1': if (srcDomain == 'all'): conf301 = "#301-START\nRewriteEngine on\nRewriteRule ^(.*)$ " + toDomain + "$1 [L,R=301]#301-END\n" else: conf301 = "#301-START\nRewriteEngine on\nRewriteCond %{HTTP_HOST} ^" + srcDomain + " [NC]\nRewriteRule ^(.*) " + toDomain + "$1 [L,R=301]\n#301-END\n" public.writeFile(file, conf301) else: public.ExecShell('rm -f {}*'.format(file)) isError = public.checkWebConfig() if (isError != True): return public.return_msg_gettext(False, public.lang('ERROR:
' + isError.replace("\n", '
') + '
')) public.serviceReload() return public.return_msg_gettext(True, public.lang("Setup successfully!")) # 取子目录绑定 def GetDirBinding(self, get): path = public.M('sites').where('id=?', (get.id,)).getField('path') if not os.path.exists(path): checks = ['/', '/usr', '/etc'] if path in checks: data = {} data['dirs'] = [] data['binding'] = [] return data public.ExecShell('mkdir -p ' + path) public.ExecShell('chmod 755 ' + path) public.ExecShell('chown www:www ' + path) get.path = path self.SetDirUserINI(get) siteName = public.M('sites').where('id=?', (get.id,)).getField('name') public.write_log_gettext('Site manager', "Site [{}], document root [{}] does NOT exist, recreated!", (siteName, path)) dirnames = [] # 取运行目录 run_path = self.GetRunPath(get) if run_path: path += run_path # 遍历目录 if os.path.exists(path): for filename in os.listdir(path): try: json.dumps(filename) if sys.version_info[0] == 2: filename = filename.encode('utf-8') else: filename.encode('utf-8') filePath = path + '/' + filename if os.path.islink(filePath): continue if os.path.isdir(filePath): dirnames.append(filename) except: pass data = {} data['run_path'] = run_path # 运行目录 data['dirs'] = dirnames data['binding'] = public.M('binding').where('pid=?',(get.id,)).field('id,pid,domain,path,port,addtime').select() # 标记子目录是否存在 for dname in data['binding']: _path = os.path.join(path,dname['path']) if not os.path.exists(_path): _path = _path.replace(run_path,'') if not os.path.exists(_path): dname['path'] += ' >> error: directory does not exist' else: dname['path'] = '../' + dname['path'] return data # 添加子目录绑定 def AddDirBinding(self, get): import shutil id = get.id tmp = get.domain.split(':') domain = tmp[0].lower() # 中文域名转码 domain = public.en_punycode(domain) port = '80' version = '' if len(tmp) > 1: port = tmp[1] if not hasattr(get, 'dirName'): public.return_msg_gettext(False, 'Directory cannot be empty!') dirName = get.dirName reg = r"^([\w\-\*]{1,100}\.){1,4}([\w\-]{1,100}|[\w\-]{1,100}\.[\w\-]{1,100})$" if not re.match(reg, domain): return public.return_msg_gettext(False, public.lang("Format of primary domain is incorrect")) siteInfo = public.M('sites').where("id=?",(id,)).field('id,path,name').find() # 实际运行目录 root_path = siteInfo['path'] run_path = self.GetRunPath(get) if run_path: root_path += run_path webdir = root_path + '/' + dirName webdir = webdir.replace('//','/').strip() if not os.path.exists(webdir): # 如果在运行目录找不到指定子目录,尝试到根目录查找 root_path = siteInfo['path'] webdir = root_path + '/' + dirName webdir = webdir.replace('//','/').strip() sql = public.M('binding') if sql.where("domain=?", (domain,)).count() > 0: return public.return_msg_gettext(False, public.lang("The domain you tried to add already exists!")) if public.M('domain').where("name=?", (domain,)).count() > 0: return public.return_msg_gettext(False, public.lang("The domain you tried to add already exists!")) filename = self.setupPath + '/panel/vhost/nginx/' + siteInfo['name'] + '.conf' nginx_conf_file = filename conf = public.readFile(filename) if conf: listen_ipv6 = '' if self.is_ipv6: listen_ipv6 = "\n listen [::]:%s;" % port try: rep = r"enable-php-(\w{2,5})\.conf" tmp = re.search(rep,conf) if not tmp: rep = r"enable-php-(\d+-wpfastcgi).conf" tmp = re.search(rep, conf) except: return public.returnMsg(False, public.lang("Get enable php config failed!")) tmp = tmp.groups() version = tmp[0] bindingConf = r''' #BINDING-%s-START server { listen %s;%s server_name %s; index index.php index.html index.htm default.php default.htm default.html; root %s; include enable-php-%s.conf; include %s/panel/vhost/rewrite/%s.conf; %s location ~ ^/(\.user.ini|\.htaccess|\.git|\.env|\.svn|\.project|LICENSE|README.md) { return 404; } %s location ~ \.well-known{ allow all; } location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$ { expires 30d; error_log /dev/null; access_log /dev/null; } location ~ .*\\.(js|css)?$ { expires 12h; error_log /dev/null; access_log /dev/null; } access_log %s.log; error_log %s.error.log; } #BINDING-%s-END''' % (domain, port, listen_ipv6, domain, webdir, version, self.setupPath, siteInfo['name'], ("# Forbidden files or directories"), ("# Directory verification related settings for one-click application for SSL certificate"), public.GetConfigValue('logs_path') + '/' + siteInfo['name'], public.GetConfigValue('logs_path') + '/' + siteInfo['name'], domain) conf += bindingConf shutil.copyfile(filename, self.nginx_conf_bak) public.writeFile(filename, conf) filename = self.setupPath + '/panel/vhost/apache/' + siteInfo['name'] + '.conf' conf = public.readFile(filename) if conf: try: try: httpdVersion = public.readFile(self.setupPath + '/apache/version.pl').strip() except: httpdVersion = "" if httpdVersion == '2.2': phpConfig = "" apaOpt = "Order allow,deny\n\t\tAllow from all" else: # rep = r"php-cgi-([0-9]{2,3})\.sock" # tmp = re.search(rep,conf).groups() # version = tmp[0] version = public.get_php_version_conf(conf) phpConfig = ''' #PHP SetHandler "proxy:%s" ''' % (public.get_php_proxy(version, 'apache'),) apaOpt = 'Require all granted' bindingConf = r''' #BINDING-%s-START ServerAdmin webmaster@example.com DocumentRoot "%s" ServerAlias %s #errorDocument 404 /404.html ErrorLog "%s-error_log" CustomLog "%s-access_log" combined %s #DENY FILES Order allow,deny Deny from all #PATH SetOutputFilter DEFLATE Options FollowSymLinks AllowOverride All %s DirectoryIndex index.php index.html index.htm default.php default.html default.htm #BINDING-%s-END''' % (domain, port, webdir, domain, public.GetConfigValue('logs_path') + '/' + siteInfo['name'], public.GetConfigValue('logs_path') + '/' + siteInfo['name'], phpConfig, webdir, apaOpt, domain) conf += bindingConf shutil.copyfile(filename, self.apache_conf_bak) public.writeFile(filename, conf) except: pass get.webname = siteInfo['name'] get.port = port self.phpVersion = version self.siteName = siteInfo['name'] self.sitePath = webdir listen_file = self.setupPath + "/panel/vhost/openlitespeed/listen/80.conf" listen_conf = public.readFile(listen_file) if listen_conf: rep = r'secure\s*0' map = '\tmap {}_{} {}'.format(siteInfo['name'], dirName, domain) listen_conf = re.sub(rep, 'secure 0\n' + map, listen_conf) public.writeFile(listen_file, listen_conf) self.openlitespeed_add_site(get) # 检查配置是否有误 isError = public.checkWebConfig() if isError != True: if os.path.exists(self.nginx_conf_bak): shutil.copyfile(self.nginx_conf_bak, nginx_conf_file) if os.path.exists(self.apache_conf_bak): shutil.copyfile(self.apache_conf_bak, filename) return public.return_msg_gettext(False, public.lang('ERROR:
' + isError.replace("\n", '
') + '
')) public.M('binding').add('pid,domain,port,path,addtime', (id, domain, port, dirName, public.getDate())) public.serviceReload() public.write_log_gettext('Site manager', 'Site [{}] subdirectory [{}] bound to [{}]', (siteInfo['name'], dirName, domain)) return public.return_msg_gettext(True, public.lang("Successfully added")) def delete_dir_bind_multiple(self, get): ''' @name 批量删除网站 @author zhwen<2020-11-17> @param bind_ids 1,2,3 ''' bind_ids = get.bind_ids.split(',') del_successfully = [] del_failed = {} for bind_id in bind_ids: get.id = bind_id domain = public.M('binding').where("id=?", (get.id,)).getField('domain') if not domain: continue try: self.DelDirBinding(get, multiple=1) del_successfully.append(domain) except: del_failed[domain] = public.lang("There was an error deleting, please try again.") pass public.serviceReload() return {'status': True, 'msg': public.get_msg_gettext('Delete [{}] subdirectory binding successfully', (','.join(del_successfully),)), 'error': del_failed, 'success': del_successfully} # 删除子目录绑定 def DelDirBinding(self, get, multiple=None): id = get.id binding = public.M('binding').where("id=?", (id,)).field('id,pid,domain,path').find() siteName = public.M('sites').where("id=?", (binding['pid'],)).getField('name') # nginx filename = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf' conf = public.readFile(filename) if conf: rep = r"\s*.+BINDING-" + binding['domain'] + "-START(.|\n)+BINDING-" + binding['domain'] + "-END" conf = re.sub(rep, '', conf) public.writeFile(filename, conf) # apache filename = self.setupPath + '/panel/vhost/apache/' + siteName + '.conf' conf = public.readFile(filename) if conf: rep = r"\s*.+BINDING-" + binding['domain'] + "-START(.|\n)+BINDING-" + binding['domain'] + "-END" conf = re.sub(rep, '', conf) public.writeFile(filename, conf) # openlitespeed filename = self.setupPath + '/panel/vhost/openlitespeed/' + siteName + '.conf' conf = public.readFile(filename) rep = "#SUBDIR\\s*{s}_{d}\\s*START(\n|.)+#SUBDIR\\s*{s}_{d}\\s*END".format(s=siteName, d=binding['path']) if conf: conf = re.sub(rep, '', conf) public.writeFile(filename, conf) # 删除域名,前端需要传域名 get.webname = siteName get.domain = binding['domain'] self._del_ols_domain(get) # 清理子域名监听文件 listen_file = self.setupPath + "/panel/vhost/openlitespeed/listen/80.conf" listen_conf = public.readFile(listen_file) if listen_conf: map_reg = r'\s*map\s*{}_{}.*'.format(siteName, binding['path']) listen_conf = re.sub(map_reg, '', listen_conf) public.writeFile(listen_file, listen_conf) # 清理detail文件 detail_file = "{}/panel/vhost/openlitespeed/detail/{}_{}.conf".format(self.setupPath, siteName, binding['path']) public.ExecShell("rm -f {}*".format(detail_file)) # 从数据库删除绑定 public.M('binding').where("id=?",(id,)).delete() # 如果没有其它域名绑定同一子目录,则删除该子目录的伪静态规则 if not public.M('binding').where("path=? AND pid=?",(binding['path'],binding['pid'])).count(): filename = self.setupPath + '/panel/vhost/rewrite/' + siteName + '_' + binding['path'] + '.conf' if os.path.exists(filename): public.ExecShell('rm -rf %s'%filename) # 是否需要重载服务 if not multiple: public.serviceReload() public.write_log_gettext('Site manager', 'Deleted site [{}] subdirectory [{}] binding', (siteName, binding['path'])) return public.return_msg_gettext(True, public.lang("Successfully deleted")) # 取子目录Rewrite def GetDirRewrite(self, get): id = get.id find = public.M('binding').where("id=?", (id,)).field('id,pid,domain,path').find() site = public.M('sites').where("id=?", (find['pid'],)).field('id,name,path').find() if (public.get_webserver() != 'nginx'): filename = site['path'] + '/' + find['path'] + '/.htaccess' else: filename = self.setupPath + '/panel/vhost/rewrite/' + site['name'] + '_' + find['path'] + '.conf' if hasattr(get, 'add'): public.writeFile(filename, '') if public.get_webserver() == 'nginx': file = self.setupPath + '/panel/vhost/nginx/' + site['name'] + '.conf' conf = public.readFile(file) domain = find['domain'] rep = "\n#BINDING-" + domain + "-START(.|\n)+BINDING-" + domain + "-END" tmp = re.search(rep, conf).group() dirConf = tmp.replace('rewrite/' + site['name'] + '.conf;', 'rewrite/' + site['name'] + '_' + find['path'] + '.conf;') conf = conf.replace(tmp, dirConf) public.writeFile(file, conf) data = {} data['status'] = False if os.path.exists(filename): data['status'] = True data['data'] = public.readFile(filename) data['rlist'] = ['0.default'] webserver = public.get_webserver() if webserver == "openlitespeed": webserver = "apache" for ds in os.listdir('rewrite/' + webserver): if ds == 'list.txt': continue data['rlist'].append(ds[0:len(ds) - 5]) data['filename'] = filename return data # 取默认文档 def GetIndex(self, get): id = get.id Name = public.M('sites').where("id=?", (id,)).getField('name') file = self.setupPath + '/panel/vhost/' + public.get_webserver() + '/' + Name + '.conf' if public.get_webserver() == 'openlitespeed': file = self.setupPath + '/panel/vhost/' + public.get_webserver() + '/detail/' + Name + '.conf' conf = public.readFile(file) if conf == False: return public.return_msg_gettext(False, public.lang("Configuration file not exist")) if public.get_webserver() == 'nginx': rep = r"\s+index\s+(.+);" elif public.get_webserver() == 'apache': rep = "DirectoryIndex\\s+(.+)\n" else: rep = "indexFiles\\s+(.+)\n" if re.search(rep, conf): tmp = re.search(rep, conf).groups() if public.get_webserver() == 'openlitespeed': return tmp[0] return tmp[0].replace(' ', ',') return public.return_msg_gettext(False, public.lang("Failed to get, there is no default document in the configuration file")) # 设置默认文档 def SetIndex(self, get): id = get.id Index = get.Index.replace(' ', '') Index = Index.replace(',,', ',').strip() if not Index: return public.returnMsg(False, public.lang("Default index file cannot be empty")) if get.Index.find('.') == -1: return public.return_msg_gettext(False, public.lang("Default Document Format is invalid, e.g., index.html")) if len(Index) < 3: return public.return_msg_gettext(False, public.lang("Default Document cannot be empty!")) Name = public.M('sites').where("id=?", (id,)).getField('name') # 准备指令 Index_L = Index.replace(",", " ") # nginx file = self.setupPath + '/panel/vhost/nginx/' + Name + '.conf' conf = public.readFile(file) if conf: rep = r"\s+index\s+.+;" conf = re.sub(rep, "\n\tindex " + Index_L + ";", conf) public.writeFile(file, conf) # apache file = self.setupPath + '/panel/vhost/apache/' + Name + '.conf' conf = public.readFile(file) if conf: rep = "DirectoryIndex\\s+.+\n" conf = re.sub(rep, 'DirectoryIndex ' + Index_L + "\n", conf) public.writeFile(file, conf) # openlitespeed file = self.setupPath + '/panel/vhost/openlitespeed/detail/' + Name + '.conf' conf = public.readFile(file) if conf: rep = "indexFiles\\s+.+\n" Index = Index.split(',') Index = [i for i in Index if i] Index = ",".join(Index) conf = re.sub(rep, 'indexFiles ' + Index + "\n", conf) public.writeFile(file, conf) public.serviceReload() public.write_log_gettext('Site manager', 'Defualt document of site [{}] is [{}]', (Name, Index_L)) return public.return_msg_gettext(True, public.lang("Setup successfully!")) # 修改物理路径 def SetPath(self, get): id = get.id Path = self.GetPath(get.path) if Path == "" or id == '0': return public.return_msg_gettext(False, public.lang("Directory cannot be empty!")) if not self.__check_site_path(Path): return public.return_msg_gettext(False, public.lang("System critical directory cannot be used as site directory")) if not public.check_site_path(Path): a, c = public.get_sys_path() return public.return_msg_gettext(False, public.lang("Please do not set the website root directory to the system main directory:
{}", "
".join(a+c))) SiteFind = public.M("sites").where("id=?", (id,)).field('path,name').find() if SiteFind["path"] == Path: return public.return_msg_gettext(False, public.lang("Same as original path, no need to change!")) Name = SiteFind['name'] file = self.setupPath + '/panel/vhost/nginx/' + Name + '.conf' conf = public.readFile(file) if conf: conf = conf.replace(SiteFind['path'], Path) public.writeFile(file, conf) file = self.setupPath + '/panel/vhost/apache/' + Name + '.conf' conf = public.readFile(file) if conf: rep = "DocumentRoot\\s+.+\n" conf = re.sub(rep, 'DocumentRoot "' + Path + '"\n', conf) rep = "\n", conf) public.writeFile(file, conf) # OLS file = self.setupPath + '/panel/vhost/openlitespeed/' + Name + '.conf' conf = public.readFile(file) if conf: reg = 'vhRoot.*' conf = re.sub(reg, 'vhRoot ' + Path, conf) public.writeFile(file, conf) # 创建basedir userIni = Path + '/.user.ini' if os.path.exists(userIni): public.ExecShell("chattr -i " + userIni) public.writeFile(userIni, 'open_basedir=' + Path + '/:/tmp/') public.ExecShell('chmod 644 ' + userIni) public.ExecShell('chown root:root ' + userIni) public.ExecShell('chattr +i ' + userIni) public.set_site_open_basedir_nginx(Name) public.serviceReload() public.M("sites").where("id=?",(id,)).setField('path',Path) public.write_log_gettext('Site manager', 'Successfully changed directory of site [{}]!',(Name,)) self.CheckRunPathExists(id) return public.return_msg_gettext(True, public.lang("Successfully set")) def CheckRunPathExists(self,site_id): ''' @name 检查站点运行目录是否存在 @author hwliang @param site_id int 站点ID @return bool ''' site_info = public.M('sites').where('id=?',(site_id,)).field('name,path').find() if not site_info: return False args = public.dict_obj() args.id = site_id run_path = self.GetRunPath(args) site_run_path = site_info['path'] + '/' + run_path if os.path.exists(site_run_path): return True args.runPath = '/' self.SetSiteRunPath(args) public.WriteLog('TYPE_SITE','Due to modifying the root directory of the website [{}], the original running directory [.{}] does not exist, and the directory has been automatically switched to [./]'.format(site_info['name'],run_path)) return False #取当前可用PHP版本 def GetPHPVersion(self,get): phpVersions = public.get_php_versions() phpVersions.insert(0,'other') phpVersions.insert(0,'00') httpdVersion = "" filename = self.setupPath + '/apache/version.pl' if os.path.exists(filename): httpdVersion = public.readFile(filename).strip() if httpdVersion == '2.2': phpVersions = ('00','52','53','54') if httpdVersion == '2.4': if '52' in phpVersions: phpVersions.remove('52') if os.path.exists('/www/server/nginx/sbin/nginx'): cfile = '/www/server/nginx/conf/enable-php-00.conf' if not os.path.exists(cfile): public.writeFile(cfile,'') s_type = getattr(get,'s_type',0) data = [] for val in phpVersions: tmp = {} checkPath = self.setupPath+'/php/'+val+'/bin/php' if val in ['00','other']: checkPath = '/etc/init.d/bt' if httpdVersion == '2.2': checkPath = self.setupPath+'/php/'+val+'/libphp5.so' if os.path.exists(checkPath): tmp['version'] = val tmp['name'] = 'PHP-'+val if val == '00': tmp['name'] = public.lang("Static") if val == 'other': if s_type: tmp['name'] = 'Customize' else: continue data.append(tmp) return data # 取指定站点的PHP版本 def GetSitePHPVersion(self, get): try: siteName = get.siteName data = {} data['phpversion'] = public.get_site_php_version(siteName) conf = public.readFile(self.setupPath + '/panel/vhost/' + public.get_webserver() + '/' + siteName + '.conf') data['tomcat'] = conf.find('#TOMCAT-START') data['tomcatversion'] = public.readFile(self.setupPath + '/tomcat/version.pl') data['nodejsversion'] = public.readFile(self.setupPath + '/node.js/version.pl') data['php_other'] = '' if data['phpversion'] == 'other': other_file = '/www/server/panel/vhost/other_php/{}/enable-php-other.conf'.format(siteName) if os.path.exists(other_file): conf = public.readFile(other_file) data['php_other'] = re.findall(r"fastcgi_pass\s+(.+);",conf)[0] return data except: return public.return_msg_gettext(False, public.lang("Apache2.2 does NOT support MultiPHP!,{}", public.get_error_info())) def set_site_php_version_multiple(self, get): ''' @name 批量设置PHP版本 @author zhwen<2020-11-17> @param sites_id "1,2" @param version 52...74 ''' sites_id = get.sites_id.split(',') set_phpv_successfully = [] set_phpv_failed = {} for site_id in sites_id: get.id = site_id get.siteName = public.M('sites').where("id=?", (site_id,)).getField('name') if not get.siteName: continue try: result = self.SetPHPVersion(get, multiple=1) if not result['status']: set_phpv_failed[get.siteName] = result['msg'] continue set_phpv_successfully.append(get.siteName) except: set_phpv_failed[get.siteName] = public.lang("There was an error setting, please try again.") pass public.serviceReload() return {'status': True, 'msg': public.get_msg_gettext('Set up website [{}] PHP version successfully', (','.join(set_phpv_successfully),)), 'error': set_phpv_failed, 'success': set_phpv_successfully} # 设置指定站点的PHP版本 def SetPHPVersion(self, get, multiple=None): siteName = get.siteName version = get.version if version == 'other' and not public.get_webserver() in ['nginx','tengine']: return public.return_msg_gettext(False, public.lang("Custom PHP configuration only supports Nginx")) try: # nginx file = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf' conf = public.readFile(file) if conf: wp00 = "/www/server/nginx/conf/enable-php-00-wpfastcgi.conf" if not os.path.exists(wp00): public.writeFile(wp00, '') other_path = '/www/server/panel/vhost/other_php/{}'.format(siteName) if not os.path.exists(other_path): os.makedirs(other_path) other_rep = "{}/enable-php-other.conf".format(other_path) if version == 'other': dst = other_rep get.other = get.other.strip() if not get.other: return public.return_msg_gettext(False, public.lang("The PHP connection configuration cannot be empty when customizing the version!")) if not re.match(r"^(\d+\.\d+\.\d+\.\d+:\d+|unix:[\w/\.-]+)$",get.other): return public.return_msg_gettext(False, public.lang("The PHP connection configuration format is incorrect, please refer to the example!")) other_tmp = get.other.split(':') if other_tmp[0] == 'unix': if not os.path.exists(other_tmp[1]): return public.return_msg_gettext(False, public.lang("The specified unix socket [{}] does not exist!", other_tmp[1])) else: if not public.check_tcp(other_tmp[0],int(other_tmp[1])): return public.return_msg_gettext(False, public.lang("Unable to connect to [{}], please check whether the machine can connect to the target server", get.other)) other_conf = r'''location ~ [^/]\.php(/|$) {{ try_files $uri =404; fastcgi_pass {}; fastcgi_index index.php; include fastcgi.conf; include pathinfo.conf; }}'''.format(get.other) public.writeFile(other_rep,other_conf) conf = conf.replace(other_rep,dst) rep = r"include\s+enable-php-(\w{2,5})\.conf" tmp = re.search(rep,conf) if tmp: conf = conf.replace(tmp.group(),'include ' + dst) elif re.search(r"enable-php-\d+-wpfastcgi.conf",conf): dst = 'enable-php-{}-wpfastcgi.conf'.format(version) conf = conf.replace(other_rep,dst) rep = r"enable-php-\d+-wpfastcgi.conf" tmp = re.search(rep, conf) if tmp:conf = conf.replace(tmp.group(),dst) else: dst = 'enable-php-'+version+'.conf' conf = conf.replace(other_rep,dst) rep = r"enable-php-(\w{2,5})\.conf" tmp = re.search(rep,conf) if tmp: conf = conf.replace(tmp.group(),dst) public.writeFile(file,conf) try: import site_dir_auth site_dir_auth_module = site_dir_auth.SiteDirAuth() auth_list = site_dir_auth_module.get_dir_auth(get) if auth_list: for i in auth_list[siteName]: auth_name = i['name'] auth_file = "{setup_path}/panel/vhost/nginx/dir_auth/{site_name}/{auth_name}.conf".format( setup_path=self.setupPath,site_name=siteName,auth_name = auth_name) if os.path.exists(auth_file): site_dir_auth_module.change_dir_auth_file_nginx_phpver(siteName,version,auth_name) except: pass #apache file = self.setupPath + '/panel/vhost/apache/'+siteName+'.conf' conf = public.readFile(file) if conf and version != 'other': rep = r"(unix:/tmp/php-cgi-(\w{2,5})\.sock\|fcgi://localhost|fcgi://127.0.0.1:\d+)" tmp = re.search(rep,conf).group() conf = conf.replace(tmp,public.get_php_proxy(version,'apache')) public.writeFile(file,conf) #OLS if version != 'other': file = self.setupPath + '/panel/vhost/openlitespeed/detail/'+siteName+'.conf' conf = public.readFile(file) if conf: rep = r'lsphp\d+' tmp = re.search(rep, conf) if tmp: conf = conf.replace(tmp.group(), 'lsphp' + version) public.writeFile(file, conf) if not multiple: public.serviceReload() public.write_log_gettext("Site manager", 'Successfully changed PHP Version of site [{}] to PHP-{}', (siteName, version)) return public.return_msg_gettext(True, 'Successfully changed PHP Version of site [{}] to PHP-{}', (siteName, version)) except: return public.get_error_info() return public.return_msg_gettext(False, public.lang("Setup failed, no enable-php-xx related configuration items were found in the website configuration file!")) # 是否开启目录防御 def GetDirUserINI(self, get): path = get.path + self.GetRunPath(get) if not path: return public.return_msg_gettext(False, public.lang("Requested directory does not exist")) id = get.id get.name = public.M('sites').where("id=?", (id,)).getField('name') data = {} data['logs'] = self.GetLogsStatus(get) data['userini'] = False user_ini_file = path + '/.user.ini' user_ini_conf = public.readFile(user_ini_file) if user_ini_conf and "open_basedir" in user_ini_conf: data['userini'] = True data['runPath'] = self.GetSiteRunPath(get) data['pass'] = self.GetHasPwd(get) return data # 清除多余user.ini def DelUserInI(self, path, up=0): useriniPath = path + '/.user.ini' if os.path.exists(useriniPath): public.ExecShell('chattr -i ' + useriniPath) try: os.remove(useriniPath) except: pass for p1 in os.listdir(path): try: npath = path + '/' + p1 if not os.path.isdir(npath): continue useriniPath = npath + '/.user.ini' if os.path.exists(useriniPath): public.ExecShell('chattr -i ' + useriniPath) os.remove(useriniPath) if up < 3: self.DelUserInI(npath, up + 1) except: continue return True # 设置目录防御 def SetDirUserINI(self, get): path = get.path runPath = self.GetRunPath(get) filename = path + runPath + '/.user.ini' siteName = public.M('sites').where('path=?', (get.path,)).getField('name') conf = public.readFile(filename) try: self._set_ols_open_basedir(get) public.ExecShell("chattr -i " + filename) if conf and "open_basedir" in conf: rep = "\n*open_basedir.*" conf = re.sub(rep, "", conf) if not conf: os.remove(filename) else: public.writeFile(filename, conf) public.ExecShell("chattr +i " + filename) public.set_site_open_basedir_nginx(siteName) return public.return_msg_gettext(True, public.lang("Base directory turned off!")) if conf and "session.save_path" in conf: rep = r"session.save_path\s*=\s*(.*)" s_path = re.search(rep, conf).groups(1)[0] public.writeFile(filename, conf + '\nopen_basedir={}/:/tmp/:{}'.format(path, s_path)) else: public.writeFile(filename, 'open_basedir={}/:/tmp/'.format(path)) public.ExecShell("chattr +i " + filename) public.set_site_open_basedir_nginx(siteName) public.serviceReload() return public.return_msg_gettext(True, public.lang("Base directory turned on!")) except Exception as e: public.ExecShell("chattr +i " + filename) return str(e) def _set_ols_open_basedir(self, get): # 设置ols try: sitename = public.M('sites').where("id=?", (get.id,)).getField('name') # sitename = path.split('/')[-1] f = "/www/server/panel/vhost/openlitespeed/detail/{}.conf".format(sitename) c = public.readFile(f) if not c: return False if f: rep = '\nphp_admin_value\\s*open_basedir.*' result = re.search(rep, c) s = 'on' if not result: s = 'off' rep = '\n#php_admin_value\\s*open_basedir.*' result = re.search(rep, c) result = result.group() if s == 'on': c = re.sub(rep, '\n#' + result[1:], c) else: result = result.replace('#', '') c = re.sub(rep, result, c) public.writeFile(f, c) except: pass # 读配置 def __read_config(self, path): if not os.path.exists(path): public.writeFile(path, '[]') upBody = public.readFile(path) if not upBody: upBody = '[]' return json.loads(upBody) # 写配置 def __write_config(self, path, data): return public.writeFile(path, json.dumps(data)) # 取某个站点某条反向代理详情 def GetProxyDetals(self, get): proxyUrl = self.__read_config(self.__proxyfile) sitename = get.sitename proxyname = get.proxyname for i in proxyUrl: if i["proxyname"] == proxyname and i["sitename"] == sitename: return i # 取某个站点反向代理列表 def GetProxyList(self, get): n = 0 for w in ["nginx", "apache"]: conf_path = "%s/panel/vhost/%s/%s.conf" % (self.setupPath, w, get.sitename) old_conf = "" if os.path.exists(conf_path): old_conf = public.readFile(conf_path) rep = "(#PROXY-START(\n|.)+#PROXY-END)" url_rep = r"proxy_pass (.*);|ProxyPass\s/\s(.*)|Host\s(.*);" host_rep = r"Host\s(.*);" if re.search(rep, old_conf): # 构造代理配置 if w == "nginx": get.todomain = str(re.search(host_rep, old_conf).group(1)) get.proxysite = str(re.search(url_rep, old_conf).group(1)) else: get.todomain = "" get.proxysite = str(re.search(url_rep, old_conf).group(2)) get.proxyname = public.lang("Old proxy") get.type = 1 get.proxydir = "/" get.advanced = 0 get.cachetime = 1 get.cache = 0 get.subfilter = "[{\"sub1\":\"\",\"sub2\":\"\"},{\"sub1\":\"\",\"sub2\":\"\"},{\"sub1\":\"\",\"sub2\":\"\"}]" # proxyname_md5 = self.__calc_md5(get.proxyname) # 备份并替换老虚拟主机配置文件 public.ExecShell("cp %s %s_bak" % (conf_path, conf_path)) conf = re.sub(rep, "", old_conf) public.writeFile(conf_path, conf) if n == 0: self.CreateProxy(get) n += 1 # 写入代理配置 # proxypath = "%s/panel/vhost/%s/proxy/%s/%s_%s.conf" % ( # self.setupPath, w, get.sitename, proxyname_md5, get.sitename) # proxycontent = str(re.search(rep, old_conf).group(1)) # public.writeFile(proxypath, proxycontent) if n == "1": public.serviceReload() proxyUrl = self.__read_config(self.__proxyfile) sitename = get.sitename proxylist = [] for i in proxyUrl: if i["sitename"] == sitename: proxylist.append(i) return proxylist def del_proxy_multiple(self, get): ''' @name 批量网站到期时间 @author zhwen<2020-11-20> @param site_id 1 @param proxynames ces,aaa ''' proxynames = get.proxynames.split(',') del_successfully = [] del_failed = {} get.sitename = public.M('sites').where("id=?", (get.site_id,)).getField('name') for proxyname in proxynames: if not proxyname: continue get.proxyname = proxyname try: resule = self.RemoveProxy(get, multiple=1) if not resule['status']: del_failed[proxyname] = resule['msg'] del_successfully.append(proxyname) except: del_failed[proxyname] = public.lang("There was an error deleting, please try again.") pass return {'status': True, 'msg': public.get_msg_gettext('Delete [ {} ] proxy successfully', (','.join(del_failed),)), 'error': del_failed, 'success': del_successfully} # 删除反向代理 def RemoveProxy(self, get, multiple=None): conf = self.__read_config(self.__proxyfile) sitename = get.sitename proxyname = get.proxyname for i in range(len(conf)): c_sitename = conf[i]["sitename"] c_proxyname = conf[i]["proxyname"] if c_sitename == sitename and c_proxyname == proxyname: proxyname_md5 = self.__calc_md5(c_proxyname) for w in ["apache", "nginx", "openlitespeed"]: p = "{sp}/panel/vhost/{w}/proxy/{s}/{m}_{s}.conf*".format(sp=self.setupPath, w=w, s=c_sitename, m=proxyname_md5) public.ExecShell('rm -f {}'.format(p)) p = "{sp}/panel/vhost/openlitespeed/proxy/{s}/urlrewrite/{m}_{s}.conf*".format(sp=self.setupPath, m=proxyname_md5, s=get.sitename) public.ExecShell('rm -f {}'.format(p)) del conf[i] self.__write_config(self.__proxyfile, conf) self.SetNginx(get) self.SetApache(get.sitename) if not multiple: public.serviceReload() return public.return_msg_gettext(True, public.lang("Successfully deleted")) # 检查代理是否存在 def __check_even(self, get, action=""): conf_data = self.__read_config(self.__proxyfile) for i in conf_data: if i["sitename"] == get.sitename: if action == "create": if i["proxydir"] == get.proxydir or i["proxyname"] == get.proxyname: return i else: if i["proxyname"] != get.proxyname and i["proxydir"] == get.proxydir: return i # 检测全局代理和目录代理是否同时存在 def __check_proxy_even(self, get, action=""): conf_data = self.__read_config(self.__proxyfile) n = 0 if action == "": for i in conf_data: if i["sitename"] == get.sitename: n += 1 if n == 1: return for i in conf_data: if i["sitename"] == get.sitename: if i["advanced"] != int(get.advanced): return i # 计算proxyname md5 def __calc_md5(self, proxyname): md5 = hashlib.md5() md5.update(proxyname.encode('utf-8')) return md5.hexdigest() # 检测URL是否可以访问 def __CheckUrl(self, get): sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sk.settimeout(5) rep = r"(https?)://([\w\.\-]+):?([\d]+)?" h = re.search(rep, get.proxysite).group(1) d = re.search(rep, get.proxysite).group(2) try: p = re.search(rep, get.proxysite).group(3) except: p = "" try: if p: sk.connect((d, int(p))) else: if h == "http": sk.connect((d, 80)) else: sk.connect((d, 443)) except: return public.return_msg_gettext(False, public.lang("Can NOT get target URL")) # 基本设置检查 def __CheckStart(self, get, action=""): isError = public.checkWebConfig() if isinstance(isError,str): if isError.find('/proxy/') == -1: # 如果是反向代理配置文件本身的错误,跳过 return public.return_msg_gettext(False, public.lang("An error was detected in the configuration file. Please solve it before proceeding")) if action == "create": if sys.version_info.major < 3: if len(get.proxyname) < 3 or len(get.proxyname) > 40: return public.return_msg_gettext(False, public.lang("Database name cannot be more than 40 characters!")) else: if len(get.proxyname.encode("utf-8")) < 3 or len(get.proxyname.encode("utf-8")) > 40: return public.return_msg_gettext(False, public.lang("Database name cannot be more than 40 characters!")) if self.__check_even(get, action): return public.return_msg_gettext(False, public.lang("Specified reverse proxy name or proxy folder already exists")) # 判断代理,只能有全局代理或目录代理 if self.__check_proxy_even(get, action): return public.return_msg_gettext(False, public.lang("Cannot set both directory and global proxies")) # 判断cachetime类型 if get.cachetime: try: int(get.cachetime) except: return public.return_msg_gettext(False, public.lang("Please enter number")) rep = r"http(s)?\:\/\/" # repd = r"http(s)?\:\/\/([a-zA-Z0-9][-a-zA-Z0-9]{0,62}\.)+([a-zA-Z0-9][a-zA-Z0-9]{0,62})+.?" tod = "[a-zA-Z]+$" repte = "[\\?\\=\\[\\]\\)\\(\\*\\&\\^\\%\\$\\#\\@\\!\\~\\`{\\}\\>\\<\\,\',\"]+" # 检测代理目录格式 if re.search(repte, get.proxydir): return public.return_msg_gettext(False, "PROXY_DIR_ERR", ("?,=,[,],),(,*,&,^,%,$,#,@,!,~,`,{,},>,<,\\,',\"]",)) # 检测发送域名格式 if get.todomain: if re.search("[\\}\\{\\#\\;\"\']+",get.todomain): return public.return_msg_gettext(False, public.lang("Sent Domain format error :'+get.todomain+'
The following special characters cannot exist [ } { # ; \" \' ] ")) if public.get_webserver() != 'openlitespeed' and not get.todomain: get.todomain = "$host" # 检测目标URL格式 if not re.match(rep, get.proxysite): return public.return_msg_gettext(False, 'Sent domain format ERROR {}', (get.proxysite,)) if re.search(repte, get.proxysite): return public.return_msg_gettext(False, "PROXY_URL_ERR", ("?,=,[,],),(,*,&,^,%,$,#,@,!,~,`,{,},>,<,\\,',\"]",)) # 检测目标url是否可用 # if re.match(repd, get.proxysite): # if self.__CheckUrl(get): # return public.returnMsg(False, public.lang("The target URL cannot be accessed")) subfilter = json.loads(get.subfilter) # 检测替换内容 if subfilter: for s in subfilter: if not s["sub1"]: if s["sub2"]: return public.return_msg_gettext(False, public.lang("Please enter the content to be replaced")) elif s["sub1"] == s["sub2"]: return public.return_msg_gettext(False, public.lang("The content to replace cannot be the same as the content to be replaced")) # 设置Nginx配置 def SetNginx(self, get): ng_proxyfile = "%s/panel/vhost/nginx/proxy/%s/*.conf" % (self.setupPath, get.sitename) ng_file = self.setupPath + "/panel/vhost/nginx/" + get.sitename + ".conf" p_conf = self.__read_config(self.__proxyfile) cureCache = '' if public.get_webserver() == 'nginx': shutil.copyfile(ng_file, '/tmp/ng_file_bk.conf') # if os.path.exists('/www/server/nginx/src/ngx_cache_purge'): cureCache += ''' location ~ /purge(/.*) { proxy_cache_purge cache_one $host$1$is_args$args; #access_log /www/wwwlogs/%s_purge_cache.log; }''' % (get.sitename) if os.path.exists(ng_file): self.CheckProxy(get) ng_conf = public.readFile(ng_file) if not p_conf: # rep = "%s[\\w\\s\\~\\/\\(\\)\\.\\*\\{\\}\\;\\$\n\\#]+.{1,66}[\\s\\w\\/\\*\\.\\;]+include enable-php-" % public.GetMsg( # "CLEAR_CACHE") rep = "%s[\\w\\s\\~\\/\\(\\)\\.\\*\\{\\}\\;\\$\n\\#]+.*\n.*" % ("#Clear cache") # ng_conf = re.sub(rep, 'include enable-php-', ng_conf) ng_conf = re.sub(rep, '', ng_conf) oldconf = '''location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$ { expires 30d; error_log /dev/null; access_log /dev/null; } location ~ .*\\.(js|css)?$ { expires 12h; error_log /dev/null; access_log /dev/null; }''' if "(gif|jpg|jpeg|png|bmp|swf)$" not in ng_conf: ng_conf = re.sub(r'access_log\s*/www', oldconf + "\n\taccess_log /www",ng_conf) public.writeFile(ng_file, ng_conf) return sitenamelist = [] for i in p_conf: sitenamelist.append(i["sitename"]) if get.sitename in sitenamelist: rep = r"include.*\/proxy\/.*\*.conf;" if not re.search(rep, ng_conf): rep = "location.+\\(gif[\\w\\|\\$\\(\\)\n\\{\\}\\s\\;\\/\\~\\.\\*\\\\\\?]+access_log\\s+/" ng_conf = re.sub(rep, 'access_log /', ng_conf) ng_conf = ng_conf.replace("include enable-php-", "%s\n" % public.get_msg_gettext( "#Clear cache") + cureCache + "\n\t%s\n\t" % public.get_msg_gettext( "#Referenced reverse proxy rule, if commented, the configured reverse proxy will be invalid") + "include " + ng_proxyfile + ";\n\n\tinclude enable-php-") public.writeFile(ng_file, ng_conf) else: # rep = "%s[\\w\\s\\~\\/\\(\\)\\.\\*\\{\\}\\;\\$\n\\#]+.{1,66}[\\s\\w\\/\\*\\.\\;]+include enable-php-" % public.GetMsg( # "CLEAR_CACHE") rep = "%s[\\w\\s\\~\\/\\(\\)\\.\\*\\{\\}\\;\\$\n\\#]+.*\n.*" % ("#Clear cache") # ng_conf = re.sub(rep, 'include enable-php-', ng_conf) ng_conf = re.sub(rep,'',ng_conf) oldconf = '''location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$ { expires 30d; error_log /dev/null; access_log /dev/null; } location ~ .*\\.(js|css)?$ { expires 12h; error_log /dev/null; access_log /dev/null; }''' if "(gif|jpg|jpeg|png|bmp|swf)$" not in ng_conf: ng_conf = re.sub(r'access_log\s*/www', oldconf + "\n\taccess_log /www",ng_conf) public.writeFile(ng_file, ng_conf) # 设置apache配置 def SetApache(self, sitename): ap_proxyfile = "%s/panel/vhost/apache/proxy/%s/*.conf" % (self.setupPath, sitename) ap_file = self.setupPath + "/panel/vhost/apache/" + sitename + ".conf" p_conf = public.readFile(self.__proxyfile) if public.get_webserver() == 'apache': shutil.copyfile(ap_file, '/tmp/ap_file_bk.conf') if os.path.exists(ap_file): ap_conf = public.readFile(ap_file) if p_conf == "[]": rep = "\n*%s\n+\\s+IncludeOptiona[\\s\\w\\/\\.\\*]+" % ("#Referenced reverse proxy rule, if commented, the configured reverse proxy will be invalid") ap_conf = re.sub(rep, '', ap_conf) public.writeFile(ap_file, ap_conf) return if sitename in p_conf: rep = "combined(\n|.)+IncludeOptional.*\\/proxy\\/.*conf" rep1 = "combined" if not re.search(rep, ap_conf): ap_conf = ap_conf.replace(rep1, rep1 + "\n\t%s\n\t" % public.get_msg_gettext( '#Referenced reverse proxy rule, if commented, the configured reverse proxy will be invalid') + "\n\tIncludeOptional " + ap_proxyfile) public.writeFile(ap_file, ap_conf) else: # rep = "\n*#引用反向代理(\n|.)+IncludeOptional.*\\/proxy\\/.*conf" rep = "\n*%s\n+\\s+IncludeOptiona[\\s\\w\\/\\.\\*]+" % ("#Referenced reverse proxy rule, if commented, the configured reverse proxy will be invalid") ap_conf = re.sub(rep, '', ap_conf) public.writeFile(ap_file, ap_conf) # 设置OLS def _set_ols_proxy(self, get): # 添加反代配置 proxyname_md5 = self.__calc_md5(get.proxyname) dir_path = "%s/panel/vhost/openlitespeed/proxy/%s/" % (self.setupPath, get.sitename) if not os.path.exists(dir_path): os.makedirs(dir_path) file_path = "{}{}_{}.conf".format(dir_path, proxyname_md5, get.sitename) reverse_proxy_conf = """ extprocessor %s { type proxy address %s maxConns 1000 pcKeepAliveTimeout 600 initTimeout 600 retryTimeout 0 respBuffer 0 } """ % (get.proxyname, get.proxysite) public.writeFile(file_path, reverse_proxy_conf) # 添加urlrewrite dir_path = "%s/panel/vhost/openlitespeed/proxy/%s/urlrewrite/" % (self.setupPath, get.sitename) if not os.path.exists(dir_path): os.makedirs(dir_path) file_path = "{}{}_{}.conf".format(dir_path, proxyname_md5, get.sitename) reverse_urlrewrite_conf = """ RewriteRule ^%s(.*)$ http://%s/$1 [P,E=Proxy-Host:%s] """ % (get.proxydir, get.proxyname, get.todomain) public.writeFile(file_path, reverse_urlrewrite_conf) # 检查伪静态、主配置文件是否有location冲突 def CheckLocation(self, get): # 伪静态文件路径 rewriteconfpath = "%s/panel/vhost/rewrite/%s.conf" % (self.setupPath, get.sitename) # 主配置文件路径 nginxconfpath = "%s/nginx/conf/nginx.conf" % (self.setupPath) # vhost文件 vhostpath = "%s/panel/vhost/nginx/%s.conf" % (self.setupPath, get.sitename) rep = "location\\s+/[\n\\s]+{" for i in [rewriteconfpath, nginxconfpath, vhostpath]: conf = public.readFile(i) if re.findall(rep, conf): return public.return_msg_gettext(False, public.lang("A global reverse proxy already exists in the rewrite/nginx master configuration/vhost file")) # 创建反向代理 def CreateProxy(self, get): try: nocheck = get.nocheck except: nocheck = "" if not get.get('proxysite',None): return public.returnMsg(False, public.lang("Destination URL cannot be empty")) if not nocheck: if self.__CheckStart(get, "create"): return self.__CheckStart(get, "create") if public.get_webserver() == 'nginx': if self.CheckLocation(get): return self.CheckLocation(get) if not get.proxysite.split('//')[-1]: return public.returnMsg(False, public.lang("The target URL cannot be [http:// or https://], please fill in the full URL, such as: https://yakpanel.com")) # project_type = public.M('sites').where('name=?', (get.sitename,)).field('project_type').find()['project_type'] # if project_type == 'WP': # return public.return_msg_gettext(False, public.lang("Reverse proxies are not currently available for Wordpress sites that use one-click deployment")) proxyUrl = self.__read_config(self.__proxyfile) proxyUrl.append({ "proxyname": get.proxyname, "sitename": get.sitename, "proxydir": get.proxydir, "proxysite": get.proxysite, "todomain": get.todomain, "type": int(get.type), "cache": int(get.cache), "subfilter": json.loads(get.subfilter), "advanced": int(get.advanced), "cachetime": int(get.cachetime) }) self.__write_config(self.__proxyfile, proxyUrl) self.SetNginx(get) self.SetApache(get.sitename) self._set_ols_proxy(get) status = self.SetProxy(get) if not status["status"]: return status if get.proxydir == '/': get.version = '00' get.siteName = get.sitename self.SetPHPVersion(get) public.serviceReload() return public.return_msg_gettext(True, public.lang("Setup successfully!")) # 取代理配置文件 def GetProxyFile(self, get): import files conf = self.__read_config(self.__proxyfile) sitename = get.sitename proxyname = get.proxyname proxyname_md5 = self.__calc_md5(proxyname) get.path = "%s/panel/vhost/%s/proxy/%s/%s_%s.conf" % ( self.setupPath, get.webserver, sitename, proxyname_md5, sitename) for i in conf: if proxyname == i["proxyname"] and sitename == i["sitename"] and i["type"] != 1: return public.return_msg_gettext(False, public.lang("Proxy suspended")) f = files.files() return f.GetFileBody(get), get.path # 保存代理配置文件 def SaveProxyFile(self, get): import files f = files.files() return f.SaveFileBody(get) # return public.returnMsg(True, public.lang("Saved successfully")) # 检查是否存在#Set Nginx Cache def check_annotate(self, data): rep = "\n\\s*#Set\\s*Nginx\\s*Cache" if re.search(rep, data): return True def old_proxy_conf(self,conf,ng_conf_file,get): rep = r'location\s*\~\*.*gif\|png\|jpg\|css\|js\|woff\|woff2\)\$' if not re.search(rep,conf): return conf self.RemoveProxy(get) self.CreateProxy(get) return public.readFile(ng_conf_file) # 修改反向代理 def ModifyProxy(self, get): if not get.get('proxysite',None): return public.returnMsg(False, public.lang("Destination URL cannot be empty")) proxyname_md5 = self.__calc_md5(get.proxyname) ap_conf_file = "{p}/panel/vhost/apache/proxy/{s}/{n}_{s}.conf".format( p=self.setupPath, s=get.sitename, n=proxyname_md5) ng_conf_file = "{p}/panel/vhost/nginx/proxy/{s}/{n}_{s}.conf".format( p=self.setupPath, s=get.sitename, n=proxyname_md5) ols_conf_file = "{p}/panel/vhost/openlitespeed/proxy/{s}/urlrewrite/{n}_{s}.conf".format( p=self.setupPath, s=get.sitename, n=proxyname_md5) if self.__CheckStart(get): return self.__CheckStart(get) conf = self.__read_config(self.__proxyfile) random_string = public.GetRandomString(8) for i in range(len(conf)): if conf[i]["proxyname"] == get.proxyname and conf[i]["sitename"] == get.sitename: if int(get.type) != 1: if not os.path.exists(ng_conf_file): return public.returnMsg(False, public.lang("Please enable the reverse proxy before editing!")) public.ExecShell("mv {f} {f}_bak".format(f=ap_conf_file)) public.ExecShell("mv {f} {f}_bak".format(f=ng_conf_file)) public.ExecShell("mv {f} {f}_bak".format(f=ols_conf_file)) conf[i]["type"] = int(get.type) self.__write_config(self.__proxyfile, conf) public.serviceReload() return public.return_msg_gettext(True, public.lang("Setup successfully!")) else: if os.path.exists(ap_conf_file + "_bak"): public.ExecShell("mv {f}_bak {f}".format(f=ap_conf_file)) public.ExecShell("mv {f}_bak {f}".format(f=ng_conf_file)) public.ExecShell("mv {f}_bak {f}".format(f=ols_conf_file)) ng_conf = public.readFile(ng_conf_file) ng_conf = self.old_proxy_conf(ng_conf, ng_conf_file, get) # 修改nginx配置 # 如果代理URL后缀带有URI则删除URI,正则匹配不支持proxypass处带有uri php_pass_proxy = get.proxysite if get.proxysite[-1] == '/' or get.proxysite.count('/') > 2 or '?' in get.proxysite: php_pass_proxy = re.search(r'(https?\:\/\/[\w\.]+)', get.proxysite).group(0) ng_conf = re.sub(r"location\s+[\^\~]*\s?%s" % conf[i]["proxydir"], "location ^~ " + get.proxydir, ng_conf) ng_conf = re.sub(r"proxy_pass\s+%s" % conf[i]["proxysite"], "proxy_pass " + get.proxysite, ng_conf) ng_conf = re.sub("location\\s+\\~\\*\\s+\\\\.\\(php.*\n\\{\\s*proxy_pass\\s+%s.*" % (php_pass_proxy), "location ~* \\.(php|jsp|cgi|asp|aspx)$\n{\n\tproxy_pass %s;" % php_pass_proxy,ng_conf) ng_conf = re.sub("location\\s+\\~\\*\\s+\\\\.\\(gif.*\n\\{\\s*proxy_pass\\s+%s.*" % (php_pass_proxy), "location ~* \\.(gif|png|jpg|css|js|woff|woff2)$\n{\n\tproxy_pass %s;" % php_pass_proxy,ng_conf) backslash = "" if "Host $host" in ng_conf: backslash = "\\" ng_conf = re.sub(r"\sHost\s+%s" % backslash + conf[i]["todomain"], " Host " + get.todomain, ng_conf) cache_rep = r"proxy_cache_valid\s+200\s+304\s+301\s+302\s+\d+m;((\n|.)+expires\s+\d+m;)*" if int(get.cache) == 1: if re.search(cache_rep, ng_conf): expires_rep = "\\{\n\\s+expires\\s+12h;" ng_conf = re.sub(expires_rep, "{", ng_conf) ng_conf = re.sub(cache_rep, "proxy_cache_valid 200 304 301 302 {0}m;".format(get.cachetime), ng_conf) else: # ng_cache = """ # proxy_ignore_headers Set-Cookie Cache-Control expires; # proxy_cache cache_one; # proxy_cache_key $host$uri$is_args$args; # proxy_cache_valid 200 304 301 302 %sm;""" % (get.cachetime) ng_cache = r""" if ( $uri ~* "\.(gif|png|jpg|css|js|woff|woff2)$" ) { expires 1m; } proxy_ignore_headers Set-Cookie Cache-Control expires; proxy_cache cache_one; proxy_cache_key $host$uri$is_args$args; proxy_cache_valid 200 304 301 302 %sm;""" % (get.cachetime) if self.check_annotate(ng_conf): cache_rep = '\n\\s*#Set\\s*Nginx\\s*Cache(.|\n)*no-cache;\\s*\n*\\s*\\}' ng_conf = re.sub(cache_rep, '\n\t#Set Nginx Cache\n' + ng_cache, ng_conf) else: # cache_rep = r'#proxy_set_header\s+Connection\s+"upgrade";' cache_rep = r"proxy_set_header\s+REMOTE-HOST\s+\$remote_addr;" ng_conf = re.sub(cache_rep, r"\n\tproxy_set_header\s+REMOTE-HOST\s+\$remote_addr;\n\t#Set Nginx Cache" + ng_cache, ng_conf) else: no_cache = r""" #Set Nginx Cache set $static_file%s 0; if ( $uri ~* "\.(gif|png|jpg|css|js|woff|woff2)$" ) { set $static_file%s 1; expires 1m; } if ( $static_file%s = 0 ) { add_header Cache-Control no-cache; } } #PROXY-END/""" % (random_string, random_string, random_string) if self.check_annotate(ng_conf): rep = r'\n\s*#Set\s*Nginx\s*Cache(.|\n)*' # ng_conf = re.sub(rep, # "\n\t#Set Nginx Cache\n\tproxy_ignore_headers Set-Cookie Cache-Control expires;\n\tadd_header Cache-Control no-cache;", # ng_conf) ng_conf = re.sub(rep,no_cache,ng_conf) else: rep = r"\s+proxy_cache\s+cache_one.*[\n\s\w\_\";\$]+m;" # ng_conf = re.sub(rep, # r"\n\t#Set Nginx Cache\n\tproxy_ignore_headers Set-Cookie Cache-Control expires;\n\tadd_header Cache-Control no-cache;", # ng_conf) ng_conf = re.sub(rep,no_cache,ng_conf) sub_rep = "sub_filter" subfilter = json.loads(get.subfilter) if str(conf[i]["subfilter"]) != str(subfilter) or ng_conf.find('sub_filter_once') == -1: if re.search(sub_rep, ng_conf): sub_rep = "\\s+proxy_set_header\\s+Accept-Encoding(.|\n)+off;" ng_conf = re.sub(sub_rep, "", ng_conf) # 构造替换字符串 ng_subdata = '' ng_sub_filter = ''' proxy_set_header Accept-Encoding "";%s sub_filter_once off;''' if subfilter: for s in subfilter: if not s["sub1"]: continue if '"' in s["sub1"]: s["sub1"] = s["sub1"].replace('"', '\\"') if '"' in s["sub2"]: s["sub2"] = s["sub2"].replace('"', '\\"') ng_subdata += '\n\tsub_filter "%s" "%s";' % (s["sub1"], s["sub2"]) if ng_subdata: ng_sub_filter = ng_sub_filter % (ng_subdata) else: ng_sub_filter = '' sub_rep = r'#Set\s+Nginx\s+Cache' ng_conf = re.sub(sub_rep, '#Set Nginx Cache\n' + ng_sub_filter, ng_conf) # 修改apache配置 ap_conf = public.readFile(ap_conf_file) ap_conf = re.sub(r"ProxyPass\s+%s\s+%s" % (conf[i]["proxydir"], conf[i]["proxysite"]), "ProxyPass %s %s" % (get.proxydir, get.proxysite), ap_conf) ap_conf = re.sub(r"ProxyPassReverse\s+%s\s+%s" % (conf[i]["proxydir"], conf[i]["proxysite"]), "ProxyPassReverse %s %s" % (get.proxydir, get.proxysite), ap_conf) # 修改OLS配置 p = "{p}/panel/vhost/openlitespeed/proxy/{s}/{n}_{s}.conf".format(p=self.setupPath, n=proxyname_md5, s=get.sitename) c = public.readFile(p) if c: rep = r'address\s+(.*)' new_proxysite = 'address\t{}'.format(get.proxysite) c = re.sub(rep, new_proxysite, c) public.writeFile(p, c) # p = "{p}/panel/vhost/openlitespeed/proxy/{s}/urlrewrite/{n}_{s}.conf".format(p=self.setupPath,n=proxyname_md5,s=get.sitename) c = public.readFile(ols_conf_file) if c: rep = r'RewriteRule\s*\^{}\(\.\*\)\$\s+http://{}/\$1\s*\[P,E=Proxy-Host:{}\]'.format( conf[i]["proxydir"], get.proxyname, conf[i]["todomain"]) new_content = 'RewriteRule ^{}(.*)$ http://{}/$1 [P,E=Proxy-Host:{}]'.format(get.proxydir, get.proxyname, get.todomain) c = re.sub(rep, new_content, c) public.writeFile(ols_conf_file, c) conf[i]["proxydir"] = get.proxydir conf[i]["proxysite"] = get.proxysite conf[i]["todomain"] = get.todomain conf[i]["type"] = int(get.type) conf[i]["cache"] = int(get.cache) conf[i]["subfilter"] = json.loads(get.subfilter) conf[i]["advanced"] = int(get.advanced) conf[i]["cachetime"] = int(get.cachetime) public.writeFile(ng_conf_file, ng_conf) public.writeFile(ap_conf_file, ap_conf) self.__write_config(self.__proxyfile, conf) self.SetNginx(get) self.SetApache(get.sitename) # self.SetProxy(get) # if int(get.type) != 1: # os.system("mv %s %s_bak" % (ap_conf_file, ap_conf_file)) # os.system("mv %s %s_bak" % (ng_conf_file, ng_conf_file)) if not hasattr(get, 'notreload'): public.serviceReload() return public.return_msg_gettext(True, public.lang("Setup successfully!")) # 设置反向代理 def SetProxy(self, get): sitename = get.sitename # 站点名称 advanced = int(get.advanced) type = int(get.type) cache = int(get.cache) cachetime = int(get.cachetime) proxysite = get.proxysite proxydir = get.proxydir ng_file = self.setupPath + "/panel/vhost/nginx/" + sitename + ".conf" ap_file = self.setupPath + "/panel/vhost/apache/" + sitename + ".conf" p_conf = self.__read_config(self.__proxyfile) random_string = public.GetRandomString(8) # websocket前置map map_file = self.setupPath + "/panel/vhost/nginx/0.websocket.conf" if not os.path.exists(map_file): map_body = '''map $http_upgrade $connection_upgrade { default upgrade; '' close; } ''' public.writeFile(map_file,map_body) # 配置Nginx # 构造清理缓存连接 # 构造缓存配置 ng_cache = r""" if ( $uri ~* "\.(gif|png|jpg|css|js|woff|woff2)$" ) { expires 1m; } proxy_ignore_headers Set-Cookie Cache-Control expires; proxy_cache cache_one; proxy_cache_key $host$uri$is_args$args; proxy_cache_valid 200 304 301 302 %sm;""" % (cachetime) no_cache = r""" set $static_file%s 0; if ( $uri ~* "\.(gif|png|jpg|css|js|woff|woff2)$" ) { set $static_file%s 1; expires 1m; } if ( $static_file%s = 0 ) { add_header Cache-Control no-cache; }""" % (random_string,random_string,random_string) # rep = r"(https?://[\w\.]+)" # proxysite1 = re.search(rep,get.proxysite).group(1) ng_proxy = ''' #PROXY-START%s location %s { proxy_pass %s; proxy_set_header Host %s; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_http_version 1.1; # proxy_hide_header Upgrade; %s add_header X-Cache $upstream_cache_status; #Set Nginx Cache %s %s } #PROXY-END%s''' ng_proxy_cache = '' proxyname_md5 = self.__calc_md5(get.proxyname) ng_proxyfile = "%s/panel/vhost/nginx/proxy/%s/%s_%s.conf" % (self.setupPath, sitename, proxyname_md5, sitename) ng_proxydir = "%s/panel/vhost/nginx/proxy/%s" % (self.setupPath, sitename) if not os.path.exists(ng_proxydir): public.ExecShell("mkdir -p %s" % ng_proxydir) # 构造替换字符串 ng_subdata = '' ng_sub_filter = ''' proxy_set_header Accept-Encoding "";%s sub_filter_once off;''' if get.subfilter: for s in json.loads(get.subfilter): if not s["sub1"]: continue if '"' in s["sub1"]: s["sub1"] = s["sub1"].replace('"', '\\"') if '"' in s["sub2"]: s["sub2"] = s["sub2"].replace('"', '\\"') ng_subdata += '\n\tsub_filter "%s" "%s";' % (s["sub1"], s["sub2"]) if ng_subdata: ng_sub_filter = ng_sub_filter % (ng_subdata) else: ng_sub_filter = '' # 构造反向代理 # 如果代理URL后缀带有URI则删除URI,正则匹配不支持proxypass处带有uri # php_pass_proxy = get.proxysite # if get.proxysite[-1] == '/' or get.proxysite.count('/') > 2 or '?' in get.proxysite: # php_pass_proxy = re.search(r'(https?\:\/\/[\w\.]+)', get.proxysite).group(0) if advanced == 1: if proxydir[-1] != '/': proxydir = '{}/'.format(proxydir) if proxysite[-1] != '/': proxysite = '{}/'.format(proxysite) if type == 1 and cache == 1: ng_proxy_cache += ng_proxy % ( proxydir, proxydir, proxysite, get.todomain, ("#Persistent connection related configuration"), ng_sub_filter, ng_cache, get.proxydir) if type == 1 and cache == 0: ng_proxy_cache += ng_proxy % ( get.proxydir, get.proxydir, proxysite, get.todomain, ("#Persistent connection related configuration"), ng_sub_filter, no_cache, get.proxydir) else: if type == 1 and cache == 1: ng_proxy_cache += ng_proxy % ( get.proxydir, get.proxydir, get.proxysite, get.todomain, ("#Persistent connection related configuration"), ng_sub_filter, ng_cache, get.proxydir) if type == 1 and cache == 0: ng_proxy_cache += ng_proxy % ( get.proxydir, get.proxydir, get.proxysite, get.todomain, ("#Persistent connection related configuration"), ng_sub_filter, no_cache, get.proxydir) public.writeFile(ng_proxyfile, ng_proxy_cache) # APACHE # 反向代理文件 ap_proxyfile = "%s/panel/vhost/apache/proxy/%s/%s_%s.conf" % ( self.setupPath, get.sitename, proxyname_md5, get.sitename) ap_proxydir = "%s/panel/vhost/apache/proxy/%s" % (self.setupPath, get.sitename) if not os.path.exists(ap_proxydir): public.ExecShell("mkdir -p %s" % ap_proxydir) ap_proxy = '' if type == 1: ap_proxy += '''#PROXY-START%s ProxyRequests Off SSLProxyEngine on ProxyPass %s %s/ ProxyPassReverse %s %s/ #PROXY-END%s''' % (get.proxydir, get.proxydir, get.proxysite, get.proxydir, get.proxysite, get.proxydir) public.writeFile(ap_proxyfile, ap_proxy) isError = public.checkWebConfig() if (isError != True): if public.get_webserver() == "nginx": shutil.copyfile('/tmp/ng_file_bk.conf', ng_file) else: shutil.copyfile('/tmp/ap_file_bk.conf', ap_file) for i in range(len(p_conf) - 1, -1, -1): if get.sitename == p_conf[i]["sitename"] and p_conf[i]["proxyname"]: del p_conf[i] self.RemoveProxy(get) return public.return_msg_gettext(False, 'ERROR: %s
' % public.get_msg_gettext( 'Configuration ERROR') + isError.replace("\n", '
') + '
') return public.return_msg_gettext(True, public.lang("Setup successfully!")) # 开启缓存 def ProxyCache(self, get): if public.get_webserver() != 'nginx': return public.return_msg_gettext(False, public.lang("Currently only support Nginx")) file = self.setupPath + "/panel/vhost/nginx/" + get.siteName + ".conf" conf = public.readFile(file) if conf.find('proxy_pass') == -1: return public.return_msg_gettext(False, public.lang("Failed to set")) if conf.find('#proxy_cache') != -1: conf = conf.replace('#proxy_cache', 'proxy_cache') conf = conf.replace('#expires 12h', 'expires 12h') else: conf = conf.replace('proxy_cache', '#proxy_cache') conf = conf.replace('expires 12h', '#expires 12h') public.writeFile(file, conf) public.serviceReload() return public.return_msg_gettext(True, public.lang("Setup successfully!")) # 检查反向代理配置 def CheckProxy(self, get): if public.get_webserver() != 'nginx': return True file = self.setupPath + "/nginx/conf/proxy.conf" if not os.path.exists(file): conf = '''proxy_temp_path %s/nginx/proxy_temp_dir; proxy_cache_path %s/nginx/proxy_cache_dir levels=1:2 keys_zone=cache_one:10m inactive=1d max_size=5g; client_body_buffer_size 512k; proxy_connect_timeout 60; proxy_read_timeout 60; proxy_send_timeout 60; proxy_buffer_size 32k; proxy_buffers 4 64k; proxy_busy_buffers_size 128k; proxy_temp_file_write_size 128k; proxy_next_upstream error timeout invalid_header http_500 http_503 http_404; proxy_cache cache_one;''' % (self.setupPath, self.setupPath) public.writeFile(file, conf) file = self.setupPath + "/nginx/conf/nginx.conf" conf = public.readFile(file) if (conf.find('include proxy.conf;') == -1): rep = r"include\s+mime.types;" conf = re.sub(rep, "include mime.types;\n\tinclude proxy.conf;", conf) public.writeFile(file, conf) def get_project_find(self,project_name): ''' @name 获取指定项目配置 @author hwliang<2021-08-09> @param project_name 项目名称 @return dict ''' project_info = public.M('sites').where('project_type=? AND name=?',('Java',project_name)).find() if not project_info: return False project_info['project_config'] = json.loads(project_info['project_config']) return project_info #取伪静态规则应用列表 def GetRewriteList(self,get): if get.siteName.find('node_') == 0: get.siteName = get.siteName.replace('node_', '') rewriteList = {} ws = public.get_webserver() if ws == "openlitespeed": ws = "apache" if ws == 'apache': get.id = public.M('sites').where("name=?", (get.siteName,)).getField('id') runPath = self.GetSiteRunPath(get) if runPath['runPath'].find('/www/server/stop') != -1: runPath['runPath'] = runPath['runPath'].replace('/www/server/stop', '') rewriteList['sitePath'] = public.M('sites').where("name=?", (get.siteName,)).getField('path') + runPath[ 'runPath'] rewriteList['rewrite'] = [] rewriteList['rewrite'].append('0.' + public.lang("Current")) for ds in os.listdir('rewrite/' + ws): if ds == 'list.txt': continue rewriteList['rewrite'].append(ds[0:len(ds) - 5]) rewriteList['rewrite'] = sorted(rewriteList['rewrite']) return rewriteList # 保存伪静态模板 def SetRewriteTel(self, get): ws = public.get_webserver() if not get.name: public.return_msg_gettext(True, 'Please enter a template name') if ws == "openlitespeed": ws = "apache" if sys.version_info[0] == 2: get.name = get.name.encode('utf-8') filename = 'rewrite/' + ws + '/' + get.name + '.conf' public.writeFile(filename, get.data) return public.return_msg_gettext(True, public.lang("New URL rewrite rule has been saved!")) # 打包 def ToBackup(self, get): id = get.id find = public.M('sites').where("id=?", (id,)).field('name,path,id').find() import time fileName = find['name'] + '_' + time.strftime('%Y%m%d_%H%M%S', time.localtime()) + '.zip' backupPath = session['config']['backup_path'] + '/site' zipName = backupPath + '/' + fileName if not (os.path.exists(backupPath)): os.makedirs(backupPath) tmps = '/tmp/panelExec.log' execStr = "cd '" + find['path'] + "' && zip '" + zipName + "' -x .user.ini -r ./ > " + tmps + " 2>&1" public.ExecShell(execStr) sql = public.M('backup').add('type,name,pid,filename,size,addtime', (0, fileName, find['id'], zipName, 0, public.getDate())) public.write_log_gettext('Site manager', 'Backup site [{}] succeed!', (find['name'],)) return public.return_msg_gettext(True, public.lang("Backup Succeeded!")) # 删除备份文件 def DelBackup(self, get): id = get.id where = "id=?" backup_info = public.M('backup').where(where,(id,)).find() filename = backup_info['filename'] if os.path.exists(filename): os.remove(filename) name = '' if filename == 'qiniu': name = backup_info['name'] public.ExecShell(public.get_python_bin() + " "+self.setupPath + '/panel/script/backup_qiniu.py delete_file ' + name) pid = backup_info['pid'] site_name = public.M('sites').where('id=?',(pid,)).getField('name') public.write_log_gettext('Site manager', 'Successfully deleted backup [{}] of site [{}]!', (site_name, filename)) public.M('backup').where(where, (id,)).delete() return public.return_msg_gettext(True, public.lang("Successfully deleted")) # 旧版本配置文件处理 def OldConfigFile(self): # 检查是否需要处理 moveTo = 'data/moveTo.pl' if os.path.exists(moveTo): return # 处理Nginx配置文件 filename = self.setupPath + "/nginx/conf/nginx.conf" if os.path.exists(filename): conf = public.readFile(filename) if conf.find('include vhost/*.conf;') != -1: conf = conf.replace('include vhost/*.conf;', 'include ' + self.setupPath + '/panel/vhost/nginx/*.conf;') public.writeFile(filename, conf) self.moveConf(self.setupPath + "/nginx/conf/vhost", self.setupPath + '/panel/vhost/nginx', 'rewrite', self.setupPath + '/panel/vhost/rewrite') self.moveConf(self.setupPath + "/nginx/conf/rewrite", self.setupPath + '/panel/vhost/rewrite') # 处理Apache配置文件 filename = self.setupPath + "/apache/conf/httpd.conf" if os.path.exists(filename): conf = public.readFile(filename) if conf.find('IncludeOptional conf/vhost/*.conf') != -1: conf = conf.replace('IncludeOptional conf/vhost/*.conf', 'IncludeOptional ' + self.setupPath + '/panel/vhost/apache/*.conf') public.writeFile(filename, conf) self.moveConf(self.setupPath + "/apache/conf/vhost", self.setupPath + '/panel/vhost/apache') # 标记处理记录 public.writeFile(moveTo, 'True') public.serviceReload() # 移动旧版本配置文件 def moveConf(self, Path, toPath, Replace=None, ReplaceTo=None): if not os.path.exists(Path): return import shutil letPath = '/etc/letsencrypt/live' nginxPath = self.setupPath + '/nginx/conf/key' apachePath = self.setupPath + '/apache/conf/key' for filename in os.listdir(Path): # 准备配置文件 name = filename[0:len(filename) - 5] filename = Path + '/' + filename conf = public.readFile(filename) # 替换关键词 if Replace: conf = conf.replace(Replace, ReplaceTo) ReplaceTo = letPath + name Replace = 'conf/key/' + name if conf.find(Replace) != -1: conf = conf.replace(Replace, ReplaceTo) Replace = 'key/' + name if conf.find(Replace) != -1: conf = conf.replace(Replace, ReplaceTo) public.writeFile(filename, conf) # 提取配置信息 if conf.find('server_name') != -1: self.formatNginxConf(filename) elif conf.find(' 0: return public.M('sites').add('name,path,status,ps,addtime', (name, path, '1', public.lang("Please enter a note"), public.getDate())) pid = public.M('sites').where("name=?", (name,)).getField('id') for domain in domains: public.M('domain').add('pid,name,port,addtime', (pid, domain, '80', public.getDate())) # 移动旧版本证书 def moveKey(self, srcPath, dstPath): if not os.path.exists(srcPath): return import shutil os.makedirs(dstPath) srcKey = srcPath + '/key.key' srcCsr = srcPath + '/csr.key' if os.path.exists(srcKey): shutil.move(srcKey, dstPath + '/privkey.pem') if os.path.exists(srcCsr): shutil.move(srcCsr, dstPath + '/fullchain.pem') # 路径处理 def GetPath(self, path): if path[-1] == '/': return path[0:-1] return path # 日志开关 def logsOpen(self, get): get.name = public.M('sites').where("id=?", (get.id,)).getField('name') # APACHE filename = public.GetConfigValue('setup_path') + '/panel/vhost/apache/' + get.name + '.conf' if os.path.exists(filename): conf = public.readFile(filename) if conf.find('#ErrorLog') != -1: conf = conf.replace("#ErrorLog", "ErrorLog").replace('#CustomLog', 'CustomLog') else: conf = conf.replace("ErrorLog", "#ErrorLog").replace('CustomLog', '#CustomLog') public.writeFile(filename, conf) # NGINX filename = public.GetConfigValue('setup_path') + '/panel/vhost/nginx/' + get.name + '.conf' if os.path.exists(filename): conf = public.readFile(filename) rep = public.GetConfigValue('logs_path') + "/" + get.name + ".log" if conf.find(rep) != -1: conf = conf.replace(rep, "/dev/null") else: # conf = re.sub('}\n\\s+access_log\\s+off', '}\n\taccess_log ' + rep, conf) conf = conf.replace('access_log /dev/null', 'access_log ' + rep) public.writeFile(filename, conf) # OLS filename = public.GetConfigValue('setup_path') + '/panel/vhost/openlitespeed/detail/' + get.name + '.conf' conf = public.readFile(filename) if conf: rep = "\nerrorlog(.|\n)*compressArchive\\s*1\\s*\n}" tmp = re.search(rep, conf) s = 'on' if not tmp: s = 'off' rep = "\n#errorlog(.|\n)*compressArchive\\s*1\\s*\n#}" tmp = re.search(rep, conf) tmp = tmp.group() if tmp: result = '' if s == 'on': for l in tmp.strip().splitlines(): result += "\n#"+l else: for l in tmp.splitlines(): result += "\n"+l[1:] conf = re.sub(rep,"\n"+result.strip(),conf) public.writeFile(filename,conf) public.serviceReload() return public.return_msg_gettext(True, public.lang("Setup successfully!")) # 取日志状态 def GetLogsStatus(self, get): if not hasattr(get, 'name') or not get.name: return True if isinstance(get.name, list): site_name = get.name[0] if get.name else '' elif isinstance(get.name, str): site_name = get.name else: site_name = str(get.name) site_name = site_name.strip() if not site_name: return True filename = public.GetConfigValue( 'setup_path') + '/panel/vhost/' + public.get_webserver() + '/' + get.name + '.conf' if public.get_webserver() == 'openlitespeed': filename = public.GetConfigValue( 'setup_path') + '/panel/vhost/' + public.get_webserver() + '/detail/' + get.name + '.conf' conf = public.readFile(filename) if not conf: return True if conf.find('#ErrorLog') != -1: return False #if re.search("}\n*\\s*access_log\\s+off", conf): if conf.find("access_log /dev/null") != -1: return False if re.search('\n#accesslog', conf): return False return True # 取目录加密状态 def GetHasPwd(self, get): if not hasattr(get, 'siteName'): get.siteName = public.M('sites').where('id=?', (get.id,)).getField('name') get.configFile = self.setupPath + '/panel/vhost/nginx/' + get.siteName + '.conf' conf = public.readFile(get.configFile) if type(conf) == bool: return False if conf.find('#AUTH_START') != -1: return True return False # 设置目录加密 def SetHasPwd(self, get): if public.get_webserver() == 'openlitespeed': return public.return_msg_gettext(False, public.lang("The current web server is openlitespeed. This function is not supported yet.")) if len(get.username.strip()) < 3 or len(get.password.strip()) < 3: return public.return_msg_gettext(False, public.lang("Username or password cannot be less than 3 digits!")) if not hasattr(get, 'siteName'): get.siteName = public.M('sites').where('id=?', (get.id,)).getField('name') self.CloseHasPwd(get) filename = public.GetConfigValue('setup_path') + '/pass/' + get.siteName + '.pass' try: passconf = get.username + ':' + public.hasPwd(get.password) except: return public.returnMsg(False, public.lang("The password fomart is wrong, please do not use special symbols for the first two digits!")) if get.siteName == 'phpmyadmin': get.configFile = self.setupPath + '/nginx/conf/nginx.conf' if os.path.exists(self.setupPath + '/panel/vhost/nginx/phpmyadmin.conf'): get.configFile = self.setupPath + '/panel/vhost/nginx/phpmyadmin.conf' else: get.configFile = self.setupPath + '/panel/vhost/nginx/' + get.siteName + '.conf' # 处理Nginx配置 conf = public.readFile(get.configFile) if conf: rep = '#error_page 404 /404.html;' if conf.find(rep) == -1: rep = '#error_page 404/404.html;' data = ''' #AUTH_START auth_basic "Authorization"; auth_basic_user_file %s; #AUTH_END''' % (filename,) conf = conf.replace(rep, rep + data) public.writeFile(get.configFile, conf) if get.siteName == 'phpmyadmin': get.configFile = self.setupPath + '/apache/conf/extra/httpd-vhosts.conf' if os.path.exists(self.setupPath + '/panel/vhost/apache/phpmyadmin.conf'): get.configFile = self.setupPath + '/panel/vhost/apache/phpmyadmin.conf' else: get.configFile = self.setupPath + '/panel/vhost/apache/' + get.siteName + '.conf' conf = public.readFile(get.configFile) if conf: # 处理Apache配置 rep = 'SetOutputFilter' if conf.find(rep) != -1: data = '''#AUTH_START AuthType basic AuthName "Authorization " AuthUserFile %s Require user %s #AUTH_END ''' % (filename, get.username) conf = conf.replace(rep, data + rep) conf = conf.replace(' Require all granted', " #Require all granted") public.writeFile(get.configFile, conf) # 写密码配置 passDir = public.GetConfigValue('setup_path') + '/pass' if not os.path.exists(passDir): public.ExecShell('mkdir -p ' + passDir) public.writeFile(filename, passconf) public.serviceReload() public.write_log_gettext("Site manager", "Set site [{}] to password authentication required!", (get.siteName,)) return public.return_msg_gettext(True, public.lang("Setup successfully!")) # 取消目录加密 def CloseHasPwd(self, get): if not hasattr(get, 'siteName'): get.siteName = public.M('sites').where('id=?', (get.id,)).getField('name') if get.siteName == 'phpmyadmin': get.configFile = self.setupPath + '/nginx/conf/nginx.conf' else: get.configFile = self.setupPath + '/panel/vhost/nginx/' + get.siteName + '.conf' if os.path.exists(get.configFile): conf = public.readFile(get.configFile) rep = "\n\\s*#AUTH_START(.|\n){1,200}#AUTH_END" conf = re.sub(rep, '', conf) public.writeFile(get.configFile, conf) if get.siteName == 'phpmyadmin': get.configFile = self.setupPath + '/apache/conf/extra/httpd-vhosts.conf' else: get.configFile = self.setupPath + '/panel/vhost/apache/' + get.siteName + '.conf' if os.path.exists(get.configFile): conf = public.readFile(get.configFile) rep = "\n\\s*#AUTH_START(.|\n){1,200}#AUTH_END" conf = re.sub(rep, '', conf) conf = conf.replace(' #Require all granted', " Require all granted") public.writeFile(get.configFile, conf) public.serviceReload() public.write_log_gettext("Site manager", "Cleared password authentication for site [{}]!", (get.siteName,)) return public.return_msg_gettext(True, public.lang("Setup successfully!")) # 启用tomcat支持 def SetTomcat(self, get): siteName = get.siteName name = siteName.replace('.', '_') rep = r"^(\d{1,3}\.){3,3}\d{1,3}$" if re.match(rep, siteName): return public.return_msg_gettext(False, public.lang("ERROR, primary domain cannot be IP address!")) # nginx filename = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf' if os.path.exists(filename): conf = public.readFile(filename) if conf.find('#TOMCAT-START') != -1: return self.CloseTomcat(get) tomcatConf = r'''#TOMCAT-START location / { proxy_pass "http://%s:8080"; proxy_set_header Host %s; proxy_set_header X-Forwarded-For $remote_addr; } location ~ .*\.(gif|jpg|jpeg|bmp|png|ico|txt|js|css)$ { expires 12h; } location ~ .*\.war$ { return 404; } #TOMCAT-END ''' % (siteName, siteName) rep = 'include enable-php' conf = conf.replace(rep, tomcatConf + rep) public.writeFile(filename, conf) # apache filename = self.setupPath + '/panel/vhost/apache/' + siteName + '.conf' if os.path.exists(filename): conf = public.readFile(filename) if conf.find('#TOMCAT-START') != -1: return self.CloseTomcat(get) tomcatConf = '''#TOMCAT-START ProxyRequests Off SSLProxyEngine on ProxyPass / http://%s:8080/ ProxyPassReverse / http://%s:8080/ RequestHeader unset Accept-Encoding ExtFilterDefine fixtext mode=output intype=text/html cmd="/bin/sed 's,:8080,,g'" SetOutputFilter fixtext #TOMCAT-END ''' % (siteName, siteName) rep = '#PATH' conf = conf.replace(rep, tomcatConf + rep) public.writeFile(filename, conf) path = public.M('sites').where("name=?", (siteName,)).getField('path') import tomcat tomcat.tomcat().AddVhost(path, siteName) public.serviceReload() public.ExecShell('/etc/init.d/tomcat stop') public.ExecShell('/etc/init.d/tomcat start') public.ExecShell('echo "127.0.0.1 ' + siteName + '" >> /etc/hosts') public.write_log_gettext('TYPE_SITE', 'Turned on Tomcat supporting for site [{}]!', (siteName,)) return public.return_msg_gettext(True, public.lang("Succeeded, please test JSP program!")) # 关闭tomcat支持 def CloseTomcat(self, get): if not os.path.exists('/etc/init.d/tomcat'): return False siteName = get.siteName name = siteName.replace('.', '_') # nginx filename = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf' if os.path.exists(filename): conf = public.readFile(filename) rep = "\\s*#TOMCAT-START(.|\n)+#TOMCAT-END" conf = re.sub(rep, '', conf) public.writeFile(filename, conf) # apache filename = self.setupPath + '/panel/vhost/apache/' + siteName + '.conf' if os.path.exists(filename): conf = public.readFile(filename) rep = "\\s*#TOMCAT-START(.|\n)+#TOMCAT-END" conf = re.sub(rep, '', conf) public.writeFile(filename, conf) public.ExecShell('rm -rf ' + self.setupPath + '/panel/vhost/tomcat/' + name) try: import tomcat tomcat.tomcat().DelVhost(siteName) except: pass public.serviceReload() public.ExecShell('/etc/init.d/tomcat restart') public.ExecShell("sed -i '/" + siteName + "/d' /etc/hosts") public.write_log_gettext('Site manager', 'Turned off Tomcat supporting for site [{}]!', (siteName,)) return public.return_msg_gettext(True, public.lang("Tomcat mapping closed!")) #取当站点前运行目录 def GetSiteRunPath(self,get): site = public.M('sites').where('id=?',(get.id,)).field('name,path,service_type').find() if not site: return {"runPath": "/", 'dirs': []} siteName = site['name'] sitePath = site['path'] if not siteName or os.path.isfile(sitePath): return {"runPath":"/",'dirs':[]} path = sitePath # 兼容多服务 webserver = public.get_webserver() if public.get_multi_webservice_status(): webserver = site['service_type'] if site['service_type'] else 'nginx' if webserver == 'nginx': filename = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf' if os.path.exists(filename): conf = public.readFile(filename) rep = r'\s*root\s+(.+);' path = re.search(rep, conf) if not path: return public.return_msg_gettext(False, public.lang("Get Site run path false")) path = path.groups()[0] elif webserver == 'apache': filename = self.setupPath + '/panel/vhost/apache/' + siteName + '.conf' if os.path.exists(filename): conf = public.readFile(filename) rep = '\\s*DocumentRoot\\s*"(.+)"\\s*\n' path = re.search(rep, conf) if not path: return public.return_msg_gettext(False, public.lang("Get Site run path false")) path = path.groups()[0] else: filename = self.setupPath + '/panel/vhost/openlitespeed/' + siteName + '.conf' if os.path.exists(filename): conf = public.readFile(filename) rep = r"vhRoot\s*(.*)" path = re.search(rep, conf) if not path: return public.return_msg_gettext(False, public.lang("Get Site run path false")) path = path.groups()[0] data = {} if sitePath == path: data['runPath'] = '/' else: data['runPath'] = path.replace(sitePath, '') dirnames = [] dirnames.append('/') if not os.path.exists(sitePath): os.makedirs(sitePath) for filename in os.listdir(sitePath): try: json.dumps(filename) if sys.version_info[0] == 2: filename = filename.encode('utf-8') else: filename.encode('utf-8') filePath = sitePath + '/' + filename if not os.path.exists(filePath): continue if os.path.islink(filePath): continue if os.path.isdir(filePath): dirnames.append('/' + filename) except: pass data['dirs'] = dirnames return data # 设置当前站点运行目录 def SetSiteRunPath(self, get): siteName = public.M('sites').where('id=?', (get.id,)).getField('name') sitePath = public.M('sites').where('id=?', (get.id,)).getField('path') old_run_path = self.GetRunPath(get) # 处理Nginx filename = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf' if os.path.exists(filename): conf = public.readFile(filename) if conf: rep = r'\s*root\s+(.+);' tmp = re.search(rep,conf) if tmp: path = tmp.groups()[0] conf = conf.replace(path,sitePath + get.runPath) public.writeFile(filename,conf) #处理Apache filename = self.setupPath + '/panel/vhost/apache/' + siteName + '.conf' if os.path.exists(filename): conf = public.readFile(filename) if conf: rep = '\\s*DocumentRoot\\s*"(.+)"\\s*\n' tmp = re.search(rep,conf) if tmp: path = tmp.groups()[0] conf = conf.replace(path,sitePath + get.runPath) public.writeFile(filename,conf) # 处理OLS self._set_ols_run_path(sitePath, get.runPath, siteName) # self.DelUserInI(sitePath) # get.path = sitePath; # self.SetDirUserINI(get); s_path = sitePath + old_run_path + "/.user.ini" d_path = sitePath + get.runPath + "/.user.ini" if s_path != d_path: public.ExecShell("chattr -i {}".format(s_path)) public.ExecShell("mv {} {}".format(s_path, d_path)) public.ExecShell("chattr +i {}".format(d_path)) public.serviceReload() return public.return_msg_gettext(True, public.lang("Setup successfully!")) def _set_ols_run_path(self, site_path, run_path, sitename): ols_conf_file = "{}/panel/vhost/openlitespeed/{}.conf".format(self.setupPath, sitename) ols_conf = public.readFile(ols_conf_file) if not ols_conf: return reg = '#VHOST\\s*{s}\\s*START(.|\n)+#VHOST\\s*{s}\\s*END'.format(s=sitename) tmp = re.search(reg, ols_conf) if not tmp: return reg = r"vhRoot\s*(.*)" # tmp = re.search(reg,tmp.group()) # if not tmp: # return tmp = "vhRoot " + site_path + run_path ols_conf = re.sub(reg, tmp, ols_conf) public.writeFile(ols_conf_file, ols_conf) # 设置默认站点 def SetDefaultSite(self, get): import time if public.GetWebServer() in ['openlitespeed']: return public.returnMsg(False, public.lang("OpenLiteSpeed does not support setting the default site")) default_site_save = 'data/defaultSite.pl' # 清理旧的 defaultSite = public.readFile(default_site_save) http2 = '' versionStr = public.readFile('/www/server/nginx/version.pl') if versionStr: if versionStr.find('1.8.1') == -1 and versionStr.find('1.25') == -1 and versionStr.find('1.26') == -1: http2 = ' http2' if defaultSite: path = self.setupPath + '/panel/vhost/nginx/' + defaultSite + '.conf' if os.path.exists(path): conf = public.readFile(path) rep = r"listen\s+80.+;" conf = re.sub(rep, 'listen 80;', conf, 1) rep = r"listen\s+\[::\]:80.+;" conf = re.sub(rep, 'listen [::]:80;', conf, 1) rep = r"listen\s+443.+;" conf = re.sub(rep, 'listen 443 ssl' + http2 + ';', conf, 1) rep = r"listen\s+\[::\]:443.+;" conf = re.sub(rep, 'listen [::]:443 ssl' + http2 + ';', conf, 1) public.writeFile(path, conf) path = self.setupPath + '/apache/htdocs/.htaccess' if os.path.exists(path): os.remove(path) if get.name == '0': if os.path.exists(default_site_save): os.remove(default_site_save) public.serviceReload() return public.return_msg_gettext(True, public.lang("Setup successfully!")) # 处理新的 path = self.setupPath + '/apache/htdocs' if os.path.exists(path): conf = ''' RewriteEngine on RewriteCond %{HTTP_HOST} !^127.0.0.1 [NC] RewriteRule (.*) http://%s/$1 [L] ''' conf = conf.replace("%s", get.name) if get.name == 'off': conf = '' public.writeFile(path + '/.htaccess', conf) path = self.setupPath + '/panel/vhost/nginx/' + get.name + '.conf' if os.path.exists(path): conf = public.readFile(path) rep = r"listen\s+80\s*;" conf = re.sub(rep, 'listen 80 default_server;', conf, 1) rep = r"listen\s+\[::\]:80\s*;" conf = re.sub(rep, 'listen [::]:80 default_server;', conf, 1) rep = r"listen\s+443\s*ssl\s*\w*\s*;" conf = re.sub(rep, 'listen 443 ssl' + http2 + ' default_server;', conf, 1) rep = r"listen\s+\[::\]:443\s*ssl\s*\w*\s*;" conf = re.sub(rep, 'listen [::]:443 ssl' + http2 + ' default_server;', conf, 1) public.writeFile(path, conf) path = self.setupPath + '/panel/vhost/nginx/default.conf' if os.path.exists(path): public.ExecShell('rm -f ' + path) public.writeFile(default_site_save, get.name) public.serviceReload() return public.return_msg_gettext(True, public.lang("Setup successfully!")) # 取默认站点 def GetDefaultSite(self, get): data = {} data['sites'] = public.M('sites').where('project_type=? OR project_type=?',('PHP','WP')).field('name').order('id desc').select() data['defaultSite'] = public.readFile('data/defaultSite.pl') return data # 扫描站点 def CheckSafe(self, get): import db, time isTask = '/tmp/panelTask.pl' if os.path.exists(self.setupPath + '/panel/class/panelSafe.py'): import py_compile py_compile.compile(self.setupPath + '/panel/class/panelSafe.py') get.path = public.M('sites').where('id=?', (get.id,)).getField('path') execstr = "cd " + public.GetConfigValue( 'setup_path') + "/panel/class && " + public.get_python_bin() + " panelSafe.pyc " + get.path sql = db.Sql() sql.table('tasks').add('id,name,type,status,addtime,execstr', ( None, '%s [' % public.lang("Scan directory") + get.path + ']', 'execshell', '0', time.strftime('%Y-%m-%d %H:%M:%S'), execstr)) public.writeFile(isTask, 'True') public.write_log_gettext('Installer', 'Added trojan scan task for directory [{}]!', (get.path,)) return public.return_msg_gettext(True, public.lang("Scan Task has in the queue!")) # 获取结果信息 def GetCheckSafe(self, get): get.path = public.M('sites').where('id=?', (get.id,)).getField('path') path = get.path + '/scan.pl' result = {} result['data'] = [] result['phpini'] = [] result['userini'] = result['sshd'] = True result['scan'] = False result['outime'] = result['count'] = result['error'] = 0 if not os.path.exists(path): return result import json return json.loads(public.readFile(path)) # 更新病毒库 def UpdateRulelist(self, get): try: conf = public.httpGet(public.getUrl() + '/install/ruleList.conf') if conf: public.writeFile(self.setupPath + '/panel/data/ruleList.conf', conf) return public.return_msg_gettext(True, public.lang("Update Succeeded!")) return public.return_msg_gettext(False, public.lang("Failed to connect server!")) except: return public.return_msg_gettext(False, public.lang("Failed to connect server!")) def set_site_etime_multiple(self, get): ''' @name 批量网站到期时间 @author zhwen<2020-11-17> @param sites_id "1,2" @param edate 2020-11-18 ''' sites_id = get.sites_id.split(',') set_edate_successfully = [] set_edate_failed = {} for site_id in sites_id: get.id = site_id site_name = public.M('sites').where("id=?", (site_id,)).getField('name') if not site_name: continue try: self.SetEdate(get) set_edate_successfully.append(site_name) except: set_edate_failed[site_name] = 'There was an error setting, please try again.' pass return {'status': True, 'msg': public.get_msg_gettext('Set the website [{}] expiration time successfully', (','.join(set_edate_successfully),)), 'error': set_edate_failed, 'success': set_edate_successfully} # 设置到期时间 def SetEdate(self, get): result = public.M('sites').where('id=?', (get.id,)).setField('edate', get.edate) siteName = public.M('sites').where('id=?', (get.id,)).getField('name') public.write_log_gettext('Site manager', 'Set expired date to [{}] for site[{}]!', (get.edate,siteName)) return public.return_msg_gettext(True, public.lang("Successfully set, the site will stop automatically when expires!")) # 获取防盗链状态 def GetSecurity(self, get): file = '/www/server/panel/vhost/nginx/' + get.name + '.conf' conf = public.readFile(file) if type(conf) == bool: return public.return_msg_gettext(False, public.lang("Configuration file not exist")) if not isinstance(conf, str) or not conf.strip(): return public.return_msg_gettext(False, public.lang("Configuration file not exist")) data = {} if conf.find('SECURITY-START') != -1: rep = "#SECURITY-START(\n|.)+#SECURITY-END" tmp = re.search(rep, conf).group() data['fix'] = re.search(r"\(.+\)\$", tmp).group().replace('(', '').replace(')$', '').replace('|', ',') try: data['domains'] = ','.join( list(set(re.search("valid_referers\\s+none\\s+blocked\\s+(.+);\n", tmp).groups()[0].split()))) except: data['domains'] = ','.join(list(set(re.search("valid_referers\\s+(.+);\n", tmp).groups()[0].split()))) data['status'] = True data['none'] = tmp.find('none blocked') != -1 try: data['return_rule'] = re.findall(r'(return|rewrite)\s+.*(\d{3}|(/.+)\s+(break|last));', conf)[0][ 1].replace('break', '').strip() except: data['return_rule'] = '404' else: data['fix'] = 'jpg,jpeg,gif,png,js,css' domains = public.M('domain').where('pid=?', (get.id,)).field('name').select() tmp = [] for domain in domains: tmp.append(domain['name']) data['return_rule'] = '404' data['domains'] = ','.join(tmp) data['status'] = False data['none'] = False return data # 设置防盗链 def SetSecurity(self, get): if len(get.fix) < 2: return public.return_msg_gettext(False, public.lang("URL suffix cannot be empty!")) if len(get.domains) < 3: return public.return_msg_gettext(False, public.lang("Anti-theft chain domain name cannot be empty!")) file = '/www/server/panel/vhost/nginx/' + get.name + '.conf' if os.path.exists(file): conf = public.readFile(file) if get.status == '1': if conf.find('SECURITY-START') == -1: return public.return_msg_gettext(False, public.lang("Please turn on the hotlink first!")) r_key = 'valid_referers none blocked' d_key = 'valid_referers' if conf.find(r_key) == -1: conf = conf.replace(d_key, r_key) else: conf = conf.replace(r_key, d_key) else: if conf.find('SECURITY-START') != -1: # 先替换域名部分,防止域名过多导致替换失败 rep = r"\s+valid_referers.+" conf = re.sub(rep,'',conf) # 再替换配置部分 rep = "\\s+#SECURITY-START(\n|.){1,500}#SECURITY-END\n?" conf = re.sub(rep,'\n',conf) public.write_log_gettext('Site manager', "Hotlink Protection for site [{}] disabled!", (get.name,)) else: return_rule = 'return 404' if 'return_rule' in get: get.return_rule = get.return_rule.strip() if get.return_rule in ['404', '403', '200', '301', '302', '401', '201']: return_rule = 'return {}'.format(get.return_rule) else: if get.return_rule[0] != '/': return public.return_msg_gettext(False, public.lang("Response resources should use URI path or HTTP status code, such as: /test.png or 404")) return_rule = 'rewrite /.* {} break'.format(get.return_rule) rconf = r'''%s location ~ .*\.(%s)$ { expires 30d; access_log /dev/null; valid_referers %s; if ($invalid_referer){ %s; } } #SECURITY-END include enable-php-''' % (("#SECURITY-START Hotlink protection configuration"), get.fix.strip().replace(',', '|'), get.domains.strip().replace(',', ' '), return_rule) conf = re.sub(r"include\s+enable-php-", rconf, conf) public.write_log_gettext('Site manager', "Hotlink Protection for site [{}] enabled!", (get.name,)) public.writeFile(file, conf) file = '/www/server/panel/vhost/apache/' + get.name + '.conf' if os.path.exists(file): conf = public.readFile(file) if get.status == '1': r_key = '#SECURITY-START.*\n RewriteEngine on\n RewriteCond %{HTTP_REFERER} !^$ [NC]\n' d_key = '#SECURITY-START.*\n RewriteEngine on\n' if conf.find(r_key) == -1: conf = conf.replace(d_key, r_key) else: if conf.find('SECURITY-START') == -1: return public.return_msg_gettext(False, public.lang("Please turn on anti-theft first!")) conf = conf.replace(r_key, d_key) else: if conf.find('SECURITY-START') != -1: rep = "#SECURITY-START(\n|.){1,500}#SECURITY-END\n" conf = re.sub(rep, '', conf) else: return_rule = '/404.html [R=404,NC,L]' if 'return_rule' in get: get.return_rule = get.return_rule.strip() if get.return_rule in ['404', '403', '200', '301', '302', '401', '201']: return_rule = '/{s}.html [R={s},NC,L]'.format(s=get.return_rule) else: if get.return_rule[0] != '/': return public.return_msg_gettext(False, public.lang("Response resources should use URI path or HTTP status code, such as: /test.png or 404")) return_rule = '{}'.format(get.return_rule) tmp = " RewriteCond %{HTTP_REFERER} !{DOMAIN} [NC]" tmps = [] for d in get.domains.split(','): tmps.append(tmp.replace('{DOMAIN}', d)) domains = "\n".join(tmps) rconf = "combined\n " + public.get_msg_gettext( '#SECURITY-START Hotlink protection configuration') + "\n RewriteEngine on\n" + domains + "\n RewriteRule .(" + get.fix.strip().replace( ',', '|') + ") " + return_rule + "\n #SECURITY-END" conf = conf.replace('combined', rconf) public.writeFile(file, conf) # OLS cond_dir = '/www/server/panel/vhost/openlitespeed/prevent_hotlink/' if not os.path.exists(cond_dir): os.makedirs(cond_dir) file = cond_dir + get.name + '.conf' if get.status == '1': conf = r""" RewriteCond %{HTTP_REFERER} !^$ RewriteCond %{HTTP_REFERER} !YP_SSL_DOMAIN_NAME [NC] RewriteRule \.(YP_FILE_EXT)$ /404.html [R,NC] """ conf = conf.replace('YP_SSL_DOMAIN_NAME', get.domains.replace(',', ' ')).replace('YP_FILE_EXT', get.fix.replace(',', '|')) else: conf = r""" RewriteCond %{HTTP_REFERER} !YP_SSL_DOMAIN_NAME [NC] RewriteRule \.(YP_FILE_EXT)$ /404.html [R,NC] """ conf = conf.replace('YP_SSL_DOMAIN_NAME', get.domains.replace(',', ' ')).replace('YP_FILE_EXT', get.fix.replace(',', '|')) public.writeFile(file, conf) if get.status == "false": public.ExecShell('rm -f {}'.format(file)) public.serviceReload() return public.return_msg_gettext(True, public.lang("Setup successfully!")) # xss 防御 def xsssec(self,text): replace_list = { "<":"<", ">":">", "'":"'", '"':""", } for k,v in replace_list.items(): text = text.replace(k,v) return public.xssencode2(text) # 取网站日志 def GetSiteLogs(self, get): serverType = public.get_webserver() if serverType == "nginx": logPath = '/www/wwwlogs/' + get.siteName + '.log' elif serverType == 'apache': logPath = '/www/wwwlogs/' + get.siteName + '-access_log' else: logPath = '/www/wwwlogs/' + get.siteName + '_ols.access_log' if not os.path.exists(logPath): return public.return_msg_gettext(False, public.lang("Log is empty")) return public.return_msg_gettext(True, self.xsssec(public.GetNumLines(logPath, 1000))) # 取网站日志 def get_site_err_log(self, get): serverType = public.get_webserver() if serverType == "nginx": logPath = '/www/wwwlogs/' + get.siteName + '.error.log' elif serverType == 'apache': logPath = '/www/wwwlogs/' + get.siteName + '-error_log' else: logPath = '/www/wwwlogs/' + get.siteName + '_ols.error_log' if not os.path.exists(logPath): return public.return_msg_gettext(False, public.lang("Log is empty")) return public.return_msg_gettext(True, self.xsssec(public.GetNumLines(logPath, 1000))) # 取网站分类 def get_site_types(self, get): data = public.M("site_types").field("id,name").order("id asc").select() if not isinstance(data, list): data = [] data.insert(0, {"id": 0, "name": public.lang("Default category")}) for i in data: i['name']=public.xss_version(i['name']) return data # 添加网站分类 def add_site_type(self, get): get.name = get.name.strip() if not get.name: return public.return_msg_gettext(False, public.lang("Category name cannot be empty")) if len(get.name) > 16: return public.return_msg_gettext(False, public.lang("Category name cannot exceed 16 letters")) type_sql = public.M('site_types') if type_sql.count() >= 10: return public.return_msg_gettext(False, public.lang("Add up to 10 categories!")) if type_sql.where('name=?', (get.name,)).count() > 0: return public.return_msg_gettext(False, public.lang("Specified category name already exists!")) type_sql.add("name",(public.xssencode2(get.name),)) return public.return_msg_gettext(True, public.lang("Setup successfully!")) # 删除网站分类 def remove_site_type(self, get): type_sql = public.M('site_types') if type_sql.where('id=?', (get.id,)).count() == 0: return public.return_msg_gettext(False, public.lang("Specified category does NOT exist!")) type_sql.where('id=?', (get.id,)).delete() public.M("sites").where("type_id=?", (get.id,)).save("type_id", (0,)) return public.return_msg_gettext(True, public.lang("Category deleted!")) # 修改网站分类名称 def modify_site_type_name(self, get): get.name = get.name.strip() if not get.name: return public.return_msg_gettext(False, public.lang("Category name cannot be empty")) if len(get.name) > 16: return public.return_msg_gettext(False, public.lang("Category name cannot exceed 16 letters")) type_sql = public.M('site_types') if type_sql.where('id=?', (get.id,)).count() == 0: return public.return_msg_gettext(False, public.lang("Specified category does NOT exist!")) type_sql.where('id=?', (get.id,)).setField('name', get.name) return public.return_msg_gettext(True, public.lang("Successfully modified")) # 设置指定站点的分类 def set_site_type(self, get): site_ids = json.loads(get.site_ids) site_sql = public.M("sites") for s_id in site_ids: site_sql.where("id=?", (s_id,)).setField("type_id", get.id) return public.returnMsg(True, public.lang("Setup successfully!")) # 设置目录保护 def set_dir_auth(self, get): sd = site_dir_auth.SiteDirAuth() return sd.set_dir_auth(get) def delete_dir_auth_multiple(self, get): ''' @name 批量目录保护 @author zhwen<2020-11-17> @param site_id 1 @param names test,baohu ''' names = get.names.split(',') del_successfully = [] del_failed = {} for name in names: get.name = name get.id = get.site_id try: get.multiple = 1 result = self.delete_dir_auth(get) if not result['status']: del_failed[name] = result['msg'] continue del_successfully.append(name) except: del_failed[name] = public.lang("There was an error deleting, please try again.") public.serviceReload() return {'status': True, 'msg': public.get_msg_gettext('Delete [ {} ] dir auth successfully', (','.join(del_successfully),)), 'error': del_failed, 'success': del_successfully} # 删除目录保护 def delete_dir_auth(self, get): sd = site_dir_auth.SiteDirAuth() return sd.delete_dir_auth(get) # 获取目录保护列表 def get_dir_auth(self, get): sd = site_dir_auth.SiteDirAuth() return sd.get_dir_auth(get) # 修改目录保护密码 def modify_dir_auth_pass(self, get): sd = site_dir_auth.SiteDirAuth() return sd.modify_dir_auth_pass(get) def _check_path_total(self,path, limit): """ 根据路径获取文件/目录大小 @path 文件或者目录路径 return int """ if not os.path.exists(path): return 0; if not os.path.isdir(path): return os.path.getsize(path) size_total = 0 for nf in os.walk(path): for f in nf[2]: filename = nf[0] + '/' + f if not os.path.exists(filename): continue; if os.path.islink(filename): continue; size_total += os.path.getsize(filename) if size_total >= limit: return limit return size_total def get_average_num(self,slist): """ @获取平均值 """ count = len(slist) limit_size = 1 * 1024 * 1024 if count <= 0: return limit_size print(slist) if len(slist) > 1: slist = sorted(slist) limit_size =int((slist[0] + slist[-1])/2 * 0.85) return limit_size def check_del_data(self,get): """ @删除前置检测 @ids = [1,2,3] """ ids = json.loads(get['ids']) slist = {} result = [] import database db_data = database.database().get_database_size(ids,True) limit_size = 50 * 1024 * 1024 f_list_size = [];db_list_size = [] for id in ids: data = public.M('sites').where("id=?",(id,)).field('id,name,path,addtime').find(); if not data: continue addtime = public.to_date(times = data['addtime']) data['st_time'] = addtime data['limit'] = False data['backup_count'] = public.M('backup').where("pid=? AND type=?",(data['id'],'0')).count() f_size = self._check_path_total(data['path'],limit_size) data['total'] = f_size; data['score'] = 0 #目录太小不计分 if f_size > 0: f_list_size.append(f_size) # 10k 目录不参与排序 if f_size > 10 * 1024: data['score'] = int(time.time() - addtime) + f_size if data['total'] >= limit_size: data['limit'] = True data['database'] = False find = public.M('databases').field('id,pid,name,ps,addtime').where('pid=?',(data['id'],)).find() if find: db_addtime = public.to_date(times = find['addtime']) data['database'] = db_data[find['name']] data['database']['st_time'] = db_addtime db_score = 0 db_size = data['database']['total'] if db_size > 0: db_list_size.append(db_size) if db_size > 50 * 1024: db_score += int(time.time() - db_addtime) + db_size data['score'] += db_score result.append(data) slist['data'] = sorted(result,key= lambda x:x['score'],reverse=True) slist['file_size'] = self.get_average_num(f_list_size) slist['db_size'] = self.get_average_num(db_list_size) return slist def get_https_mode(self, get=None): ''' @name 获取https模式 @author hwliang<2022-01-14> @return bool False.宽松模式 True.严格模式 ''' web_server = public.get_webserver() if web_server not in ['nginx', 'apache']: return False if web_server == 'nginx': default_conf_file = "{}/nginx/0.default.conf".format(public.get_vhost_path()) else: default_conf_file = "{}/apache/0.default.conf".format(public.get_vhost_path()) if not os.path.exists(default_conf_file): return False default_conf = public.readFile(default_conf_file) if not default_conf: return False if default_conf.find('DEFAULT SSL CONFI') != -1: return True return False def write_ngx_default_conf_by_ssl(self): ''' @name 写nginx默认配置文件(含SSL配置) @author hwliang<2022-01-14> @return bool ''' default_conf_body = '''server { listen 80; listen 443 ssl; server_name _; index index.html; root /www/server/nginx/html; # DEFAULT SSL CONFIG ssl_certificate /www/server/panel/vhost/cert/0.default/fullchain.pem; ssl_certificate_key /www/server/panel/vhost/cert/0.default/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; add_header Strict-Transport-Security "max-age=31536000"; }''' ngx_default_conf_file = "{}/nginx/0.default.conf".format(public.get_vhost_path()) self.create_default_cert() return public.writeFile(ngx_default_conf_file, default_conf_body) def write_ngx_default_conf(self): ''' @name 写nginx默认配置文件 @author hwliang<2022-01-14> @return bool ''' default_conf_body = '''server { listen 80; server_name _; index index.html; root /www/server/nginx/html; }''' ngx_default_conf_file = "{}/nginx/0.default.conf".format(public.get_vhost_path()) return public.writeFile(ngx_default_conf_file, default_conf_body) def write_apa_default_conf_by_ssl(self): ''' @name 写nginx默认配置文件(含SSL配置) @author hwliang<2022-01-14> @return bool ''' port_80 = '80' port_443 = '443' if public.get_multi_webservice_status(): port_443 = '8290' port_80 = '8288' default_conf_body = f''' ServerAdmin webmaster@example.com DocumentRoot "/www/server/apache/htdocs" ServerName bt.default.com SetOutputFilter DEFLATE Options FollowSymLinks AllowOverride All Order allow,deny Allow from all DirectoryIndex index.html ServerAdmin webmaster@example.com DocumentRoot "/www/server/apache/htdocs" ServerName ssl.default.com # DEFAULT SSL CONFIG SSLEngine On SSLCertificateFile /www/server/panel/vhost/cert/0.default/fullchain.pem SSLCertificateKeyFile /www/server/panel/vhost/cert/0.default/privkey.pem SSLCipherSuite EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5 SSLProtocol All -SSLv2 -SSLv3 -TLSv1 SSLHonorCipherOrder On SetOutputFilter DEFLATE Options FollowSymLinks AllowOverride All Order allow,deny Allow from all DirectoryIndex index.html ''' apa_default_conf_file = "{}/apache/0.default.conf".format(public.get_vhost_path()) self.create_default_cert() return public.writeFile(apa_default_conf_file, default_conf_body) def write_apa_default_conf(self): ''' @name 写apache默认配置文件 @author hwliang<2022-01-14> @return bool ''' port = '80' if public.get_multi_webservice_status(): port = '8290' default_conf_body = f''' ServerAdmin webmaster@example.com DocumentRoot "/www/server/apache/htdocs" ServerName bt.default.com SetOutputFilter DEFLATE Options FollowSymLinks AllowOverride All Order allow,deny Allow from all DirectoryIndex index.html ''' apa_default_conf_file = "{}/apache/0.default.conf".format(public.get_vhost_path()) return public.writeFile(apa_default_conf_file, default_conf_body) def set_https_mode(self, get=None): ''' @name 设置https模式 @author hwliang<2022-01-14> @return dict ''' web_server = public.get_webserver() if web_server not in ['nginx', 'apache']: return public.return_msg_gettext(False, public.lang("This function only supports Nginx/Apache")) ngx_default_conf_file = "{}/nginx/0.default.conf".format(public.get_vhost_path()) apa_default_conf_file = "{}/apache/0.default.conf".format(public.get_vhost_path()) ngx_default_conf = public.readFile(ngx_default_conf_file) apa_default_conf = public.readFile(apa_default_conf_file) status = False if ngx_default_conf: if ngx_default_conf.find('DEFAULT SSL CONFIG') != -1: status = False self.write_ngx_default_conf() self.write_apa_default_conf() else: status = True self.write_ngx_default_conf_by_ssl() self.write_apa_default_conf_by_ssl() else: status = True self.write_ngx_default_conf_by_ssl() self.write_apa_default_conf_by_ssl() public.serviceReload() status_msg = {True: 'Open', False: 'Close'} msg = public.gettext_msg('Has {} HTTPS strict mode',(status_msg[status],)) public.write_log_gettext('WebSite manager', msg) return public.return_msg_gettext(True, msg) def create_default_cert(self): ''' @name 创建默认SSL证书 @author hwliang<2022-01-14> @return bool ''' cert_pem = '/www/server/panel/vhost/cert/0.default/fullchain.pem' cert_key = '/www/server/panel/vhost/cert/0.default/privkey.pem' if os.path.exists(cert_pem) and os.path.exists(cert_key): return True cert_path = os.path.dirname(cert_pem) if not os.path.exists(cert_path): os.makedirs(cert_path) import OpenSSL key = OpenSSL.crypto.PKey() key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048) cert = OpenSSL.crypto.X509() cert.set_serial_number(0) # cert.get_subject().CN = '' cert.set_issuer(cert.get_subject()) cert.gmtime_adj_notBefore(0) cert.gmtime_adj_notAfter(86400 * 3650) cert.set_pubkey(key) cert.sign(key, 'md5') cert_ca = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) private_key = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key) if len(cert_ca) > 100 and len(private_key) > 100: public.writeFile(cert_pem, cert_ca, 'wb+') public.writeFile(cert_key, private_key, 'wb+') return True return False def get_upload_ssl_list(self, get): """ @获取上传证书列表 @siteName string 网站名称 """ siteName = get['siteName'] path = '{}/vhost/upload_ssl/{}'.format(public.get_panel_path(), siteName) if not os.path.exists(path): os.makedirs(path) res = [] for filename in os.listdir(path): try: filename = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(int(filename))) res.append(filename) except: pass return res # 获取指定证书基本信息 def get_cert_init(self, cert_data, ssl_info=None): """ @获取指定证书基本信息 @cert_data string 证书数据 @ssl_info dict 证书信息 """ try: result = {} if ssl_info and ssl_info['ssl_type'] == 'pfx': x509 = self.__check_pfx_pwd(cert_data, ssl_info['pwd'])[0] else: x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_data) # 取产品名称 issuer = x509.get_issuer() result['issuer'] = '' if hasattr(issuer, 'CN'): result['issuer'] = issuer.CN if not result['issuer']: is_key = [b'0', '0'] issue_comp = issuer.get_components() if len(issue_comp) == 1: is_key = [b'CN', 'CN'] for iss in issue_comp: if iss[0] in is_key: result['issuer'] = iss[1].decode() break # 取到期时间 result['notAfter'] = self.strf_date( bytes.decode(x509.get_notAfter())[:-1]) # 取申请时间 result['notBefore'] = self.strf_date( bytes.decode(x509.get_notBefore())[:-1]) # 取可选名称 result['dns'] = [] for i in range(x509.get_extension_count()): s_name = x509.get_extension(i) if s_name.get_short_name() in [b'subjectAltName', 'subjectAltName']: s_dns = str(s_name).split(',') for d in s_dns: result['dns'].append(d.split(':')[1]) subject = x509.get_subject().get_components() # 取主要认证名称 if len(subject) == 1: result['subject'] = subject[0][1].decode() else: if len(result['dns']) > 0: result['subject'] = result['dns'][0] else: result['subject'] = ''; return result except: return False def strf_date(self, sdate): """ @转换证书时间 """ return time.strftime('%Y-%m-%d', time.strptime(sdate, '%Y%m%d%H%M%S')) def check_ssl_endtime(self, data, ssl_info=None): """ @检查证书是否有效(证书最高有效期不超过1年) @data string 证书数据 @ssl_info dict 证书信息 """ info = self.get_cert_init(data, ssl_info) if info: end_time = time.mktime(time.strptime(info['notAfter'], "%Y-%m-%d")) start_time = time.mktime(time.strptime(info['notBefore'], "%Y-%m-%d")) days = int((end_time - start_time) / 86400) if days < 400: # 1年有效期+1个月续签时间 return data return False # 证书转为pkcs12 def dump_pkcs12(self, key_pem=None, cert_pem=None, ca_pem=None, friendly_name=None): """ @证书转为pkcs12 @key_pem string 私钥数据 @cert_pem string 证书数据 @ca_pem string 可选的CA证书数据 @friendly_name string 可选的证书名称 """ p12 = OpenSSL.crypto.PKCS12() if cert_pem: x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_pem.encode()) p12.set_certificate(x509) if key_pem: p12.set_privatekey(OpenSSL.crypto.load_privatekey( OpenSSL.crypto.FILETYPE_PEM, key_pem.encode())) if ca_pem: p12.set_ca_certificates((OpenSSL.crypto.load_certificate( OpenSSL.crypto.FILETYPE_PEM, ca_pem.encode()),)) if friendly_name: p12.set_friendlyname(friendly_name.encode()) return p12 def download_cert(self, get): """ @下载证书 @get dict 请求参数 siteName string 网站名称 ssl_type string 证书类型 key string 密钥 pem string 证书数据 pwd string 证书密码 """ pem = get['pem'] siteName = get['siteName'] ssl_type = get['ssl_type'] rpath = '{}/temp/ssl/'.format(public.get_panel_path()) if os.path.exists(rpath): shutil.rmtree(rpath) ca_list = [] path = '{}/{}_{}'.format(rpath, siteName, int(time.time())) if ssl_type == 'pfx': res = self.__check_pfx_pwd(base64.b64decode(pem), get['pwd']) p12 = res[1]; x509 = res[0]; get['pwd'] = res[2] print(get['pwd']) ca_list = [] for x in p12.get_ca_certificates(): ca_list.insert(0, OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, x).decode().strip()) ca_cert = '\n'.join(ca_list) key = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, p12.get_privatekey()).decode().strip() domain_cert = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, x509).decode().strip() else: key = get['key'] domain_cert = pem.split('-----END CERTIFICATE-----')[0] + "-----END CERTIFICATE-----\n" ca_cert = pem.replace(domain_cert, '') p12 = self.dump_pkcs12(key, '{}\n{}'.format(domain_cert.strip(), ca_cert), ca_cert) for x in ['IIS', 'Apache', 'Nginx', 'Other']: d_file = '{}/{}'.format(path, x) if not os.path.exists(d_file): os.makedirs(d_file) if x == 'IIS': public.writeFile2(d_file + '/fullchain.pfx', p12.export(), 'wb+') public.writeFile(d_file + '/password.txt', get['pwd']) elif x == 'Apache': public.writeFile(d_file + '/privkey.key', key) public.writeFile(d_file + '/root_bundle.crt', ca_cert) public.writeFile(d_file + '/domain.crt', domain_cert) else: public.writeFile(d_file + '/privkey.key', key) public.writeFile(d_file + '/fullchain.pem', '{}\n{}'.format(domain_cert.strip(), ca_cert)) flist = [] public.get_file_list(path, flist) zfile = '{}/{}.zip'.format(rpath, os.path.basename(path)) import zipfile f = zipfile.ZipFile(zfile, 'w', zipfile.ZIP_DEFLATED) for item in flist: s_path = item.replace(path, '') if s_path: f.write(item, s_path) f.close() return public.returnMsg(True, zfile); def check_ssl_info(self, get): """ @解析证书信息 @get dict 请求参数 path string 上传文件路径 """ path = get['path'] if not os.path.exists(path): return public.returnMsg(False, public.lang("Query failed , does not exist address")) info = {'root': '', 'cert': '', 'pem': '', 'key': ''} ssl_info = {'pwd': None, 'ssl_type': None} for filename in os.listdir(path): filepath = '{}/{}'.format(path, filename) ext = filename[-4:] if ext == '.pfx': ssl_info['ssl_type'] = 'pfx' f = open(filepath, 'rb') # pfx为二进制文件 info['pem'] = f.read() else: data = public.readFile(filepath) if filename.find('password') >= 0: # 取pfx密码 ssl_info['pwd'] = re.search('([a-zA-Z0-9]+)', data).groups()[0] continue if len(data) < 1024: continue if data.find('PRIVATE KEY') >= 0: info['key'] = data # 取key if ext == '.pem': if self.check_ssl_endtime(data): info['pem'] = data else: if data.find('BEGIN CERTIFICATE') >= 0: if not info['root']: info['root'] = data else: info['cert'] = data if ssl_info['ssl_type'] == 'pfx': info['pem'] = self.check_ssl_endtime(info['pem'], ssl_info) if info['pem']: info['pem'] = base64.b64encode(info['pem']) info['key'] = True else: if not info['pem']: # 确认ca证书和域名证书顺序 info['pem'] = self.check_ssl_endtime(info['root'] + "\n" + info['cert'], ssl_info) if not info['pem']: info['pem'] = self.check_ssl_endtime(info['cert'] + "\n" + info['root'], ssl_info) if info['key'] and info['pem']: return {'key': info['key'], 'pem': info['pem'], 'ssl_type': ssl_info['ssl_type'], 'pwd': ssl_info['pwd']} return False def __check_pfx_pwd(self, data, pwd): """ @检测pfx证书密码 @data string pfx证书内容 @pwd string 密码 """ try: p12 = OpenSSL.crypto.load_pkcs12(data, pwd) x509 = p12.get_certificate() except: pwd = re.search('([a-zA-Z0-9]+)', pwd).groups()[0] p12 = OpenSSL.crypto.load_pkcs12(data, pwd) x509 = p12.get_certificate() return [x509, p12, pwd] def auto_restart_rph(self,get): #设置申请或续签SSL时自动停止反向代理、重定向、http to https,申请完成后自动开启 conf_file = '{}/data/stop_rp_when_renew_ssl.pl'.format(public.get_panel_path()) conf = public.readFile(conf_file) if not conf: public.writeFile(conf_file,json.dumps([get.sitename])) try: conf = json.loads(conf) if get.sitename not in conf: conf.append(get.sitename) public.writeFile(conf_file,json.dumps(conf)) except: return public.returnMsg(True, public.lang("Error parsing configuration file")) return public.returnMsg(True, public.lang("Setup successfully")) def remove_auto_restart_rph(self,get): #设置申请或续签SSL时自动停止反向代理、重定向、http to https,申请完成后自动开启 conf_file = '{}/data/stop_rp_when_renew_ssl.pl'.format(public.get_panel_path()) conf = public.readFile(conf_file) if not conf: return public.returnMsg(False, public.lang("Website [proxy,redirect,http to https] are not set to restart automatically")) try: conf = json.loads(conf) conf.remove(get.sitename) public.writeFile(conf_file,json.dumps(conf)) except: return public.returnMsg(False, public.lang("Configuration file parsing error")) return public.returnMsg(True, public.lang("Setup successfully")) def get_auto_restart_rph(self,get): #设置申请或续签SSL时自动停止反向代理、重定向、http to https,申请完成后自动开启 conf_file = '{}/data/stop_rp_when_renew_ssl.pl'.format(public.get_panel_path()) conf = public.readFile(conf_file) if not conf: return public.returnMsg(False, public.lang("Website [proxy,redirect,http to https] are not set to restart automatically")) try: conf = json.loads(conf) if get.sitename in conf: return public.returnMsg(True, public.lang("This website has turn on [proxy,redirect,http to https] auto restart")) return public.returnMsg(False, public.lang("Website has turn off auto restart")) except: return public.returnMsg(False, public.lang("Configuration file parsing error")) def reset_wp_password(self,get): return one_key_wp.one_key_wp().reset_wp_password(get) def is_update (self,get): return one_key_wp.one_key_wp().is_update(get) def purge_all_cache(self,get): return one_key_wp.one_key_wp().purge_all_cache(get) def set_fastcgi_cache(self,get): return one_key_wp.one_key_wp().set_fastcgi_cache(get) def update_wp(self,get): return one_key_wp.one_key_wp().update_wp(get) def get_language(self,get): return one_key_wp.one_key_wp().get_language(get) def deploy_wp(self,get): return one_key_wp.one_key_wp().deploy_wp(get) def get_wp_username(self,get): return one_key_wp.one_key_wp().get_wp_username(get) def reset_wp_db(self,get): return one_key_wp.one_key_wp().reset_wp_db(get) @staticmethod def test_domains_api(get): try: domains = json.loads(get.domains.strip()) except (json.JSONDecodeError, AttributeError, KeyError): return public.returnMsg(False, public.lang("parameter error")) try: from panelDnsapi import DnsMager public.print_log("开始测试域名解析---- {}") # public.print_log("开始测试域名解析---- {}".format(domains[0])) return DnsMager().test_domains_api(domains) except: pass @staticmethod def get_ssl_protocol(get): """ 获取全局TLS版本 @author baozi <202-04-18> @param: @return """ protocols = { "TLSv1": False, "TLSv1.1": True, "TLSv1.2": True, "TLSv1.3": False, } file_path = public.get_panel_path() + "/data/ssl_protocol.json" if os.path.exists(file_path): data = public.readFile(file_path) if data is not False: protocols = json.loads(data) return protocols return protocols def site_rname(self, get): try: if not (hasattr(get, "id") and hasattr(get, "rname")): return public.returnMsg(False, public.lang("parameter error")) id = get.id rname = get.rname data = public.M('sites').where("id=?", (id,)).select() if not data: return public.returnMsg(False, public.lang("The site does not exist!")) data = data[0] name = data['rname'] if 'rname' in data.keys() and data.get('rname', '') else data['name'] if 'rname' not in data.keys(): public.M('sites').execute("ALTER TABLE 'sites' ADD 'rname' text DEFAULT ''", ()) public.M('sites').where('id=?', data['id']).update({'rname': rname}) # public.write_log_gettext('Site manager', 'Website [{}] renamed: [{}]'.format(name, rname)) return public.returnMsg(True, public.lang("Website [{}] renamed: [{}]", name, rname)) except: import traceback return public.returnMsg(False, traceback.format_exc())