Files
aaPanel/class/panelSite.py
T
2019-06-20 16:29:37 +08:00

3684 lines
153 KiB
Python

#coding: utf-8
#-------------------------------------------------------------------
# 宝塔Linux面板
#-------------------------------------------------------------------
# Copyright (c) 2015-2017 宝塔软件(http:#bt.cn) All rights reserved.
#-------------------------------------------------------------------
# Author: 黄文良 <287962566@qq.com>
#-------------------------------------------------------------------
#------------------------------
# 网站管理类
#------------------------------
import io,re,public,os,sys,shutil,json,hashlib,socket,time
from BTPanel import session
from panelRedirect import panelRedirect
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 = '/www/server';
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'):
os.system('mkdir -p ' + path);
os.system('wget -O ' + path + '/index.html '+public.get_url()+'/stop_en.html &');
self.__proxyfile = '/www/server/panel/data/proxyfile.json'
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')
#默认配置文件
def check_default(self):
nginx = self.setupPath + '/panel/vhost/nginx'
httpd = self.setupPath + '/panel/vhost/apache'
httpd_default = '''<VirtualHost *:80>
ServerAdmin webmaster@example.com
DocumentRoot "/www/server/apache/htdocs"
ServerName bt.default.com
<Directory "/www/server/apache/htdocs">
SetOutputFilter DEFLATE
Options FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
DirectoryIndex index.html
</Directory>
</VirtualHost>'''
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):
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 = "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]
listen_ipv6 = ''
if self.is_ipv6: listen_ipv6 = "\nListen [::]:" + port
allConf = allConf.replace(listen,listen + "\nListen " + port + listen_ipv6)
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
<FilesMatch \\.php$>
SetHandler "proxy:unix:/tmp/php-cgi-%s.sock|fcgi://localhost"
</FilesMatch>
''' % (self.phpVersion,)
apaOpt = 'Require all granted';
conf='''%s<VirtualHost *:%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
<Files ~ (\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md)$>
Order allow,deny
Deny from all
</Files>
%s
#PATH
<Directory "%s">
SetOutputFilter DEFLATE
Options FollowSymLinks
AllowOverride All
%s
DirectoryIndex index.php index.html index.htm default.php default.html default.htm
</Directory>
</VirtualHost>''' % (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 755 ' + 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='''server
{
listen %s;%s
server_name %s;
index index.php index.html index.htm default.php default.htm default.html;
root %s;
#SSL-START %s
#error_page 404/404.html;
#SSL-END
#ERROR-PAGE-START %s
error_page 404 /404.html;
error_page 502 /502.html;
#ERROR-PAGE-END
#PHP-INFO-START %s
include enable-php-%s.conf;
#PHP-INFO-END
#REWRITE-START %s
include %s/panel/vhost/rewrite/%s.conf;
#REWRITE-END
%s
location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md)
{
return 404;
}
%s
location ~ \.well-known{
allow all;
}
location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 30d;
error_log off;
access_log /dev/null;
}
location ~ .*\\.(js|css)?$
{
expires 12h;
error_log off;
access_log /dev/null;
}
access_log %s.log;
error_log %s.error.log;
}''' % (self.sitePort,listen_ipv6,self.siteName,self.sitePath,public.getMsg('NGINX_CONF_MSG1'),public.getMsg('NGINX_CONF_MSG2'),public.getMsg('NGINX_CONF_MSG3'),self.phpVersion,public.getMsg('NGINX_CONF_MSG4'),self.setupPath,self.siteName,public.GetMsg("NGINX_CONF_BAN_FILE_DIR"),public.GetMsg("NGINX_CONF_ONECLICK_SET_SSL_DIR"),public.GetConfigValue('logs_path')+'/'+self.siteName,public.GetConfigValue('logs_path')+'/'+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();
return True;
#添加站点
def AddSite(self,get):
self.check_default()
isError = public.checkWebConfig()
if isError != True:
return public.returnMsg(False,'ERROR: %s<br><br><a style="color:red;">' % public.GetMsg("GET_ERR_IN_CONFILE")+isError.replace("\n",'<br>')+'</a>');
import json,files
get.path = self.__get_site_format_path(get.path)
siteMenu = json.loads(get.webname)
self.siteName = self.ToPunycode(siteMenu['domain'].strip().split(':')[0]).strip().lower();
self.sitePath = self.ToPunycodePath(self.GetPath(get.path.replace(' ','')));
self.sitePort = get.port.strip().replace(' ','');
if self.sitePort == "": get.port = "80";
if not public.checkPort(self.sitePort): return public.returnMsg(False,'SITE_ADD_ERR_PORT');
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.returnMsg(False,'PHP_NOT_EXISTS')
domain = None
#if siteMenu['count']:
# domain = get.domain.replace(' ','')
#表单验证
if not files.files().CheckDir(self.sitePath) or not self.__check_site_path(self.sitePath): return public.returnMsg(False,'PATH_ERROR');
if len(self.phpVersion) < 2: return public.returnMsg(False,'SITE_ADD_ERR_PHPEMPTY');
reg = "^([\w\-\*]{1,100}\.){1,4}([\w\-]{1,24}|[\w\-]{1,24}\.[\w\-]{1,24})$";
if not re.match(reg, self.siteName): return public.returnMsg(False,'SITE_ADD_ERR_DOMAIN');
if self.siteName.find('*') != -1: return public.returnMsg(False,'SITE_ADD_ERR_DOMAIN_TOW');
if not domain: domain = self.siteName;
#是否重复
sql = public.M('sites');
if sql.where("name=?",(self.siteName,)).count(): return public.returnMsg(False,'SITE_ADD_ERR_EXISTS');
opid = public.M('domain').where("name=?",(self.siteName,)).getField('pid');
if opid:
if public.M('sites').where('id=?',(opid,)).count():
return public.returnMsg(False,'SITE_ADD_ERR_DOMAIN_EXISTS');
public.M('domain').where('pid=?',(opid,)).delete();
if public.M('binding').where('domain=?',(self.siteName,)).count():
return public.returnMsg(False,'SITE_ADD_ERR_DOMAIN_EXISTS');
#创建根目录
if not os.path.exists(self.sitePath):
os.makedirs(self.sitePath)
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/:/proc/');
public.ExecShell('chmod 644 ' + userIni);
public.ExecShell('chown root:root ' + userIni);
public.ExecShell('chattr +i '+userIni);
#创建默认文档
index = self.sitePath+'/index.html'
if not os.path.exists(index):
public.writeFile(index, public.readFile('data/defaultDoc.html'))
public.ExecShell('chmod -R 755 ' + 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 755 ' + doc404);
public.ExecShell('chown -R www:www ' + doc404);
#写入配置
result = self.nginxAdd()
result = self.apacheAdd()
#检查处理结果
if not result: return public.returnMsg(False,'SITE_ADD_ERR_WRITE');
ps = 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
#写入数据库
get.pid = sql.table('sites').add('name,path,status,ps,type_id,addtime',(self.siteName,self.sitePath,'1',ps,get.type_id,public.getDate()))
#添加更多域名
for domain in siteMenu['domainlist']:
get.domain = domain
get.webname = self.siteName
get.id = str(get.pid)
self.AddDomain(get)
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 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 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
public.serviceReload()
public.WriteLog('TYPE_SITE','SITE_ADD_SUCCESS',(self.siteName,))
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 DeleteSite(self,get):
proxyconf = self.__read_config(self.__proxyfile)
id = get.id;
if public.M('sites').where('id=?',(id,)).count() < 1: return public.returnMsg(False,'SITE_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)
#删除重定向
__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)
#删除伪静态文件
filename = confPath+'/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): files.files().DeleteDir(get)
#重载配置
public.serviceReload();
#从数据库删除
public.M('sites').where("id=?",(id,)).delete();
public.M('binding').where("pid=?",(id,)).delete();
public.M('domain').where("pid=?",(id,)).delete();
public.WriteLog('TYPE_SITE', "SITE_DEL_SUCCESS",(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.returnMsg(True,'SITE_DEL_SUCCESS')
#域名编码转换
def ToPunycode(self,domain):
import re;
if sys.version_info[0] == 2: domain = domain.encode('utf8');
tmp = domain.split('.');
newdomain = '';
for dkey in tmp:
#匹配非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') + '.'
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 AddDomain(self,get):
#检查配置文件
isError = public.checkWebConfig()
if isError != True:
return public.returnMsg(False,'ERROR: %s<br><br><a style="color:red;">' % public.GetMsg("GET_ERR_IN_CONFILE") +isError.replace("\n",'<br>')+'</a>');
if len(get.domain) < 3: return public.returnMsg(False,'SITE_ADD_DOMAIN_ERR_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'
reg = "^([\w\-\*]{1,100}\.){1,4}([\w\-]{1,24}|[\w\-]{1,24}\.[\w\-]{1,24})$";
if not re.match(reg, get.domain): return public.returnMsg(False,'SITE_ADD_DOMAIN_ERR_FORMAT');
if len(domain) == 2: get.port = domain[1];
if get.port == "": get.port = "80";
if not public.checkPort(get.port): return public.returnMsg(False,'SITE_ADD_DOMAIN_ERR_POER');
#检查域名是否存在
sql = public.M('domain');
opid = sql.where("name=? AND (port=? OR pid=?)",(get.domain,get.port,get.id)).getField('pid');
if opid:
if public.M('sites').where('id=?',(opid,)).count():
return public.returnMsg(False,'SITE_ADD_DOMAIN_ERR_EXISTS');
sql.where('pid=?',(opid,)).delete();
if public.M('binding').where('domain=?',(get.domain,)).count():
return public.returnMsg(False,'SITE_ADD_ERR_DOMAIN_EXISTS');
#写配置文件
self.NginxDomain(get)
try:
self.ApacheDomain(get)
except:
pass;
#添加放行端口
if get.port != '80':
import firewalls
get.ps = get.domain;
firewalls.firewalls().AddAcceptPort(get);
public.serviceReload();
public.WriteLog('TYPE_SITE', 'DOMAIN_ADD_SUCCESS',(get.webname,get.domain));
sql.table('domain').add('pid,name,port,addtime',(get.id,get.domain,get.port,public.getDate()));
return public.returnMsg(True,'SITE_ADD_DOMAIN');
#Nginx写域名配置
def NginxDomain(self,get):
file = self.setupPath + '/panel/vhost/nginx/'+get.webname+'.conf';
conf = public.readFile(file);
if not conf: return;
#添加域名
rep = "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 = "listen\s+([0-9]+)\s*[default_server]*\s*;";
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\tlisten [::]:"+get.port+';'
conf = conf.replace(listen,listen + "\n\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('<VirtualHost *:'+port+'>') != -1:
repV = "<VirtualHost\s+\*\:"+port+">(.|\n)*</VirtualHost>";
domainV = re.search(repV,conf).group()
rep = "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('<VirtualHost *:443>') != -1:
repV = "<VirtualHost\s+\*\:443>(.|\n)*</VirtualHost>";
domainV = re.search(repV,conf).group()
rep = "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 = "php-cgi-([0-9]{2,3})\.sock";
version = re.search(rep,conf).groups()[0]
if len(version) < 2: return public.returnMsg(False,'PHP_GET_ERR')
phpConfig ='''
#PHP
<FilesMatch \\.php$>
SetHandler "proxy:unix:/tmp/php-cgi-%s.sock|fcgi://localhost"
</FilesMatch>
''' % (version,);
apaOpt = 'Require all granted';
newconf='''<VirtualHost *:%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
%s
#DENY FILES
<Files ~ (\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md)$>
Order allow,deny
Deny from all
</Files>
#PATH
<Directory "%s">
SetOutputFilter DEFLATE
Options FollowSymLinks
AllowOverride All
%s
DirectoryIndex %s
</Directory>
</VirtualHost>''' % (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 DelDomain(self,get):
sql = public.M('domain');
id=get['id'];
port = get.port;
find = sql.where("pid=? AND name=?",(get.id,get.domain)).field('id,name').find();
domain_count = sql.table('domain').where("pid=?",(id,)).count();
if domain_count == 1: return public.returnMsg(False,'SITE_DEL_DOMAIN_ERR_ONLY');
#nginx
file = self.setupPath+'/panel/vhost/nginx/'+get['webname']+'.conf';
conf = public.readFile(file);
if conf:
#删除域名
rep = "server_name\s+(.+);";
tmp = re.search(rep,conf).group()
newServerName = tmp.replace(' '+get['domain']+';',';');
newServerName = newServerName.replace(' '+get['domain']+' ',' ');
conf = conf.replace(tmp,newServerName);
#删除端口
rep = "listen\s+([0-9]+);";
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 = "\n*\s+listen\s+"+port+";";
conf = re.sub(rep,'',conf);
rep = "\n*\s+listen\s+\[::\]:"+port+";";
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 = "\n*<VirtualHost \*\:" + port + ">(.|\n)*</VirtualHost>";
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 = "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)
except:
pass;
sql.table('domain').where("id=?",(find['id'],)).delete();
public.WriteLog('TYPE_SITE', 'DOMAIN_DEL_SUCCESS',(get.webname,get.domain));
public.serviceReload();
return public.returnMsg(True,'DEL_SUCCESS');
#检查域名是否解析
def CheckDomainPing(self,get):
try:
epass = public.GetRandomString(32);
spath = get.path + '/.well-known/pki-validation';
if not os.path.exists(spath): os.system("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 SetSSL(self,get):
#type = get.type;
siteName = get.siteName;
path = '/www/server/panel/vhost/cert/' + siteName;
if not os.path.exists(path):
public.ExecShell('mkdir -p ' + path)
csrpath = path+"/fullchain.pem"; #生成证书路径
keypath = path+"/privkey.pem"; #密钥文件路径
if(get.key.find('KEY') == -1): return public.returnMsg(False, 'SITE_SSL_ERR_PRIVATE');
if(get.csr.find('CERTIFICATE') == -1): return public.returnMsg(False, 'SITE_SSL_ERR_CERT');
public.writeFile('/tmp/cert.pl',get.csr);
if not public.CheckCert('/tmp/cert.pl'): return public.returnMsg(False,public.GetMsg("PEM_FORMAT_ERR"));
public.ExecShell('\\cp -a '+keypath+' /tmp/backup1.conf');
public.ExecShell('\\cp -a '+csrpath+' /tmp/backup2.conf');
#清理旧的证书链
if os.path.exists(path+'/README'):
public.ExecShell('rm -rf ' + path);
public.ExecShell('rm -rf ' + path + '-00*');
public.ExecShell('rm -rf /etc/letsencrypt/archive/' + get.siteName);
public.ExecShell('rm -rf /etc/letsencrypt/archive/' + get.siteName + '-00*');
public.ExecShell('rm -f /etc/letsencrypt/renewal/' + get.siteName + '.conf');
public.ExecShell('rm -f /etc/letsencrypt/renewal/' + get.siteName + '-00*.conf');
public.ExecShell('rm -f /etc/letsencrypt/live/' + get.siteName + '/README')
public.ExecShell('rm -f ' + path + '/README');
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):
public.ExecShell('\\cp -a /tmp/backup1.conf ' + keypath);
public.ExecShell('\\cp -a /tmp/backup2.conf ' + csrpath);
return public.returnMsg(False, 'ERROR: <br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>');
public.serviceReload();
if os.path.exists(path + '/partnerOrderId'): os.system('rm -f ' + path + '/partnerOrderId');
p_file = '/etc/letsencrypt/live/' + get.siteName + '/partnerOrderId'
if os.path.exists(p_file): os.system('rm -f ' + p_file);
public.WriteLog('TYPE_SITE', 'SITE_SSL_SAVE_SUCCESS');
return public.returnMsg(True, 'SITE_SSL_SUCCESS');
#获取运行目录
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);
return result['runPath'];
# 创建Let's Encrypt免费证书
def CreateLet(self, get):
import time
# 确定主域名顺序
domains = json.loads(get.domains)
domainsTmp = []
if get.siteName in domains: domainsTmp.append(get.siteName);
Wildcard_domain = ''
for domainTmp in domains:
if domainTmp.startswith("*."):
Wildcard_domain = domainTmp
if domainTmp == get.siteName: continue;
domainsTmp.append(domainTmp);
domains = domainsTmp;
if not len(domains): return public.returnMsg(False, public.GetMsg("CHOOSE_DOMAIN"));
get.first_domain = domains[0].replace("*.", '')
if len(domains) > 1 and Wildcard_domain:
for dom in domains:
if "*." in dom: continue
if len(dom.split(".")) == 2: continue
if Wildcard_domain.replace("*.", "") == dom.split(".")[-2] + "." + dom.split(".")[-1]:
return public.returnMsg(False, public.GetMsg("ACCEPT_SSL_ERR"));
# 定义证书目录
path = os.path.join('/www/server/panel/vhost/cert/', get.first_domain);
path = path.replace("*.", '')
if os.path.isdir(path):
public.ExecShell("rm -rf {}".format(path))
public.ExecShell("mkdir -p {}".format(path))
csrpath = os.path.join(path, "fullchain.pem"); # 生成证书路径
keypath = os.path.join(path, "privkey.pem"); # 密钥文件路径
# 准备基础信息
actionstr = get.updateOf
siteInfo = public.M('sites').where('name=?', (get.siteName,)).field('id,name,path').find();
runPath = self.GetRunPath(get);
srcPath = siteInfo['path'];
if runPath != False and runPath != '/': siteInfo['path'] += runPath;
get.path = siteInfo['path'];
email = public.M('users').getField('email');
if hasattr(get, 'email'):
if get.email.strip() != '':
public.M('users').setField('email', get.email);
email = get.email;
if "@" not in email: email = ''
force = False;
dns = False
dns_plu = False
crontab = ''
renew = False
file_auth = False
if not hasattr(get, "dnsapi"):
file_auth = True
if hasattr(get, 'force'):
if get.force == 'true':
force = True;
if hasattr(get, 'renew'): # 验证手动的dns txt解析
file_auth = False
renew = True
public.ExecShell('''cd {} && rm -rf account_key.key fullchain.pem privkey.pem Confirmation_verification domain_txt_dns_value.json '''.format(path))
result = [" ", " "]
public.WriteFile(os.path.join(path, "Confirmation_verification"), "ok", mode="w")
num = 0
while True:
num += 1
if os.path.isfile(csrpath) and os.path.isfile(keypath):
break
else:
data = {};
data['err'] = result;
data['out'] = result
if os.path.isfile(os.path.join(path, "check_authorization_status_response")):
result[1] = public.ReadFile(os.path.join(path, "check_authorization_status_response"))
public.ExecShell("cd {} && rm -rf check_authorization_status_response".format(path))
public.WriteFile(os.path.join(path, "timeout_info"), "", mode="w")
data['msg'] = public.GetMsg("CHECK_TXT_ERR1");
data['status'] = False;
return data
elif os.path.isfile(os.path.join(path, "timeout_info")) or num > 60:
result[1] = public.GetMsg("CHECK_TXT_ERR2")
data['msg'] = public.GetMsg("CHECK_TXT_ERR3")
data['status'] = False;
return data
elif not public.ExecShell('''ps aux|grep -v grep|grep sewer_Usage''')[0]:
return public.returnMsg(False, public.GetMsg("CHECK_TXT_ERR4"))
time.sleep(5)
else:
if not file_auth:
dnssleep = get.dnssleep
dnsapi = get.dnsapi
else:
dnssleep = 10
dnsapi = ''
public.ExecShell(
'''cd {} && rm -rf account_key.key fullchain.pem privkey.pem check_authorization_status_response Confirmation_verification domain_txt_dns_value.json apply_for_cert_issuance_response timeout_info'''.format(
path))
json_parem = {"dnsapi": dnsapi, "domain_alt_names": "", "contact_email": email, "dnssleep": dnssleep, "key": "", "secret": "", "path": path}
if hasattr(get, 'dnsapi'):
if get.dnsapi == 'dns':
json_parem['dnssleep'] = get.dnssleep
dns = True
else:
if not self.Check_DnsApi(get.dnsapi): return public.returnMsg(False, public.GetMsg("SET_API_FIRST"));
if get.dnsapi == 'dns_bt':
c_file = '/www/server/panel/plugin/dns/dns_main.py';
if not os.path.exists(c_file): return public.returnMsg(False,'INSTALL_CLOUDDNS_FIRST');
c_conf = public.readFile(c_file)
if c_conf.find('add_txt') == -1:
os.system('wget -O ' + c_file + ' http://download.bt.cn/install/plugin/dns/dns_main.py -T 5')
sys.path.append('/www/server/panel/plugin/dns')
import dns_main
dns_plu = dns_main.dns_main()
else:
dns_api_list = self.GetDnsApi(get)
for i in dns_api_list:
if i['name'] == get.dnsapi:
json_parem['key'] = i['data'][0]['value']
json_parem['secret'] = i['data'][1]['value']
# 构造参数
domainCount = 0
errorDomain = "";
errorDns = "";
done = '';
dns_type = hasattr(get, 'dnsapi')
for domain in domains:
if public.checkIp(domain): continue;
if not dns_type:
if domain.find('*.') != -1:
if not renew:
return public.returnMsg(False, public.GetMsg("CHECK_SSL_ERR"));
get.domain = domain;
if public.M('domain').where('name=?', (domain,)).count():
p = siteInfo['path'];
else:
p = public.M('binding').where('domain=?', (domain,)).getField('path');
get.path = p;
if force:
if not self.CheckDomainPing(get): errorDomain += '<li>' + domain + '</li>';
if dns_plu:
domainId, key = dns_plu.get_domainid_byfull('test.' + domain)
if not domainId: errorDns += '<li>' + domain + '</li>';
if p != done:
done = p;
domainCount += 1
if errorDomain: return public.returnMsg(False, 'SITE_SSL_ERR_DNS', ('<span style="color:red;"><br>' + errorDomain + '</span>',));
# 获取域名数据
if domainCount == 0: return public.returnMsg(False, 'SITE_SSL_ERR_EMPTY')
# 检查是否自定义证书
partnerOrderId = path + '/partnerOrderId';
if os.path.exists(partnerOrderId):
os.remove(partnerOrderId)
#检查依赖
self.check_ssl_pack()
# dns调用脚本获取ssl证书
if dns_type:
json_parem['domain_alt_names'] = ",".join(domains)
if dns:
result = public.ExecShell('''nohup python /www/server/panel/class/sewer_Usage.py '{}' > sewer_Usage.log 2>&1 & '''.format(json.dumps(json_parem)))
else:
shell_str = '''python /www/server/panel/class/sewer_Usage.py '{}' '''.format(json.dumps(json_parem))
result = public.ExecShell(shell_str);
crontab = shell_str
if dns: # 返回txt手动解析信息
while True:
if os.path.isfile(os.path.join(path, "domain_txt_dns_value.json")):
txt_domain_value_li = json.loads(public.ReadFile(os.path.join(path, "domain_txt_dns_value.json")))
break
elif os.path.isfile(os.path.join(path, "apply_for_cert_issuance_response")):
data = {};
data['err'] = ["", public.ReadFile(os.path.join(path, "apply_for_cert_issuance_response"))]
data['out'] = data['err'][1]
data['result'] = json.loads(data['err'][1]);
if data['result']['status'] == 429:
data['msg'] = public.GetMsg("ACCEPT_SSL_ERR1")
if data['result']['status'] == 400:
data['msg'] = public.GetMsg("ACCEPT_SSL_ERR2")
data['status'] = False;
return data
time.sleep(5)
try:
data = {}
data['err'] = result;
data['out'] = result[0];
data['status'] = True
data['fullDomain'] = []
data['txtValue'] = []
data['msg'] = public.GetMsg("RESOLVE_DOMAIN_BYF")
for i in txt_domain_value_li:
data['fullDomain'].append('_acme-challenge.' + i['dns_name'].replace('*.',''))
data['txtValue'].append(i['acme_txt_value'])
return data
except:
data = {};
data['err'] = result;
data['out'] = result[0];
data['msg'] = public.GetMsg("GET_FAIL")
data['result'] = {};
return data
# 判断是否获取成功
if not os.path.exists(csrpath) and not os.path.exists(keypath) and dns_type:
data = {};
data['err'] = result;
data['out'] = result[0];
try:
msg = json.loads(re.search("{.+}", result[1]).group())['data']
data['msg'] = "<h2>签发失败," + msg + "</h2>"
except Exception:
data['msg'] = public.GetMsg("ISSUANCE_FAIL")
data['result'] = {};
if os.path.isfile(os.path.join(path, "check_authorization_status_response")):
data['result'] = json.loads(public.ReadFile(os.path.join(path, "check_authorization_status_response")));
if data['result']['status'] == "invalid":
data['msg'] = public.GetMsg("ACCEPT_SSL_ERR3")
if os.path.isfile("timeout_info"):
data['msg'] = public.GetMsg("ACCEPT_SSL_ERR4")
if os.path.isfile(os.path.join(path, "apply_for_cert_issuance_response")):
data['result'] = json.loads(public.ReadFile(os.path.join(path, "apply_for_cert_issuance_response")));
if data['result']['status'] == 429:
data['msg'] = '<h2>签发失败,您尝试申请证书的失败次数已达上限!</h2>';
if data['result']['status'] == 400:
data['msg'] = '<h2>签发失败,::通配符域名不能和子域名一起申请证书!</h2>';
data['status'] = False;
return data
if file_auth: # 文件验证调用脚本
# 检查是否设置301和反向代理
get.sitename = get.siteName
if self.GetRedirectList(get): return public.returnMsg(False, 'SITE_SSL_ERR_301');
if self.GetProxyList(get): return public.returnMsg(False,'已开启反向代理的站点无法申请SSL!');
DOMAINS = ''
for dom in domains:
DOMAINS += 'DNS:{},'.format(dom)
json_parem = {"path": path, "siteName": get.siteName, "DOMAINS": DOMAINS[:-1],"sitePath":siteInfo['path']}
result = public.ExecShell('''python /www/server/panel/class/letsencrypt.py '{}' '''.format(json.dumps(json_parem)))
crontab = '''python /www/server/panel/class/letsencrypt.py '{}' '''.format(json.dumps(json_parem))
if os.path.exists(csrpath) and os.path.exists(keypath):
pass
else:
if result[1]:
data = {};
data['err'] = result;
data['out'] = result[0];
data['msg'] = public.GetMsg("ISSUANCE_FAIL")
data['result'] = result;
if "too many certificates already issued for exact set of domains" in result[1] or "Error creating new account :: too many registrations for this IP" in result[1]:
data['msg'] = public.GetMsg("ACCEPT_SSL_ERR1")
elif "DNS problem: NXDOMAIN looking up A for" in result[1] or "No valid IP addresses found for" in result[1] or "Invalid response from" in result[1] \
or "Policy forbids issuing for name" in result[1] or "\'status\'\:\ 4" in result[1] or '''"status": 4''' in result[1]:
data['msg'] = public.GetMsg("ACCEPT_SSL_ERR5")
elif public.GetMsg("ACCEPT_SSL_ERR6") in result[1]:
data['msg'] = public.GetMsg("ACCEPT_SSL_ERR7")
data['status'] = False;
public.ExecShell("rm -rf {}/*".format(path))
return data
public.ExecShell('echo "let" > "' + path + '/README"');
if (actionstr == '2'): return public.returnMsg(True, 'SITE_SSL_UPDATE_SUCCESS');
# 定时任务
if crontab:
if "0 0 1 * * python /www/server/panel/class/crontab_ssl.py" not in public.ExecShell("crontab -l")[0]:
with open("/var/spool/cron/root", "a") as f:
f.write("\n0 0 1 * * python /www/server/panel/class/crontab_ssl.py")
crontab_path = "/www/server/panel/vhost/crontab.json"
crontab_list = []
if os.path.isfile(crontab_path):
crontab_list = json.loads(public.ReadFile(crontab_path))
if crontab_list :
modify = False
for i in crontab_list:
if get.siteName == i["siteName"]:
i["cmd"] = crontab
modify = True
if not modify:
crontab_list.append({"siteName": get.siteName, "cmd": crontab})
else:
crontab_list.append({"siteName": get.siteName, "cmd": crontab})
public.WriteFile(crontab_path, json.dumps(crontab_list), mode="w")
sitekey = os.path.join("/www/server/panel/vhost/cert", get.siteName, 'privkey.pem')
sitecsr = os.path.join("/www/server/panel/vhost/cert", get.siteName, 'fullchain.pem')
public.ExecShell("mkdir -p {}".format(os.path.join("/www/server/panel/vhost/cert", get.siteName)))
public.ExecShell('''echo "let" > {}'''.format(os.path.join("/www/server/panel/vhost/cert", get.siteName, "README")));
if not os.path.isfile(sitecsr) and not os.path.isfile(sitekey):
public.ExecShell("/bin/cp {} {}".format(csrpath, sitecsr))
public.ExecShell("/bin/cp {} {}".format(keypath, sitekey))
# 写入配置文件
result = self.SetSSLConf(get);
result['csr'] = public.readFile(csrpath);
result['key'] = public.readFile(keypath);
public.serviceReload();
return result;
#处理acme.sh安装位置问题
def CheckAcme(self):
p1 = '/root/.acme.sh/'
p2 = '/.acme.sh/'
r_name = 'account.conf'
check_names = ['account.conf','acme.sh','dnsapi','deploy']
for r_name in check_names:
if os.path.exists(p1 +r_name):
if not os.path.exists(p2 + r_name): public.ExecShell("ln -sf " + p1 +r_name + ' ' + p2 +r_name)
else:
if os.path.exists(p2 + r_name): public.ExecShell("ln -sf " + p2 +r_name + ' ' + p1 +r_name)
return True
#检测依赖库
def check_ssl_pack(self):
try:
import requests
except:
os.system('pip install requests')
try:
import OpenSSL
except:
os.system('pip 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):
apis = json.loads(public.ReadFile('./config/dns_api.json'))
path = '/root/.acme.sh'
if not os.path.exists(path + '/account.conf'): path = "/.acme.sh"
#if not os.path.exists(path + '/dnsapi'): os.makedirs(path + '/dnsapi')
account = public.readFile(path + '/account.conf')
if not account: account = ''
is_write = False
for i in range(len(apis)):
#filename = path + '/dnsapi/' + apis[i]['name'] + '.sh'
#if not os.path.exists(filename) and apis[i]['name'] != 'dns':
# public.downloadFile('http://download.bt.cn/install/dnsapi/' + apis[i]['name'] + '.sh',filename)
# public.ExecShell("chmod +x " + filename)
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'] + "\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))
return apis
#设置DNS-API
def SetDnsApi(self,get):
#path = '/root/.acme.sh'
#if not os.path.exists(path + '/account.conf'): path = "/.acme.sh"
#filename = path + '/account.conf'
pdata = json.loads(get.pdata)
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
#kvalue = key + "='" + pdata[key] + "'"
#public.ExecShell("sed -i '/%s/d' %s" % (key,filename))
#public.ExecShell("echo \"%s\" >> %s" % (kvalue,filename))
if is_write: public.writeFile('./config/dns_api.json',json.dumps(apis))
return public.returnMsg(True,"设置成功!")
#获取站点所有域名
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').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|grep version:')[0]
nginx_v = re.search('nginx/1\.1(5|6|7|8|9).\d',nginx_v)
openssl_v = public.ExecShell(nginx_bin + ' -V 2>&1|grep OpenSSL')[0].find('OpenSSL 1.1.') != -1
if nginx_v and openssl_v:
return ' TLSv1.3'
return ''
# 添加SSL配置
def SetSSLConf(self, get):
siteName = get.siteName
if not 'first_domain' in get: get.first_domain = siteName
# Nginx配置
file = self.setupPath + '/panel/vhost/nginx/' + 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 TLSv1.1 TLSv1.2%s;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;
""" % (get.first_domain, get.first_domain,self.get_tls13());
if (conf.find('ssl_certificate') != -1):
return public.returnMsg(True, 'SITE_SSL_OPEN_SUCCESS');
conf = conf.replace('#error_page 404/404.html;', sslStr);
# 添加端口
rep = "listen\s+([0-9]+)\s*[default_server]*;";
tmp = re.findall(rep, conf);
if not public.inArray(tmp, '443'):
listen = re.search(rep,conf).group()
versionStr = public.readFile('/www/server/nginx/version.pl');
http2 = ''
if versionStr:
if versionStr.find('1.8.1') == -1: http2 = ' http2';
default_site = '';
if conf.find('default_server') != -1: default_site = ' default_server'
listen_ipv6 = ';'
if self.is_ipv6: listen_ipv6 = ";\n\tlisten [::]:443 ssl"+http2+default_site+";"
conf = conf.replace(listen,listen + "\n\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';
conf = public.readFile(file);
if conf:
if conf.find('SSLCertificateFile') == -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 = "php-cgi-([0-9]{2,3})\.sock";
version = re.search(rep, conf).groups()[0];
if len(version) < 2: return public.returnMsg(False, 'PHP_GET_ERR')
phpConfig = '''
#PHP
<FilesMatch \\.php$>
SetHandler "proxy:unix:/tmp/php-cgi-%s.sock|fcgi://localhost"
</FilesMatch>
''' % (version,);
apaOpt = 'Require all granted';
sslStr = '''%s<VirtualHost *:443>
ServerAdmin webmasterexample.com
DocumentRoot "%s"
ServerName SSL.%s
ServerAlias %s
#errorDocument 404 /404.html
ErrorLog "%s-error_log"
CustomLog "%s-access_log" combined
#SSL
SSLEngine On
SSLCertificateFile /www/server/panel/vhost/cert/%s/fullchain.pem
SSLCertificateKeyFile /www/server/panel/vhost/cert/%s/privkey.pem
SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH
SSLProtocol All -SSLv2 -SSLv3
SSLHonorCipherOrder On
%s
#DENY FILES
<Files ~ (\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md)$>
Order allow,deny
Deny from all
</Files>
#PATH
<Directory "%s">
SetOutputFilter DEFLATE
Options FollowSymLinks
AllowOverride All
%s
DirectoryIndex %s
</Directory>
</VirtualHost>''' % (vName, path, siteName, domains, public.GetConfigValue('logs_path') + '/' + siteName, public.GetConfigValue('logs_path') + '/' + siteName, get.first_domain, get.first_domain, phpConfig, path, apaOpt, index)
conf = conf + "\n" + sslStr;
self.apacheAddPort('443');
shutil.copyfile(file, self.apache_conf_bak)
public.writeFile(file, conf)
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")
return public.returnMsg(False,public.GetMsg("SSL_ERR_CERT")+': <br><a style="color:red;">'+isError.replace("\n",'<br>')+'</a>');
sql = public.M('firewall');
import firewalls
get.port = '443'
get.ps = 'HTTPS'
firewalls.firewalls().AddAcceptPort(get)
public.serviceReload();
self.save_cert(get);
public.WriteLog('TYPE_SITE', 'SITE_SSL_OPEN_SUCCESS', (siteName,));
return public.returnMsg(True, 'SITE_SSL_OPEN_SUCCESS');
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';
conf = public.readFile(file);
if conf:
if conf.find('ssl_certificate') == -1: return public.returnMsg(False,'NOT_OPEN_SSL');
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';
conf = public.readFile(file);
if conf:
httpTohttos = '''combined
#HTTP_TO_HTTPS_START
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{SERVER_PORT} !^443$
RewriteRule (.*) https://%{SERVER_NAME}$1 [L,R=301]
</IfModule>
#HTTP_TO_HTTPS_END'''
conf = re.sub('combined',httpTohttos,conf,1);
public.writeFile(file,conf);
public.serviceReload();
return public.returnMsg(True,'SET_SUCCESS');
#CloseToHttps
def CloseToHttps(self,get):
siteName = get.siteName;
file = self.setupPath + '/panel/vhost/nginx/'+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);
public.serviceReload();
return public.returnMsg(True,'SET_SUCCESS');
#是否跳转到https
def IsToHttps(self,siteName):
file = self.setupPath + '/panel/vhost/nginx/'+siteName+'.conf';
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';
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+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 = "\s+ssl\s+on;";
conf = re.sub(rep, '', conf);
rep = "\s+error_page\s497.+;";
conf = re.sub(rep, '', conf);
rep = "\s+if.+server_port.+\n.+\n\s+\s*}";
conf = re.sub(rep, '', conf);
rep = "\s+listen\s+443.*;";
conf = re.sub(rep, '', conf);
rep = "\s+listen\s+\[::\]:443.*;";
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<VirtualHost \*\:443>(.|\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)
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): os.system('rm -f ' + p_file);
public.WriteLog('TYPE_SITE', 'SITE_SSL_CLOSE_SUCCESS', (siteName,));
public.serviceReload();
return public.returnMsg(True, 'SITE_SSL_CLOSE_SUCCESS');
# 取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;
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';
conf = public.readFile(file);
keyText = 'SSLCertificateFile'
if public.get_webserver() == 'nginx': keyText = 'ssl_certificate';
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)
return {'status': status, 'domain': domains, 'key': key, 'csr': csr, 'type': type, 'httpTohttps': toHttps,'cert_data':cert_data}
#启动站点
def SiteStart(self,get):
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);
public.writeFile(file,conf)
#apaceh
file = self.setupPath + '/panel/vhost/apache/'+get.name+'.conf';
conf = public.readFile(file);
if conf:
conf = conf.replace(Path, sitePath);
public.writeFile(file,conf)
public.M('sites').where("id=?",(id,)).setField('status','1');
public.serviceReload();
public.WriteLog('TYPE_SITE','SITE_START_SUCCESS',(get.name,))
return public.returnMsg(True,'SITE_START_SUCCESS')
#停止站点
def SiteStop(self,get):
path = self.setupPath + '/stop';
id = get.id
if not os.path.exists(path):
os.makedirs(path)
public.downloadFile('http://download.bt.cn/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');
#nginx
file = self.setupPath + '/panel/vhost/nginx/'+get.name+'.conf';
conf = public.readFile(file);
if conf:
conf = conf.replace(sitePath,path);
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);
public.writeFile(file,conf)
public.M('sites').where("id=?",(id,)).setField('status','0');
public.serviceReload();
public.WriteLog('TYPE_SITE','SITE_STOP_SUCCESS',(get.name,))
return public.returnMsg(True,'SITE_STOP_SUCCESS')
#取流量限制值
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 = "\s+limit_conn\s+perserver\s+([0-9]+);";
tmp = re.search(rep, conf).groups()
data['perserver'] = int(tmp[0]);
#IP并发限制
rep = "\s+limit_conn\s+perip\s+([0-9]+);";
tmp = re.search(rep, conf).groups()
data['perip'] = int(tmp[0]);
#请求并发限制
rep = "\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.returnMsg(False, 'SITE_NETLIMIT_ERR');
id = get.id;
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 = "limit_conn\s+perserver\s+([0-9]+);";
conf = re.sub(rep,perserver,conf);
#替换IP并发限制
rep = "limit_conn\s+perip\s+([0-9]+);";
conf = re.sub(rep,perip,conf);
#替换请求流量限制
rep = "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.returnMsg(False,'ERROR: <br><a style="color:red;">'+isError.replace("\n",'<br>')+'</a>');
public.serviceReload();
public.WriteLog('TYPE_SITE','SITE_NETLIMIT_OPEN_SUCCESS',(siteName,))
return public.returnMsg(True, 'SET_SUCCESS');
#关闭流量限制
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 = "\s+limit_conn\s+perserver\s+([0-9]+);";
conf = re.sub(rep,'',conf);
#清理IP并发限制
rep = "\s+limit_conn\s+perip\s+([0-9]+);";
conf = re.sub(rep,'',conf);
#清理请求流量限制
rep = "\s+limit_rate\s+([0-9]+)\w+;";
conf = re.sub(rep,'',conf);
public.writeFile(filename,conf)
public.serviceReload();
public.WriteLog('TYPE_SITE','SITE_NETLIMIT_CLOSE_SUCCESS',(siteName,))
return public.returnMsg(True, 'SITE_NETLIMIT_CLOSE_SUCCESS');
#取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()
for key in tmp:
domains += key['name'] + ','
try:
if(public.get_webserver() == 'nginx'):
conf = public.readFile(self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf');
if conf.find('301-START') == -1:
result['domain'] = domains[:-1]
result['src'] = "";
result['status'] = False
result['url'] = "http://";
return result;
rep = "return\s+301\s+((http|https)\://.+);";
arr = re.search(rep, conf).groups()[0];
rep = "'\^(([\w-]+\.)+[\w-]+)'";
tmp = re.search(rep, conf);
src = ''
if tmp : src = tmp.groups()[0]
else:
conf = public.readFile(self.setupPath + '/panel/vhost/apache/' + siteName + '.conf');
if conf.find('301-START') == -1:
result['domain'] = domains[:-1]
result['src'] = "";
result['status'] = False
result['url'] = "http://";
return result;
rep = "RewriteRule\s+.+\s+((http|https)\://.+)\s+\[";
arr = re.search(rep, conf).groups()[0];
rep = "\^((\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 = "(http|https)\://.+";
if not re.match(rep, toDomain): return public.returnMsg(False,"SITE_PROXY_ERR_URL");
#nginx
filename = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf';
mconf = public.readFile(filename);
if mconf:
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 type == '1':
if(srcDomain == 'all'):
conf301 = "\n\t#301-START\n\t<IfModule mod_rewrite.c>\n\t\tRewriteEngine on\n\t\tRewriteRule ^(.*)$ "+toDomain+"$1 [L,R=301]\n\t</IfModule>\n\t#301-END\n";
else:
conf301 = "\n\t#301-START\n\t<IfModule mod_rewrite.c>\n\t\tRewriteEngine on\n\t\tRewriteCond %{HTTP_HOST} ^"+srcDomain+" [NC]\n\t\tRewriteRule ^(.*) "+toDomain+"$1 [L,R=301]\n\t</IfModule>\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)
isError = public.checkWebConfig();
if(isError != True):
return public.returnMsg(False,'ERROR: <br><a style="color:red;">'+isError.replace("\n",'<br>')+'</a>');
public.serviceReload();
return public.returnMsg(True,'SUCCESS');
#取子目录绑定
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;
os.system('mkdir -p ' + path);
os.system('chmod 755 ' + path);
os.system('chown www:www ' + path);
siteName = public.M('sites').where('id=?',(get.id,)).getField('name')
public.WriteLog('TYPE_SITE',"SITE_DIR_NOT_EXIST_RECREATE",(siteName,path))
dirnames = []
for filename in os.listdir(path):
try:
filePath = path + '/' + filename
if os.path.islink(filePath): continue
if os.path.isdir(filePath):
dirnames.append(filename)
except:
pass
data = {}
data['dirs'] = dirnames
data['binding'] = public.M('binding').where('pid=?',(get.id,)).field('id,pid,domain,path,port,addtime').select()
return data
#添加子目录绑定
def AddDirBinding(self,get):
import shutil
id = get.id
tmp = get.domain.split(':')
domain = tmp[0].lower();
port = '80'
if len(tmp) > 1: port = tmp[1];
if not hasattr(get,'dirName'): public.returnMsg(False, 'DIR_EMPTY');
dirName = get.dirName;
reg = "^([\w\-\*]{1,100}\.){1,4}(\w{1,10}|\w{1,10}\.\w{1,10})$";
if not re.match(reg, domain): return public.returnMsg(False,'SITE_ADD_ERR_DOMAIN');
siteInfo = public.M('sites').where("id=?",(id,)).field('id,path,name').find();
webdir = siteInfo['path'] + '/' + dirName;
sql = public.M('binding');
if sql.where("domain=?",(domain,)).count() > 0: return public.returnMsg(False, 'SITE_ADD_ERR_DOMAIN_EXISTS');
if public.M('domain').where("name=?",(domain,)).count() > 0: return public.returnMsg(False, 'SITE_ADD_ERR_DOMAIN_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
rep = "enable-php-([0-9]{2,3})\.conf";
tmp = re.search(rep,conf).groups()
version = tmp[0];
bindingConf ='''
#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|\.svn|\.project|LICENSE|README.md)
{
return 404;
}
%s
location ~ \.well-known{
allow all;
}
location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 30d;
error_log off;
access_log /dev/null;
}
location ~ .*\\.(js|css)?$
{
expires 12h;
error_log off;
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'],public.GetMsg("NGINX_CONF_BAN_FILE_DIR"),public.GetMsg("NGINX_CONF_ONECLICK_SET_SSL_DIR"),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 = "php-cgi-([0-9]{2,3})\.sock";
tmp = re.search(rep,conf).groups()
version = tmp[0];
phpConfig ='''
#PHP
<FilesMatch \\.php>
SetHandler "proxy:unix:/tmp/php-cgi-%s.sock|fcgi://localhost"
</FilesMatch>
''' % (version,)
apaOpt = 'Require all granted';
bindingConf ='''
\n#BINDING-%s-START
<VirtualHost *:%s>
ServerAdmin webmaster@example.com
DocumentRoot "%s"
ServerName %s
#errorDocument 404 /404.html
ErrorLog "%s-error_log"
CustomLog "%s-access_log" combined
%s
#DENY FILES
<Files ~ (\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md)$>
Order allow,deny
Deny from all
</Files>
#PATH
<Directory "%s">
SetOutputFilter DEFLATE
Options FollowSymLinks
AllowOverride All
%s
DirectoryIndex index.php index.html index.htm default.php default.html default.htm
</Directory>
</VirtualHost>
#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
#检查配置是否有误
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.returnMsg(False,'ERROR: <br><a style="color:red;">'+isError.replace("\n",'<br>')+'</a>');
public.M('binding').add('pid,domain,port,path,addtime',(id,domain,port,dirName,public.getDate()));
public.serviceReload();
public.WriteLog('TYPE_SITE', 'SITE_BINDING_ADD_SUCCESS',(siteInfo['name'],dirName,domain));
return public.returnMsg(True, 'ADD_SUCCESS');
#删除子目录绑定
def DelDirBinding(self,get):
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 = "\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 = "\s*.+BINDING-" + binding['domain'] + "-START(.|\n)+BINDING-" + binding['domain'] + "-END";
conf = re.sub(rep, '', conf);
public.writeFile(filename,conf)
public.M('binding').where("id=?",(id,)).delete();
filename = self.setupPath + '/panel/vhost/rewrite/' + siteName + '_' + binding['path'] + '.conf';
if os.path.exists(filename): os.remove(filename)
public.serviceReload();
public.WriteLog('TYPE_SITE', 'SITE_BINDING_DEL_SUCCESS',(siteName,binding['path']));
return public.returnMsg(True,'DEL_SUCCESS')
#取默认文档
#取子目录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() == 'apache'):
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'] = []
for ds in os.listdir('rewrite/' + public.get_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';
conf = public.readFile(file)
if public.get_webserver() == 'nginx':
rep = "\s+index\s+(.+);";
else:
rep = "DirectoryIndex\s+(.+)\n";
tmp = re.search(rep,conf).groups()
return tmp[0].replace(' ',',')
#设置默认文档
def SetIndex(self,get):
id = get.id;
if get.Index.find('.') == -1: return public.returnMsg(False, 'SITE_INDEX_ERR_FORMAT')
Index = get.Index.replace(' ', '')
Index = get.Index.replace(',,', ',')
if len(Index) < 3: return public.returnMsg(False, 'SITE_INDEX_ERR_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 = "\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);
public.serviceReload();
public.WriteLog('TYPE_SITE', 'SITE_INDEX_SUCCESS',(Name,Index_L));
return public.returnMsg(True, 'SET_SUCCESS')
#修改物理路径
def SetPath(self,get):
id = get.id
Path = self.GetPath(get.path);
if Path == "" or id == '0': return public.returnMsg(False, "DIR_EMPTY");
import files
if not files.files().CheckDir(Path) or not self.__check_site_path(Path): return public.returnMsg(False, "PATH_ERROR");
SiteFind = public.M("sites").where("id=?",(id,)).field('path,name').find();
if SiteFind["path"] == Path: return public.returnMsg(False, "SITE_PATH_ERR_RE");
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 = "<Directory\s+.+\n";
conf = re.sub(rep,'<Directory "' + Path + "\">\n",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/:/proc/')
public.ExecShell('chmod 644 ' + userIni)
public.ExecShell('chown root:root ' + userIni)
public.ExecShell('chattr +i '+userIni)
public.serviceReload();
public.M("sites").where("id=?",(id,)).setField('path',Path);
public.WriteLog('TYPE_SITE', 'SITE_PATH_SUCCESS',(Name,));
return public.returnMsg(True, "SET_SUCCESS");
#取当前可用PHP版本
def GetPHPVersion(self,get):
phpVersions = ('00','52','53','54','55','56','70','71','72','73','74')
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': phpVersions = ('00','53','54','55','56','70','71','72','73','74')
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,'');
data = []
for val in phpVersions:
tmp = {}
checkPath = self.setupPath+'/php/'+val+'/bin/php';
if val == '00': 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'] = 'Pure static';
data.append(tmp)
return data
#取指定站点的PHP版本
def GetSitePHPVersion(self,get):
try:
siteName = get.siteName;
conf = public.readFile(self.setupPath + '/panel/vhost/'+public.get_webserver()+'/'+siteName+'.conf');
if public.get_webserver() == 'nginx':
rep = "enable-php-([0-9]{2,3})\.conf"
else:
rep = "php-cgi-([0-9]{2,3})\.sock";
tmp = re.search(rep,conf).groups()
data = {}
data['phpversion'] = tmp[0];
data['tomcat'] = conf.find('#TOMCAT-START');
data['tomcatversion'] = public.readFile(self.setupPath + '/tomcat/version.pl');
data['nodejs'] = conf.find('#NODE.JS-START');
data['nodejsversion'] = public.readFile(self.setupPath + '/node.js/version.pl');
return data;
except:
return public.returnMsg(False,'SITE_PHPVERSION_ERR_A22');
#设置指定站点的PHP版本
def SetPHPVersion(self,get):
siteName = get.siteName
version = get.version
#nginx
file = self.setupPath + '/panel/vhost/nginx/'+siteName+'.conf';
conf = public.readFile(file);
if conf:
rep = "enable-php-([0-9]{2,3})\.conf";
tmp = re.search(rep,conf).group()
conf = conf.replace(tmp,'enable-php-'+version+'.conf');
public.writeFile(file,conf)
#apache
file = self.setupPath + '/panel/vhost/apache/'+siteName+'.conf';
conf = public.readFile(file);
if conf:
rep = "php-cgi-([0-9]{2,3})\.sock";
tmp = re.search(rep,conf).group()
conf = conf.replace(tmp,'php-cgi-'+version+'.sock');
public.writeFile(file,conf)
public.serviceReload();
public.WriteLog("TYPE_SITE", "SITE_PHPVERSION_SUCCESS",(siteName,version));
return public.returnMsg(True,'SITE_PHPVERSION_SUCCESS',(siteName,version));
#是否开启目录防御
def GetDirUserINI(self,get):
path = get.path + self.GetRunPath(get);
id = get.id;
get.name = public.M('sites').where("id=?",(id,)).getField('name');
data = {}
data['logs'] = self.GetLogsStatus(get);
data['userini'] = False;
if os.path.exists(path+'/.user.ini'):
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'
public.ExecShell('chattr -i ' + useriniPath);
public.ExecShell('rm -f ' + useriniPath);
for p1 in os.listdir(path):
try:
npath = path + '/' + p1;
if os.path.isdir(npath):
if up < 2: self.DelUserInI(npath, up + 1);
else:
continue;
useriniPath = npath + '/.user.ini';
if not os.path.exists(useriniPath): continue;
public.ExecShell('chattr -i ' + useriniPath);
public.ExecShell('rm -f ' + useriniPath);
except: continue;
return True;
#设置目录防御
def SetDirUserINI(self,get):
path = get.path
runPath = self.GetRunPath(get)
filename = path+runPath+'/.user.ini';
if os.path.exists(filename):
public.ExecShell("chattr -i "+filename);
os.remove(filename)
return public.returnMsg(True, 'SITE_BASEDIR_CLOSE_SUCCESS');
if len(runPath) > 1:
self.DelUserInI(path + runPath);
else:
self.DelUserInI(path);
public.writeFile(filename, 'open_basedir='+path+'/:/tmp/:/proc/');
public.ExecShell("chattr +i "+filename);
return public.returnMsg(True,'SITE_BASEDIR_OPEN_SUCCESS');
# 读配置
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 = public.readFile(conf_path)
rep = "(#PROXY-START(\n|.)+#PROXY-END)"
url_rep = "proxy_pass (.*);|ProxyPass\s/\s(.*)|Host\s(.*);"
host_rep = "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.GetMsg("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)
# 备份并替换老虚拟主机配置文件
os.system("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 RemoveProxy(self, get):
proxyUrl = self.__read_config(self.__proxyfile)
sitename = get.sitename
proxyname = get.proxyname
for i in range(len(proxyUrl)):
if proxyUrl[i]["sitename"] == sitename and proxyUrl[i]["proxyname"] == proxyname:
proxyname_md5 = self.__calc_md5(proxyUrl[i]["proxyname"])
os.system("rm -f %s/panel/vhost/nginx/proxy/%s/%s_%s.conf" % (self.setupPath,proxyUrl[i]["sitename"],proxyname_md5,proxyUrl[i]["sitename"]))
os.system("rm -f %s/panel/vhost/apache/proxy/%s/%s_%s.conf" % (self.setupPath,proxyUrl[i]["sitename"],proxyname_md5, proxyUrl[i]["sitename"]))
del proxyUrl[i]
self.__write_config(self.__proxyfile,proxyUrl)
self.SetNginx(get)
self.SetApache(get.sitename)
public.serviceReload()
return public.returnMsg(True, 'DEL_SUCCESS')
# 检查代理是否存在
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 = "(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 = ""
print(d, p)
try:
if p:
sk.connect((d, int(p)))
print (p)
else:
if h == "http":
sk.connect((d, 80))
print(80)
else:
sk.connect((d, 443))
print(443)
except:
return public.returnMsg(False, "CANT_GET_URL")
# 基本设置检查
def __CheckStart(self,get,action=""):
isError = public.checkWebConfig()
if (isError != True):
return public.returnMsg(False, 'GET_ERR_IN_CONFILE')
if action == "create":
if sys.version_info.major < 3:
if len(get.proxyname) < 3 or len(get.proxyname) > 15:
return public.returnMsg(False, 'NAME_LEN')
else:
if len(get.proxyname.encode("utf-8")) < 3 or len(get.proxyname.encode("utf-8")) > 15:
return public.returnMsg(False, 'NAME_LEN')
if self.__check_even(get,action):
return public.returnMsg(False, 'PROXY_NAME_OR_DIR_EXIST')
# 判断代理,只能有全局代理或目录代理
if self.__check_proxy_even(get,action):
return public.returnMsg(False, 'CANT_SET_DIRANDGLOBA')
#判断cachetime类型
if get.cachetime:
try:
int(get.cachetime)
except:
return public.returnMsg(False, "INPUT_NUM")
rep = "http(s)?\:\/\/"
repd = "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.returnMsg(False, "PROXY_DIR_ERR" , ("?,=,[,],),(,*,&,^,%,$,#,@,!,~,`,{,},>,<,\,',\"]",))
# 检测发送域名格式
if get.todomain:
if not re.search(tod,get.todomain):
return public.returnMsg(False, 'SENT_DOMAIN_FORMAT', (get.todomain,))
else:
get.todomain = "$host"
# 检测目标URL格式
if not re.match(rep, get.proxysite):
return public.returnMsg(False, 'DOMAIN_FORMAT',(get.proxysite,))
if re.search(repte,get.proxysite):
return public.returnMsg(False, "PROXY_URL_ERR" , ("?,=,[,],),(,*,&,^,%,$,#,@,!,~,`,{,},>,<,\,',\"]",))
# 检测目标url是否可用
# if re.match(repd, get.proxysite):
# if self.__CheckUrl(get):
# return public.returnMsg(False, "目标URL无法访问")
subfilter = json.loads(get.subfilter)
# 检测替换内容
if subfilter:
for s in subfilter:
if not s["sub1"]:
if s["sub2"]:
return public.returnMsg(False, 'INPUT_REPLACE_CONTENT')
elif s["sub1"] == s["sub2"]:
return public.returnMsg(False, 'REP_CON_CANT_SAMEAS_BEREP_CON')
# 设置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 $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\#]+.{66,66}\n+[\s\w\/\*\.\;]+include enable-php-" % public.GetMsg("CLEAR_CACHE")
ng_conf = re.sub(rep, 'include enable-php-', ng_conf)
oldconf = '''location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 30d;
error_log off;
access_log /dev/null;
}
location ~ .*\\.(js|css)?$
{
expires 12h;
error_log off;
access_log /dev/null;
}'''
ng_conf = ng_conf.replace('access_log', oldconf + "\n\taccess_log")
public.writeFile(ng_file, ng_conf)
return
sitenamelist = []
for i in p_conf:
sitenamelist.append(i["sitename"])
if get.sitename in sitenamelist:
rep = "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.GetMsg("CLEAR_CACHE") +cureCache +"\n\t%s\n\t" % public.GetMsg("NGINX_PROXY_REP") + "include " + ng_proxyfile + ";\n\n\tinclude enable-php-")
public.writeFile(ng_file,ng_conf)
else:
rep = "%s[\w\s\~\/\(\)\.\*\{\}\;\$\n\#]+.{66,66}\n+[\s\w\/\*\.\;]+include enable-php-" % public.GetMsg("CLEAR_CACHE")
ng_conf = re.sub(rep,'include enable-php-',ng_conf)
oldconf = '''location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 30d;
error_log off;
access_log /dev/null;
}
location ~ .*\\.(js|css)?$
{
expires 12h;
error_log off;
access_log /dev/null;
}'''
ng_conf = ng_conf.replace('access_log', oldconf + "\n\taccess_log")
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\/\.\*]+" % public.GetMsg("NGINX_PROXY_REP")
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.GetMsg("NGINX_PROXY_REP") + "\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\/\.\*]+" % public.GetMsg("NGINX_PROXY_REP")
ap_conf = re.sub(rep,'', ap_conf)
public.writeFile(ap_file, ap_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.returnMsg(False, 'NGINX_PROXY_EXIST_ERR')
# 创建反向代理
def CreateProxy(self, get):
try:
nocheck = get.nocheck
except:
nocheck = ""
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)
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.SetProxy(get)
# return public.returnMsg(False, '配置冲突')
get.version = '00'
get.siteName = get.sitename
self.SetPHPVersion(get)
public.serviceReload()
return public.returnMsg(True, 'ADD_SUCCESS')
# 取代理配置文件
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.returnMsg(False, 'PROXY_WAS_STOP')
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, '保存成功')
# 修改反向代理
def ModifyProxy(self, get):
proxyname_md5 = self.__calc_md5(get.proxyname)
ap_conf_file = "%s/panel/vhost/apache/proxy/%s/%s_%s.conf" % (
self.setupPath, get.sitename, proxyname_md5, get.sitename)
ng_conf_file = "%s/panel/vhost/nginx/proxy/%s/%s_%s.conf" % (
self.setupPath, get.sitename, proxyname_md5, get.sitename)
if self.__CheckStart(get):
return self.__CheckStart(get)
proxyUrl = self.__read_config(self.__proxyfile)
for i in range(len(proxyUrl)):
if proxyUrl[i]["proxyname"] == get.proxyname and proxyUrl[i]["sitename"] == get.sitename:
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))
proxyUrl[i]["type"] = int(get.type)
self.__write_config(self.__proxyfile, proxyUrl)
public.serviceReload()
return public.returnMsg(True, '修改成功')
else:
if os.path.exists(ap_conf_file+"_bak"):
os.system("mv %s_bak %s" % (ap_conf_file, ap_conf_file))
os.system("mv %s_bak %s" % (ng_conf_file, ng_conf_file))
ng_conf = public.readFile(ng_conf_file)
# 修改nginx配置
ng_conf = re.sub("location\s+%s" % proxyUrl[i]["proxydir"],"location "+get.proxydir,ng_conf)
ng_conf = re.sub("proxy_pass\s+%s" % proxyUrl[i]["proxysite"],"proxy_pass "+get.proxysite,ng_conf)
ng_conf = re.sub("\sHost\s+%s" % proxyUrl[i]["todomain"]," Host "+get.todomain,ng_conf)
cache_rep = "proxy_cache_valid\s+200\s+304\s+301\s+302\s+"
if int(get.cache) == 1:
if re.search(cache_rep,ng_conf):
ng_conf = re.sub(cache_rep+"%sm;" % proxyUrl[i]["cachetime"], "proxy_cache_valid 200 304 301 302 %sm;" % get.cachetime, ng_conf)
else:
ng_cache = """
proxy_cache cache_one;
proxy_cache_key $host$uri$is_args$args;
proxy_cache_valid 200 304 301 302 %sm;""" % (get.cachetime)
cache_rep = '#proxy_set_header\s+Connection\s+"upgrade";'
ng_conf = re.sub(cache_rep,'#proxy_set_header Connection "upgrade";\n'+ng_cache,ng_conf)
else:
rep = '\s+proxy_cache\s+cache_one.*[\n\s\w\_\";\$]+m;'
ng_conf = re.sub(rep, "", ng_conf)
sub_rep = "sub_filter"
subfilter = json.loads(get.subfilter)
if str(proxyUrl[i]["subfilter"]) != str(subfilter):
if re.search(sub_rep, ng_conf):
sub_rep = "\s+proxy_set_header\s+Accept-Encoding.*[\n\s\w\_\";]+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 s["sub1"]:
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 = '#proxy_set_header\s+Connection\s+"upgrade";'
ng_conf = re.sub(sub_rep,'#proxy_set_header Connection "upgrade";\n'+ng_sub_filter,ng_conf)
# 修改apache配置
ap_conf = public.readFile(ap_conf_file)
ap_conf = re.sub("ProxyPass\s+%s\s+%s" % (proxyUrl[i]["proxydir"], proxyUrl[i]["proxysite"]),"ProxyPass %s %s" % (get.proxydir,get.proxysite), ap_conf)
ap_conf = re.sub("ProxyPassReverse\s+%s\s+%s" % (proxyUrl[i]["proxydir"], proxyUrl[i]["proxysite"]),
"ProxyPassReverse %s %s" % (get.proxydir, get.proxysite), ap_conf)
proxyUrl[i]["proxydir"] = get.proxydir
proxyUrl[i]["proxysite"] = get.proxysite
proxyUrl[i]["todomain"] = get.todomain
proxyUrl[i]["type"] = int(get.type)
proxyUrl[i]["cache"] = int(get.cache)
proxyUrl[i]["subfilter"] = json.loads(get.subfilter)
proxyUrl[i]["advanced"] = int(get.advanced)
proxyUrl[i]["cachetime"] = int(get.cachetime)
public.writeFile(ng_conf_file,ng_conf)
public.writeFile(ap_conf_file,ap_conf)
self.__write_config(self.__proxyfile, proxyUrl)
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))
public.serviceReload()
return public.returnMsg(True, 'EDIT_SUCCESS')
# 设置反向代理
def SetProxy(self,get):
sitename = get.sitename # 站点名称
advanced = int(get.advanced)
type = int(get.type)
cache = int(get.cache)
cachetime = int(get.cachetime)
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)
# 配置Nginx
# 构造清理缓存连接
# 构造缓存配置
ng_cache = """
proxy_cache cache_one;
proxy_cache_key $host$uri$is_args$args;
proxy_cache_valid 200 304 301 302 %sm;""" % (cachetime)
rep = "(https?://[\w\.]+)"
# proxysite1 = re.search(rep,get.proxysite).group(1)
ng_proxy = '''
#PROXY-START%s
location %s
{
expires 12h;
if ($request_uri ~* "(php|jsp|cgi|asp|aspx)")
{
expires 0;
}
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;
%s
#proxy_connect_timeout 30s;
#proxy_read_timeout 86400s;
#proxy_send_timeout 30s;
#proxy_http_version 1.1;
#proxy_set_header Upgrade $http_upgrade;
#proxy_set_header Connection "upgrade";
add_header X-Cache $upstream_cache_status;
%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):
os.system("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 s["sub1"]:
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 = ''
# 构造反向代理
if advanced == 1:
if type == 1 and cache == 1:
ng_proxy_cache += ng_proxy % (
get.proxydir, get.proxydir,get.proxysite, get.todomain, public.GetMsg("NGINX_PERSISTENCE") ,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, public.GetMsg("NGINX_PERSISTENCE") ,ng_sub_filter,'' ,get.proxydir)
else:
if type == 1 and cache == 1:
ng_proxy_cache += ng_proxy % (
get.proxydir, get.proxydir, get.proxysite, get.todomain, public.GetMsg("NGINX_PERSISTENCE") ,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, public.GetMsg("NGINX_PERSISTENCE") ,ng_sub_filter, '', 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):
os.system("mkdir -p %s" % ap_proxydir)
ap_proxy = ''
if type == 1:
ap_proxy += '''#PROXY-START%s
<IfModule mod_proxy.c>
ProxyRequests Off
SSLProxyEngine on
ProxyPass %s %s/
ProxyPassReverse %s %s/
</IfModule>
#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]
return public.returnMsg(False, 'ERROR: %s<br><a style="color:red;">' % public.GetMsg("CONFIG_ERROR") + isError.replace("\n",
'<br>') + '</a>')
return public.returnMsg(True, 'SUCCESS')
#开启缓存
def ProxyCache(self,get):
if public.get_webserver() != 'nginx': return public.returnMsg(False,'WAF_NOT_NGINX');
file = self.setupPath + "/panel/vhost/nginx/"+get.siteName+".conf";
conf = public.readFile(file);
if conf.find('proxy_pass') == -1: return public.returnMsg(False,'SET_ERROR');
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.returnMsg(True,'SET_SUCCESS');
#检查反向代理配置
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 = "include\s+mime.types;";
conf = re.sub(rep, "include mime.types;\n\tinclude proxy.conf;", conf);
public.writeFile(file,conf)
#取伪静态规则应用列表
def GetRewriteList(self,get):
rewriteList = {}
if public.get_webserver() == 'apache':
get.id = public.M('sites').where("name=?",(get.siteName,)).getField('id');
runPath = self.GetSiteRunPath(get);
rewriteList['sitePath'] = public.M('sites').where("name=?",(get.siteName,)).getField('path') + runPath['runPath'];
rewriteList['rewrite'] = []
rewriteList['rewrite'].append('0.'+public.getMsg('SITE_REWRITE_NOW'))
for ds in os.listdir('rewrite/' + public.get_webserver()):
if ds == 'list.txt': continue;
rewriteList['rewrite'].append(ds[0:len(ds)-5]);
rewriteList['rewrite'] = sorted(rewriteList['rewrite']);
return rewriteList
#保存伪静态模板
def SetRewriteTel(self,get):
get.name = get.name.encode('utf-8');
filename = 'rewrite/' + public.get_webserver() + '/' + get.name + '.conf';
public.writeFile(filename,get.data);
return public.returnMsg(True, 'SITE_REWRITE_SAVE');
#打包
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.WriteLog('TYPE_SITE', 'SITE_BACKUP_SUCCESS',(find['name'],));
return public.returnMsg(True, 'BACKUP_SUCCESS');
#删除备份文件
def DelBackup(self,get):
id = get.id
where = "id=?";
filename = public.M('backup').where(where,(id,)).getField('filename');
if os.path.exists(filename): os.remove(filename)
name = '';
if filename == 'qiniu':
name = public.M('backup').where(where,(id,)).getField('name');
public.ExecShell("python "+self.setupPath + '/panel/script/backup_qiniu.py delete_file ' + name)
public.WriteLog('TYPE_SITE', 'SITE_BACKUP_DEL_SUCCESS',(name,filename));
public.M('backup').where(where,(id,)).delete();
return public.returnMsg(True, 'DEL_SUCCESS');
#旧版本配置文件处理
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('<Directory') != -1:
#self.formatApacheConf(filename)
pass;
#移动文件
shutil.move(filename, toPath + '/' + name + '.conf');
#转移证书
self.moveKey(nginxPath + '/' + name, letPath + '/' + name)
self.moveKey(apachePath + '/' + name, letPath + '/' + name)
#删除多余目录
shutil.rmtree(Path);
#重载服务
public.serviceReload();
#从Nginx配置文件获取站点信息
def formatNginxConf(self,filename):
#准备基础信息
name = os.path.basename(filename[0:len(filename) - 5]);
if name.find('.') == -1: return;
conf = public.readFile(filename);
#取域名
rep = "server_name\s+(.+);"
tmp = re.search(rep,conf);
if not tmp: return;
domains = tmp.groups()[0].split(' ');
#取根目录
rep = "root\s+(.+);"
tmp = re.search(rep,conf);
if not tmp: return;
path = tmp.groups()[0];
#提交到数据库
self.toSiteDatabase(name, domains, path);
#从Apache配置文件获取站点信息
def formatApacheConf(self,filename):
#准备基础信息
name = os.path.basename(filename[0:len(filename) - 5]);
if name.find('.') == -1: return;
conf = public.readFile(filename);
#取域名
rep = "ServerAlias\s+(.+)\n"
tmp = re.search(rep,conf);
if not tmp: return;
domains = tmp.groups()[0].split(' ');
#取根目录
rep = u"DocumentRoot\s+\"(.+)\"\n"
tmp = re.search(rep,conf);
if not tmp: return;
path = tmp.groups()[0];
#提交到数据库
self.toSiteDatabase(name, domains, path);
#添加到数据库
def toSiteDatabase(self,name,domains,path):
if public.M('sites').where('name=?',(name,)).count() > 0: return;
public.M('sites').add('name,path,status,ps,addtime',(name,path,'1',public.GetMsg("INPUT_DB_PS"),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 = conf.replace('access_log /dev/null','access_log ' + rep);
public.writeFile(filename,conf);
public.serviceReload();
return public.returnMsg(True, 'SUCCESS');
#取日志状态
def GetLogsStatus(self,get):
filename = public.GetConfigValue('setup_path') + '/panel/vhost/'+public.get_webserver()+'/' + get.name + '.conf';
conf = public.readFile(filename);
if conf.find('#ErrorLog') != -1: return False;
if conf.find("access_log /dev/null") != -1: 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 conf.find('#AUTH_START') != -1: return True;
return False;
#设置目录加密
def SetHasPwd(self,get):
if len(get.username.strip()) == 0 or len(get.password.strip()) == 0: return public.returnMsg(False,'LOGIN_USER_EMPTY')
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';
passconf = get.username + ':' + public.hasPwd(get.password);
if get.siteName == 'phpmyadmin':
get.configFile = self.setupPath + '/nginx/conf/nginx.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';
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.WriteLog("TYPE_SITE","SITE_AUTH_OPEN_SUCCESS",(get.siteName,));
return public.returnMsg(True,'SET_SUCCESS');
#取消目录加密
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.WriteLog("TYPE_SITE","SITE_AUTH_CLOSE_SUCCESS",(get.siteName,));
return public.returnMsg(True,'SET_SUCCESS');
#启用tomcat支持
def SetTomcat(self,get):
siteName = get.siteName;
name = siteName.replace('.','_');
rep = "^(\d{1,3}\.){3,3}\d{1,3}$";
if re.match(rep,siteName): return public.returnMsg(False,'TOMCAT_IP');
#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 = '''#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
<IfModule mod_proxy.c>
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
</IfModule>
#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.WriteLog('TYPE_SITE','SITE_TOMCAT_OPEN',(siteName,))
return public.returnMsg(True,'SITE_TOMCAT_OPEN');
#关闭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.WriteLog('TYPE_SITE','SITE_TOMCAT_CLOSE',(siteName,));
return public.returnMsg(True,'SITE_TOMCAT_CLOSE');
#取当站点前运行目录
def GetSiteRunPath(self,get):
siteName = public.M('sites').where('id=?',(get.id,)).getField('name');
sitePath = public.M('sites').where('id=?',(get.id,)).getField('path');
path = sitePath;
if public.get_webserver() == 'nginx':
filename = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf'
if os.path.exists(filename):
conf = public.readFile(filename)
rep = '\s*root\s*(.+);'
path = re.search(rep,conf).groups()[0];
else:
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).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:
filePath = sitePath + '/' + filename
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');
#处理Nginx
filename = self.setupPath + '/panel/vhost/nginx/' + siteName + '.conf'
if os.path.exists(filename):
conf = public.readFile(filename)
rep = '\s*root\s*(.+);'
path = re.search(rep,conf).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)
rep = '\s*DocumentRoot\s*"(.+)"\s*\n'
path = re.search(rep,conf).groups()[0];
conf = conf.replace(path,sitePath + get.runPath);
public.writeFile(filename,conf);
self.DelUserInI(sitePath);
get.path = sitePath;
self.SetDirUserINI(get);
public.serviceReload();
return public.returnMsg(True,'SET_SUCCESS');
#设置默认站点
def SetDefaultSite(self,get):
import time;
#清理旧的
defaultSite = public.readFile('data/defaultSite.pl');
http2 = ''
versionStr = public.readFile('/www/server/nginx/version.pl');
if versionStr:
if versionStr.find('1.8.1') == -1: http2 = ' http2';
if defaultSite:
path = self.setupPath + '/panel/vhost/nginx/' + defaultSite + '.conf';
if os.path.exists(path):
conf = public.readFile(path);
rep = "listen\s+80.+;"
conf = re.sub(rep,'listen 80;',conf,1);
rep = "listen\s+\[::\]:80.+;"
conf = re.sub(rep,'listen [::]:80;',conf,1);
rep = "listen\s+443.+;"
conf = re.sub(rep,'listen 443 ssl'+http2+';',conf,1);
rep = "listen\s+\[::\]:443.+;"
conf = re.sub(rep,'listen [::]:443 ssl'+http2+';',conf,1);
public.writeFile(path,conf);
#处理新的
path = self.setupPath + '/apache/htdocs';
if os.path.exists(path):
conf = '''<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule (.*) http://%s/$1 [L]
</IfModule>''' % (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 = "listen\s+80\s*;"
conf = re.sub(rep,'listen 80 default_server;',conf,1);
rep = "listen\s+\[::\]:80\s*;"
conf = re.sub(rep,'listen [::]:80 default_server;',conf,1);
rep = "listen\s+443\s*ssl\s*\w*\s*;"
conf = re.sub(rep,'listen 443 ssl'+http2+' default_server;',conf,1);
rep = "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('data/defaultSite.pl',get.name);
public.serviceReload();
return public.returnMsg(True,'SET_SUCCESS');
#取默认站点
def GetDefaultSite(self,get):
data = {}
data['sites'] = public.M('sites').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 && python panelSafe.pyc " + get.path;
sql = db.Sql()
sql.table('tasks').add('id,name,type,status,addtime,execstr',(None,'%s [' % public.GetMsg("SCAN_DIR") +get.path+']','execshell','0',time.strftime('%Y-%m-%d %H:%M:%S'),execstr))
public.writeFile(isTask,'True')
public.WriteLog('TYPE_SETUP','SITE_SCAN_ADD',(get.path,));
return public.returnMsg(True,'SITE_SCAN_ADD');
#获取结果信息
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.returnMsg(True,'UPDATE_SUCCESS');
return public.returnMsg(False,'CONNECT_ERR');
except:
return public.returnMsg(False,'CONNECT_ERR');
#设置到期时间
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.WriteLog('TYPE_SITE','SITE_EXPIRE_SUCCESS',(siteName,get.edate));
return public.returnMsg(True,'SITE_EXPIRE_SUCCESS');
#获取防盗链状态
def GetSecurity(self,get):
file = '/www/server/panel/vhost/nginx/' + get.name + '.conf';
conf = public.readFile(file);
data = {}
if conf.find('SECURITY-START') != -1:
rep = "#SECURITY-START(\n|.){1,500}#SECURITY-END";
tmp = re.search(rep,conf).group()
data['fix'] = re.search("\(.+\)\$",tmp).group().replace('(','').replace(')$','').replace('|',',');
data['domains'] = ','.join(re.search("valid_referers\s+none\s+blocked\s+(.+);\n",tmp).groups()[0].split());
data['status'] = True;
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['domains'] = ','.join(tmp);
data['status'] = False
return data;
#设置防盗链
def SetSecurity(self,get):
if len(get.fix) < 2: return public.returnMsg(False,'URL_SUFFIX_NOT_EMPTY!');
file = '/www/server/panel/vhost/nginx/' + get.name + '.conf';
if os.path.exists(file):
conf = public.readFile(file);
if conf.find('SECURITY-START') != -1:
rep = "\s{0,4}#SECURITY-START(\n|.){1,500}#SECURITY-END\n?";
conf = re.sub(rep,'',conf);
public.WriteLog('TYPE_SITE',"SITE_STOP_ANTI_STEALING_LINK",(get.name,))
else:
rconf = '''
%s
location ~ .*\.(%s)$
{
expires 30d;
access_log /dev/null;
valid_referers none blocked %s;
if ($invalid_referer){
return 404;
}
}
#SECURITY-END
include enable-php-''' % (public.GetMsg("SECURITY_START"),get.fix.strip().replace(',','|'),get.domains.strip().replace(',',' '))
conf = re.sub("include\s+enable-php-",rconf,conf);
public.WriteLog('TYPE_SITE',"SITE_START_ANTI_STEALING_LINK",(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 conf.find('SECURITY-START') != -1:
rep = "#SECURITY-START(\n|.){1,500}#SECURITY-END\n";
conf = re.sub(rep,'',conf);
else:
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.GetMsg("SECURITY_START") +"\n RewriteEngine on\n RewriteCond %{HTTP_REFERER} !^$ [NC]\n" + domains + "\n RewriteRule .("+get.fix.strip().replace(',','|')+") /404.html [R=404,NC,L]\n #SECURITY-END"
conf = conf.replace('combined',rconf)
public.writeFile(file,conf);
public.serviceReload();
return public.returnMsg(True,'SET_SUCCESS');
#取网站日志
def GetSiteLogs(self,get):
serverType = public.get_webserver();
logPath = '/www/wwwlogs/' + get.siteName + '.log';
if serverType != 'nginx': logPath = '/www/wwwlogs/' + get.siteName + '-access_log';
if not os.path.exists(logPath): return public.returnMsg(False,'LOG_EMPTY');
return public.returnMsg(True,public.GetNumLines(logPath,1000));
#取网站分类
def get_site_types(self,get):
data = public.M("site_types").field("id,name").order("id asc").select()
data.insert(0,{"id":0,"name":public.GetMsg("DEFAULT_SORT")})
return data
#添加网站分类
def add_site_type(self,get):
get.name = get.name.strip()
if not get.name: return public.returnMsg(False,"SORT_NAME_ERR")
if len(get.name) > 18: return public.returnMsg(False,"SORT_NAME_LEN_ERR")
type_sql = public.M('site_types')
if type_sql.count() >= 10: return public.returnMsg(False,'SORT_MOST')
if type_sql.where('name=?',(get.name,)).count()>0: return public.returnMsg(False,"SORT_NAME_EXIST")
type_sql.add("name",(get.name,))
return public.returnMsg(True,'ADD_SUCCESS')
#删除网站分类
def remove_site_type(self,get):
type_sql = public.M('site_types')
if type_sql.where('id=?',(get.id,)).count()==0: return public.returnMsg(False,"SORT_NOT_EXIST")
type_sql.where('id=?',(get.id,)).delete()
public.M("sites").where("type_id=?",(get.id,)).save("type_id",(0,))
return public.returnMsg(True,"SORT_DEL")
#修改网站分类名称
def modify_site_type_name(self,get):
get.name = get.name.strip()
if not get.name: return public.returnMsg(False,"SORT_NAME_ERR")
if len(get.name) > 18: return public.returnMsg(False,"SORT_NAME_LEN_ERR")
type_sql = public.M('site_types')
if type_sql.where('id=?',(get.id,)).count()==0: return public.returnMsg(False,"SORT_NOT_EXIST")
type_sql.where('id=?',(get.id,)).setField('name',get.name)
return public.returnMsg(True,"EDIT_SUCCESS")
#设置指定站点的分类
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,"SET_SUCCESS")