update log:
1.Optimize the thread safety of sqlite module.
2.Enhanced data protection inside the panel.
3.Enhanced panel input verification.
4.After adjusting the anti-cross-site, it will no longer allow access to /proc/.
5.Adjust the panel password to force salt.
6.User name will be encrypted when adjusting login panel.
7.Other details adjustment.
This commit is contained in:
jose
2020-07-11 10:39:45 +08:00
parent 5949d7624f
commit eb2e13f5a3
17 changed files with 748 additions and 161 deletions
+58 -15
View File
@@ -18,7 +18,6 @@ sys.path.insert(0,'class/')
import public
from flask import Flask,current_app,session,render_template,send_file,request,redirect,g,url_for,make_response,render_template_string,abort
from flask_session import Session
try:
from werkzeug.contrib.cache import SimpleCache
except:
@@ -33,7 +32,6 @@ app = Flask(__name__,template_folder="templates/" + public.GetConfigValue('templ
Compress(app)
sockets = Sockets(app)
import common
import db
import jobs
@@ -150,7 +148,7 @@ admin_path_checks = [
]
if admin_path in admin_path_checks: admin_path = '/bt'
@app.route('/service_status',methods = method_get)
@app.route('/service_status',methods = method_all)
def service_status():
return 'True'
@@ -176,7 +174,7 @@ def webssh(ws):
session['ssh_obj'].run(ws,session['ssh_info'])
@app.route('/term_open',methods=method_all)
@app.route('/term_open',methods=method_get)
def term_open():
comReturn = comm.local()
if comReturn: return comReturn
@@ -212,9 +210,18 @@ def reload_mod():
@app.before_request
def request_check():
#if not public.path_safe_check(request.path): return abort(404)
#路由和URI长度过滤
if len(request.path) > 64: return abort(403)
if len(request.url) > 256: return abort(403)
if request.path in ['/service_status']: return
#POST参数过滤
if request.path in ['/login','/safe','/hook','/public','/down','/get_app_bind_status','/check_bind']:
pdata = request.form.to_dict()
for k in pdata.keys():
if len(k) > 32: return abort(403)
if len(pdata[k]) > 128: return abort(403)
if not request.path in ['/safe','/hook','/public','/mail_sys','/down']:
ip_check = public.check_ip_panel()
if ip_check: return ip_check
@@ -291,6 +298,29 @@ def login():
is_auth_path = False
if admin_path != '/bt' and os.path.exists(admin_path_file) and not 'admin_auth' in session:
is_auth_path = True
#登录输入验证
if request.method == method_post[0]:
v_list = ['username','password','code','vcode','cdn_url']
for v in v_list:
pv = request.form.get(v,'').strip()
if v == 'cdn_url':
if len(pv) > 32: return public.returnMsg(False,'Wrong parameter length!')
continue
if not pv: continue
p_len = 32
if v == 'code': p_len = 4
if v == 'vcode': p_len = 6
if len(pv) != p_len:
return public.returnJson(False,'Wrong parameter length'),json_header
if not re.match(r"^\w+$",pv):
return public.returnJson(False,'Wrong parameter format'),json_header
for n in request.form.keys():
if not n in v_list:
return public.returnJson(False,'You cannot have extra parameters in the login parameters'),json_header
get = get_input()
import userlogin
if hasattr(get,'tmp_token'):
@@ -840,6 +870,10 @@ def panel_public():
if panelWaf_data.is_xss(get.__dict__):return 'ERROR'
except:
pass
if len("{}".format(get.__dict__)) > 1024 * 32:
return 'ERROR'
get.client_ip = public.GetClientIp()
if not hasattr(get,'name'): get.name = ''
if not hasattr(get,'fun'): return abort(404)
@@ -1093,6 +1127,8 @@ def download():
comReturn = comm.local()
if comReturn: return comReturn
filename = request.args.get('filename')
if filename.find('|') != -1:
filename = filename.split('|')[1]
if not filename: return public.ReturnJson(False,"INIT_ARGS_ERR"),json_header
if filename in ['alioss','qiniu','upyun','txcos','ftp']: return panel_cloud()
if filename in ['gdrive','gcloud_storage']: return "Google storage products do not currently support downloads"
@@ -1210,17 +1246,24 @@ def panel_cloud():
comReturn = comm.local()
if comReturn: return comReturn
get = get_input()
if not os.path.exists('plugin/' + get.filename + '/' + get.filename+'_main.py'):
return public.returnJson(False,'INIT_PLUGIN_NOT_EXISTS'),json_header
sys.path.append('plugin/' + get.filename)
plugin_main = __import__(get.filename+'_main')
reload(plugin_main)
tmp = eval("plugin_main.%s_main()" % get.filename)
if not hasattr(tmp,'download_file'): return public.returnJson(False,'INIT_PLUGIN_NOT_DOWN_FUN'),json_header
if get.filename == 'ftp':
download_url = tmp.getFile(get.name)
_filename = get.filename
plugin_name = ""
if _filename.find('|') != -1:
plugin_name = get.filename.split('|')[1]
else:
plugin_name = get.filename
if not os.path.exists('plugin/' + plugin_name + '/' + plugin_name+'_main.py'):
return public.returnJson(False,'INIT_PLUGIN_NOT_EXISTS'),json_header
sys.path.append('plugin/' + plugin_name)
plugin_main = __import__(plugin_name+'_main')
public.mod_reload(plugin_main)
tmp = eval("plugin_main.%s_main()" % plugin_name)
if not hasattr(tmp,'download_file'): return public.returnJson(False,'INIT_PLUGIN_NOT_DOWN_FUN'),json_header
download_url = tmp.download_file(get.name)
if plugin_name == 'ftp':
if download_url.find("ftp") != 0:download_url = "ftp://" + download_url
else:
download_url = tmp.download_file(get.name)
if download_url.find('http') != 0:download_url = 'http://' + download_url
return redirect(download_url)
+7 -6
View File
@@ -4925,11 +4925,11 @@ select[disabled]{
::-webkit-scrollbar {
/*滚动条整体样式*/
width : 10px; /*高宽分别对应横竖滚动条的尺寸*/
height: 10px;
height: 5px;
}
::-webkit-scrollbar-thumb {
/*滚动条里面小方块*/
border-radius: 0;
border-radius: 10px;
box-shadow : inset 0 0 5px rgba(0, 0, 0, 0.2);
background : #999;
}
@@ -5382,7 +5382,7 @@ select[disabled]{
.ace_catalogue_list::-webkit-scrollbar {
/*滚动条整体样式*/
width: 9px; /*高宽分别对应横竖滚动条的尺寸*/
height: 10px;
height: 1px;
}
.ace_catalogue_list::-webkit-scrollbar-thumb {
/*滚动条里面小方块*/
@@ -5465,19 +5465,20 @@ select[disabled]{
}
.ace_scrollbar::-webkit-scrollbar {
/*滚动条整体样式*/
width : 10px; /*高宽分别对应横竖滚动条的尺寸*/
width : 15px; /*高宽分别对应横竖滚动条的尺寸*/
height: 10px;
}
.ace_scrollbar::-webkit-scrollbar-thumb {
/*滚动条里面小方块*/
box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
background: #777;
border-radius: 0;
}
.ace_scrollbar::-webkit-scrollbar-track{
/*滚动条里面轨道*/
box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
background: #333;
border-radius: 10px;
border-radius: 0;
}
.ace_editors.active {
display: block;
@@ -6768,7 +6769,7 @@ select[disabled]{
.dropUpLoadFile::-webkit-scrollbar {
/*滚动条整体样式*/
width : 15px; /*高宽分别对应横竖滚动条的尺寸*/
height: 10px;
height: 1px;
}
.dropUpLoadFile::-webkit-scrollbar-thumb {
/*滚动条里面小方块*/
+333 -1
View File
@@ -9,6 +9,46 @@
<title>{{g.title}}</title>
<link rel="stylesheet" type="text/css" href="/static/css/site.css?date={{g.version}}">
<link rel="stylesheet" type="text/css" href="/static/css/login.css?date={{g.version}}">
<script type="text/javascript">
function IEVersion() {
// 取得浏览器的userAgent字符串
var userAgent = navigator.userAgent;
// 判断是否为小于IE11的浏览器
var isLessIE11 = userAgent.indexOf('compatible') > -1 && userAgent.indexOf('MSIE') > -1;
// 判断是否为IE的Edge浏览器
var isEdge = userAgent.indexOf('Edge') > -1 && !isLessIE11;
// 判断是否为IE11浏览器
var isIE11 = userAgent.indexOf('Trident') > -1 && userAgent.indexOf('rv:11.0') > -1;
if (isLessIE11) {
var IEReg = new RegExp('MSIE (\\d+\\.\\d+);');
// 正则表达式匹配浏览器的userAgent字符串中MSIE后的数字部分,,这一步不可省略!!!
IEReg.test(userAgent);
// 取正则表达式中第一个小括号里匹配到的值
var IEVersionNum = parseFloat(RegExp['$1']);
if (IEVersionNum === 7) {// IE7
return 7
} else if (IEVersionNum === 8) {// IE8
return 8
} else if (IEVersionNum === 9) {// IE9
return 9
} else if (IEVersionNum === 10) { // IE10
return 10
} else {// IE版本<7
return 6
}
} else if (isEdge) { // edge
return 'edge'
} else if (isIE11) {// IE11
return 11
} else {// 不是ie浏览器
return -1
}
}
if(IEVersion() > -1 && IEVersion() < 9){
window.location.href = '/tips';
}
</script>
</head>
<body>
<div class="main">
@@ -30,6 +70,14 @@
</div>
<div class="login_btn"><input id="login-button" value="{{data['lan']['N11']}}" type="submit"></div>
<p class="pwinfo" style="display:none">{{data['lan']['N12']}}</p>
{% if data['hosts'] != '[]' %}
<a class="static-cdn">
<span class="span-tname">Acceleration node</span>
<select name="cdn_url" id="static_cdn">
<option value="localhost">Counting...</option>
</select>
</a>
{% endif %}
<a class="resetpw" href="https://forum.aapanel.com/d/16-how-to-reset-password-for-aapanel-linux-6-x" target="_blank">{{data['lan']['N13']}}</a>
</form>
</div>
@@ -59,7 +107,7 @@
</div>
</div>
</div>
<script type="text/javascript" src="/static/js/jquery-1.10.2.min.js"></script>
<!-- <script type="text/javascript" src="/static/js/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="/static/language/zh-cn.js"></script>
<script type="text/javascript" src="/static/layer/layer.js"></script>
<script type="text/javascript" src="/static/js/jquery.qrcode.min.js"></script>
@@ -202,6 +250,290 @@
});
});
</script> -->
<script type="text/javascript">
if(typeof(String.prototype.trim) === "undefined"){
String.prototype.trim = function()
{
return String(this).replace(/^\s+|\s+$/g, '');
};
}
select_host = {
hosts: {{data.hosts|safe}},
my_hosts : {},
bt_version:"{{g.version}}",
request:function(url){
var s_time = Date.now();
var to_url = window.location.protocol + '//' + url + '/test.txt?time=' + s_time
http_request = new XMLHttpRequest();
http_request.open('GET', to_url, true);
http_request.setRequestHeader("Content-type","text/plain");
http_request.send();
http_request.onreadystatechange = function (e) {
if (e.srcElement.readyState == 4 && e.srcElement.status == 200) {
if(e.srcElement.responseText === 'true'){
var e_time = Date.now();
select_host.my_hosts[url] = e_time - s_time
}
}
}
},
run:function(){
for(var i=0;i<select_host.hosts.length;i++){
select_host.request(select_host.hosts[i].url);
}
},
compare:function (prop) {
return function (obj1, obj2) {
var val1 = obj1[prop];
var val2 = obj2[prop];
if (!isNaN(Number(val1)) && !isNaN(Number(val2))) {
val1 = Number(val1);
val2 = Number(val2);
}
if (val1 < val2) {
return -1;
} else if (val1 > val2) {
return 1;
} else {
return 0;
}
}
}
}
select_host.run();
setTimeout(function(){
if(select_host.hosts.length > 0){
var my_keys = Object.keys(select_host.my_hosts);
var host_data = [];
for(var i=0;i<select_host.hosts.length;i++){
if(my_keys.indexOf(select_host.hosts[i].url) == -1) continue;
select_host.hosts[i].speed = select_host.my_hosts[select_host.hosts[i].url];
host_data.push(select_host.hosts[i]);
}
host_data = host_data.sort(select_host.compare('speed'));
var cdn_option = '';
for(var i=0;i<host_data.length;i++){
cdn_option += '<option value="'+host_data[i].url+'">'+host_data[i].name + '('+ host_data[i].speed +'ms)' +'</option>';
}
document.getElementById('static_cdn').innerHTML = cdn_option;
var host_url = "";
if(host_data.length > 0){
host_url = window.location.protocol + '//' + host_data[0].url;
}
if(!host_url) select_host.bt_version = "static";
if(!host_url){
document.getElementById('static_cdn').innerHTML = '<option value="local">This server</option>';
}
}else{
host_url = "";
select_host.bt_version = "static";
}
function loadJs(urls,i,callback){
i++;
var script=document.createElement('script');
script.type="text/javascript";
if(typeof(callback)!="undefined"){
if(script.readyState){
script.onreadystatechange=function(){
if(script.readyState == "loaded" || script.readyState == "complete"){
script.onreadystatechange=null;
if(i==urls.length){
callback();
}else{
loadJs(urls,i,callback);
}
}
}
}else{
script.onload=function(){
if(i==urls.length){
callback();
}else{
loadJs(urls,i,callback);
}
}
}
}
script.src=urls[i-1];
document.body.appendChild(script);
}
my_urls = [
host_url+'/'+select_host.bt_version+'/js/jquery-1.10.2.min.js',
host_url+'/'+select_host.bt_version+'/js/Validform_v5.3.2_min.js',
host_url+'/'+select_host.bt_version+'/js/md5.js',
host_url+'/'+select_host.bt_version+'/js/jquery.qrcode.min.js',
host_url+'/'+select_host.bt_version+'/layer/layer.js',
host_url+'/'+select_host.bt_version+'/language/zh-cn.js'
];
loadJs(my_urls,0,run);
},200);
function run(){
function Wreset() {
var w = $(window).width();
var yzmw = $(".login .line").width() - 140;
if ($(".yzm").is(":visible") && w > 640) {
$(".login").css({ "height": "365px", "margin-top": "-230px" });
}
else {
$(".login").removeAttr("style")
}
$(".login .yzm .inputtxt").width(yzmw);
}
$(function () {
Wreset();
var setTime = '';
// $.get('/public?name=app&fun=login_qrcode', function (res) {
// if (res.status) {
// $('#qrcode').qrcode({
// render: "canvas",
// width: 150,
// height: 150,
// background: "#fefefe",
// foreground: "#333",
// text: res.msg
// });
// $('.entrance').show();
// }
// }).error(function (res, textStatus, errorThrown) {
// if (res.status == 404) {
// $('.entrance').hide();
// }
// });
function controlTime() {
$.get('/public?name=app&fun=is_scan_ok', function (res) {
if (res.status) {
layer.msg('Scanning code succeeded, waiting...', { icon: 1 });
clearInterval(setTime);
loginAdmin(res.msg);
}
});
}
function loginAdmin(key) {
$.get('/public?name=app&fun=set_login', { secret_key: key }, function (res) {
layer.msg(res.msg, { icon: res.status ? 1 : 2 })
if (res.status) {
layer.msg('Secure login in progress,waiting...', { time: 0, shade: [0.4, '#fff'], icon: 16 });
setTimeout(function () {
location.href = '/';
}, 1000);
}
});
}
$('.bg_img').click(function (event) {
if ($(this).hasClass('pc')) {
$(this).removeClass('pc');
$('.scanCode').hide().prev().show();
clearInterval(setTime);
$('.tips>span').html('<img src="/static/img/scan_ico.png"><span>Switch code scanning login</span>');
} else {
$(this).addClass('pc');
$('.account').hide().next().show();
$('.tips>span').html('<img src="/static/img/safety_ico.png"><span>Switch account login</span>')
setTime = window.setInterval(controlTime, 2000);
}
});
if("{{data['app_login']}}" == 'True'){
$('.bg_img').addClass('pc');
$('.account').hide().next().show();
$('.tips>span').html('<img src="/static/img/safety_ico.png"><span>Switch account login</span>')
setTime = window.setInterval(controlTime, 2000);
}
})
window.onresize = function () {
Wreset();
}
$(function () {
$(".loginform").Validform({
tiptype: function (msg, o, cssctl) {
if (!o.obj.is("form")) {
var objtip = o.obj.siblings(".Validform_checktip");
cssctl(objtip, o.type);
objtip.text(msg);
}
}
});
});
$('#login-button').click(function () {
var username = $("input[name='username']").val().trim();
var password = $("input[name='password']").val().trim();
var code = $("input[name='code']").val().trim();
if (username == '' || password == '') {
layer.msg("{{data['lan']['JS1']}}", { icon: 2 });
return;
}
var data = { username: md5(username), password: md5(md5(password) + '' + '_bt.cn'), code: code,cdn_url:$("#static_cdn").val() }
var loadT = layer.msg("{{data['lan']['JS2']}}", { icon: 16, time: 0, shade: [0.3, '#000'] });
$.post('/login', data, function (rdata) {
if(rdata == '1'){
layer.close(loadT);
layer.open({
type:1,
title:false,
area: ['350px', '265px'],
content:'<div class="verification_view">\
<div class="v_title">Google authentication</div>\
<div class="v_input"><input type="text" class="v_code" name="v_code" placeholder="Verification code"/></div>\
<div class="v_btn"><input id="auth_verif_btn" value="Log in" type="submit"></div>\
<div class="v_tips">* Please use the Google Authenticator app to get the verification code<br><a target="_blank" href="https://forum.aapanel.com/d/357-how-to-use-google-authenticator-in-the-aapanel" class="btlink">Unable to verify, click Help</a></div>\
</div>',
success:function(){
// 点击验证登录
$('[name="v_code"]').keyup(function(e){
if(e.keyCode == 13){
$('#auth_verif_btn').click();
}
});
$('#auth_verif_btn').click(function(e){
data['vcode'] = $('[name="v_code"]').val().trim();
var loadT = layer.msg("{{data['lan']['JS2']}}", { icon: 16, time: 0, shade: [0.3, '#000'] });
$.post('/login', data, function (rdata) {
layer.close(loadT);
if(!rdata.status){
layer.msg(rdata.msg, { icon: 2 });
return false;
}
layer.msg(rdata.msg, { icon: 16, time: 0, shade: [0.3, '#000'] });
window.location.href = '/';
});
});
}
});
return false;
}
layer.close(loadT);
if (!rdata.status) {
$("#errorStr").html(rdata.msg);
$("input[name='password']").val('');
num = rdata.msg.substring(rdata.msg.indexOf('[') + 1, rdata.msg.indexOf(']'))
if (num < 5) {
$(".yzm").show();
$(".login").css("height", "332px");
$("input[name='code']").val('');
}
$(".yzm").show();
Wreset();
$(".passcode").click();
layer.msg(rdata.msg, { icon: 2 });
return;
}
layer.msg(rdata.msg, { icon: 16, time: 0, shade: [0.3, '#000'] });
window.location.href = '/';
});
});
}
</script>
</body>
</html>
+60 -29
View File
@@ -403,6 +403,9 @@ class acme_v2:
write_log("|-Verify the dir:{}".format(acme_path))
if not os.path.exists(acme_path): return True
public.ExecShell("rm -f {}/*".format(acme_path))
acme_path = '/www/server/stop/.well-known/acme-challenge'
if os.path.exists(acme_path):
public.ExecShell("rm -f {}/*".format(acme_path))
# 写验证文件
def write_auth_file(self, auth_to, token, acme_keyauthorization):
@@ -414,9 +417,19 @@ class acme_v2:
wellknown_path = '{}/{}'.format(acme_path, token)
public.writeFile(wellknown_path, acme_keyauthorization)
public.set_own(wellknown_path, 'www')
acme_path = '/www/server/stop/.well-known/acme-challenge'
if not os.path.exists(acme_path):
os.makedirs(acme_path)
public.set_own(acme_path, 'www')
wellknown_path = '{}/{}'.format(acme_path,token)
public.writeFile(wellknown_path,acme_keyauthorization)
public.set_own(wellknown_path, 'www')
return True
except:
raise Exception("Writing verification file failed: {}".format(public.get_error_info()))
err = public.get_error_info()
print(err)
raise Exception("Writing verification file failed: {}".format(err))
# 解析域名
def create_dns_record(self, auth_to, domain, dns_value):
@@ -574,6 +587,8 @@ class acme_v2:
return "The verification timed out. Please check if the domain name is resolved correctly. If it is resolved correctly, the connection between the server and Let'sEncrypt may be abnormal. Please try again later!"
elif error.find('Cannot issue for') != -1:
return "Cannot issue a certificate for {}, cannot apply for a wildcard certificate with a domain name suffix directly!".format(re.findall(r'for\s+"(.+)"',error))
elif error.find('too many failed authorizations recently'):
return 'The account has more than 5 failed orders within 1 hour, please wait 1 hour and try again!'
elif error.find("Error creating new order") != -1:
return "Order creation failed, please try again later!"
elif error.find("Too Many Requests") != -1:
@@ -1257,12 +1272,25 @@ fullchain.pem Paste into certificate input box
import panelSite
s = panelSite.panelSite()
if args.auth_type in ['http','tls']:
if not 'siteName' in args:
args.siteName = public.M('sites').where('id=?',(args.id,)).getField('name')
args.sitename = args.siteName
if s.GetRedirectList(args): return public.returnMsg(False, 'SITE_SSL_ERR_301')
if s.GetProxyList(args): return public.returnMsg(False,'Sites with reverse proxy turned on cannot apply for SSL!')
try:
if not 'siteName' in args:
args.siteName = public.M('sites').where('id=?',(args.id,)).getField('name')
args.sitename = args.siteName
data = s.GetRedirectList(args)
if type(data) == list:
for x in data:
if x['type']: return public.returnMsg(False, 'SITE_SSL_ERR_301')
data = s.GetProxyList(args)
if type(data) == list:
for x in data:
if s.GetProxyList(args): return public.returnMsg(False,
'Sites with reverse proxy turned on cannot apply for SSL!')
#判断是否强制HTTPS
if s.IsToHttps(args.siteName):
return public.returnMsg(False, 'After configuring Force HTTPS, you cannot use [File Verification] to apply for a certificate!')
except:
return False
else:
if args.auth_to.find('Dns_com') != -1:
if not os.path.exists('plugin/dns/dns_main.py'):
@@ -1317,10 +1345,9 @@ fullchain.pem Paste into certificate input box
if 'cert' in self._config['orders'][i]:
self._config['orders'][i]['cert_timeout'] = self._config['orders'][i]['cert']['cert_timeout']
if not 'cert_timeout' in self._config['orders'][i]:
continue
self._config['orders'][i]['cert_timeout'] = int(time.time())
if self._config['orders'][i]['cert_timeout'] > s_time or self._config['orders'][i]['auth_to'] == 'dns':
continue
write_log(self._config['orders'][i]['cert_timeout'])
order_index.append(i)
if not order_index:
@@ -1335,26 +1362,30 @@ fullchain.pem Paste into certificate input box
write_log("|-Renewing certificate number of {},domain: {}..".format(n,
self._config['orders'][index]['domains']))
write_log("|-Creating order...")
index = self.create_order(
self._config['orders'][index]['domains'],
self._config['orders'][index]['auth_type'],
self._config['orders'][index]['auth_to'],
index
)
write_log("|-Getting verification information..")
self.get_auths(index)
write_log("|-Verifying domain name..")
self.auth_domain(index)
write_log("|-Sending CSR..")
self.remove_dns_record()
self.send_csr(index)
write_log("|-Downloading certificate..")
cert = self.download_cert(index)
self._config['orders'][index]['renew_time'] = int(time.time())
self.save_config()
cert['status'] = True
cert['msg'] = 'Renewed successfully!'
write_log("|-Renewed successfully!")
try:
index = self.create_order(
self._config['orders'][index]['domains'],
self._config['orders'][index]['auth_type'],
self._config['orders'][index]['auth_to'],
index
)
write_log("|-Getting verification information..")
self.get_auths(index)
write_log("|-Verifying domain name..")
self.auth_domain(index)
write_log("|-Sending CSR..")
self.remove_dns_record()
self.send_csr(index)
write_log("|-Downloading certificate..")
cert = self.download_cert(index)
self._config['orders'][index]['renew_time'] = int(time.time())
self.save_config()
cert['status'] = True
cert['msg'] = 'Renewed successfully!'
write_log("|-Renewed successfully!")
except Exception as e:
write_log("|-" + str(e).split('>>>>')[0])
write_log("-" * 70)
return cert
except Exception as ex:
self.remove_dns_record()
+1 -1
View File
@@ -34,7 +34,7 @@ class panelSetup:
ua = ua.lower()
if ua.find('spider') != -1 or ua.find('bot') != -1:
return redirect('https://www.google.com')
g.version = '6.7.5'
g.version = '6.7.6'
g.title = public.GetConfigValue('title')
g.uri = request.path
if not os.path.exists('data/debug.pl'):
+8 -8
View File
@@ -59,7 +59,7 @@ class config:
#添加接受邮件地址
def add_mail_address(self, get):
if not hasattr(get, 'email'): return public.returnMsg(False, 'Please input your email')
emailformat = re.compile('[a-zA-Z0-9.-_+%]+@[a-zA-Z0-9]+\.[a-zA-Z0-9]+')
emailformat = re.compile(r'[a-zA-Z0-9.-_+%]+@[a-zA-Z0-9]+\.[a-zA-Z0-9]+')
if not emailformat.search(get.email): return public.returnMsg(False, 'Please enter your vaild email')
# 测试发送邮件
if get.email.strip() in self.__mail_list: return public.returnMsg(True, 'Email already exists')
@@ -96,7 +96,7 @@ class config:
# 用户自定义邮件发送
def user_stmp_mail_send(self, get):
if not (hasattr(get, 'email')): return public.returnMsg(False, 'Please fill in the email address')
emailformat = re.compile('[a-zA-Z0-9.-_+%]+@[a-zA-Z0-9]+\.[a-zA-Z0-9]+')
emailformat = re.compile(r'[a-zA-Z0-9.-_+%]+@[a-zA-Z0-9]+\.[a-zA-Z0-9]+')
if not emailformat.search(get.email): return public.returnMsg(False, 'Please enter your vaild email')
# 测试发送邮件
if not get.email.strip() in self.__mail_list: return public.returnMsg(True, 'The mailbox does not exist, please add it to the mailbox list')
@@ -173,7 +173,7 @@ class config:
def setPassword(self,get):
if get.password1 != get.password2: return public.returnMsg(False,'USER_PASSWORD_CHECK')
if len(get.password1) < 5: return public.returnMsg(False,'USER_PASSWORD_LEN')
public.M('users').where("username=?",(session['username'],)).setField('password',public.md5(get.password1.strip()))
public.M('users').where("username=?",(session['username'],)).setField('password',public.password_salt(public.md5(get.password1.strip()),username=session['username']))
public.WriteLog('TYPE_PANEL','USER_PASSWORD_SUCCESS',(session['username'],))
self.reload_session()
return public.returnMsg(True,'USER_PASSWORD_SUCCESS')
@@ -189,7 +189,7 @@ class config:
#取用户列表
def get_users(self,args):
data = public.M('users').field('username').select()
data = public.M('users').field('id,username').select()
return data
# 创建新用户
@@ -199,7 +199,7 @@ class config:
if len(args.password) < 8: return public.returnMsg(False,'Password must be at least 8 characters')
pdata = {
"username": args.username.strip(),
"password": public.md5(args.password.strip())
"password": public.password_salt(public.md5(args.password.strip()),username=args.username.strip())
}
if(public.M('users').where('username=?',(pdata['username'],)).count()):
@@ -233,7 +233,7 @@ class config:
if 'password' in args:
if args.password:
if len(args.password) < 8: return public.returnMsg(False,'Password must be at least 8 characters')
pdata['password'] = public.md5(args.password.strip())
pdata['password'] = public.password_salt(public.md5(args.password.strip()),username=username)
if(public.M('users').where('id=?',(args.id,)).update(pdata)):
public.WriteLog('User Management',"Edit user{}".format(username))
@@ -651,8 +651,8 @@ class config:
#设置面板SSL
def SetPanelSSL(self,get):
if hasattr(get,"email"):
# rep_mail = "^[a-zA-Z0-9_-\.]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$"
rep_mail = "[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?"
#rep_mail = "^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$"
rep_mail = r"[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?"
if not re.search(rep_mail,get.email):
return public.returnMsg(False,'The E-Mail format is illegal')
import setPanelLets
+9 -7
View File
@@ -827,13 +827,15 @@ SetLink
try:
if data[0] == 1045:
return public.returnMsg(False,'MYSQL_PASS_ERR')
except:pass
for d in data:
for g in gets:
try:
if d[0] == g: result[g] = d[1]
except:
pass
for d in data:
for g in gets:
try:
if d[0] == g: result[g] = d[1]
except:
pass
except:
return public.returnMsg(False,str(data))
if not 'Run' in result and result:
result['Run'] = int(time.time()) - int(result['Uptime'])
tmp = panelMysql.panelMysql().query('show master status')
+4
View File
@@ -693,6 +693,10 @@ session.save_handler = files'''.format(path, sess_path, sess_path)
try:
tmp = {}
fname = rPath + file
if sys.version_info[0] == 2:
fname = fname.encode('utf-8')
else:
fname.encode('utf-8')
tmp1 = file.split('_bt_')
tmp2 = tmp1[len(tmp1)-1].split('_t_')
tmp['rname'] = file
+6 -1
View File
@@ -74,9 +74,14 @@ def control_init():
public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'sType' TEXT",())
public.M('crontab').execute("ALTER TABLE 'crontab' ADD 'urladdress' TEXT",())
public.M('users').where('email=? or email=?',('287962566@qq.com','amw_287962566@qq.com')).setField('email','test@message.com')
if not public.M('sqlite_master').where('type=? AND name=? AND sql LIKE ?', ('table', 'users','%salt%')).count():
public.M('users').execute("ALTER TABLE 'users' ADD 'salt' TEXT",())
public.chdck_salt()
filename = '/www/server/nginx/off'
if os.path.exists(filename): os.remove(filename)
+3 -3
View File
@@ -19,7 +19,7 @@ import time
os.chdir('/www/server/panel')
sys.path.insert(0,'class/')
import public
_VERSION = 1.4
_VERSION = 1.5
class backup:
_path = None
@@ -323,7 +323,7 @@ class backup:
#清理多余备份
if not self._cloud:
backups = public.M('backup').where("type=? and pid=? and filename LIKE '%/%'",('0',pid)).field('id,name,filename').select()
backups = public.M('backup').where("type=? and pid=? and filename NOT LIKE '%|%'",('0',pid)).field('id,name,filename').select()
else:
backups = public.M('backup').where('type=? and pid=? and filename LIKE "%{}%"'.format(self._cloud._name),('0',pid)).field('id,name,filename').select()
@@ -449,7 +449,7 @@ class backup:
#清理多余备份
if not self._cloud:
backups = public.M('backup').where("type=? and pid=? and filename LIKE '%/%'",('1',pid)).field('id,name,filename').select()
backups = public.M('backup').where("type=? and pid=? and filename NOT LIKE '%|%'",('1',pid)).field('id,name,filename').select()
else:
backups = public.M('backup').where('type=? and pid=? and filename LIKE "%{}%"'.format(self._cloud._name),('1',pid)).field('id,name,filename').select()
self.delete_old(backups,save,'database')
+17 -5
View File
@@ -128,8 +128,9 @@ class panelPlugin:
if os.path.exists(p_node):
if len(public.readFile(p_node)) < 100: os.remove(p_node)
if not pluginInfo: return public.returnMsg(False, 'INIT_PLUGIN_NOT_EXISTS')
self.mutex_title = pluginInfo['mutex']
if not self.check_mutex(pluginInfo['mutex']): return public.returnMsg(False, 'UNINSTALL_FIRST',
(pluginInfo['mutex'],))
(self.mutex_title,))
if not hasattr(get, 'id'):
if not self.check_dependnet(pluginInfo['dependnet']): return public.returnMsg(False, 'DEP_PAGE',
(pluginInfo['dependnet'],))
@@ -143,7 +144,15 @@ class panelPlugin:
return public.returnMsg(False,'At least [{0}] CPU cores are required to install'.format(versionInfo['cpu_limit']))
if not self.check_mem_limit(versionInfo['mem_limit']):
return public.returnMsg(False,'At least [{0} MB] memory is required to install'.format(versionInfo['mem_limit']))
if not self.check_os_limit(versionInfo['os_limit']):
m_ps = {0: "All", 1: "Centos", 2: "Ubuntu/Debian"}
return public.returnMsg(False, '仅支持[%s]系统' % m_ps[int(versionInfo['os_limit'])])
if not hasattr(get, 'id'):
if not self.check_dependnet(versionInfo['dependnet']): return public.returnMsg(False,
'Depend on the following software, please install first [%s]' %
versionInfo[
'dependnet'])
#安装插件
def install_plugin(self,get):
if not self.check_sys_write(): return public.returnMsg(False,'CANT_WRITE_SYS_DIR')
@@ -368,7 +377,7 @@ class panelPlugin:
if expire_day > 15: return False
if pm.is_level(level): #是否忽略
if level != name: #到期还是即将到期
msg_last = '您的【{}】授权还有{}天到期'.format(title,int(expire_day))
msg_last = '您的【{}】授权还有{}天到期'.format(title,int(expire_day) + 1)
else:
msg_last = '您的【{}】授权已到期'.format(title)
pl_msg = 'true'
@@ -1119,14 +1128,17 @@ class panelPlugin:
#下载图标
def download_icon(self,name,iconFile,downFile):
srcIcon = 'plugin/' + name + '/icon.png'
skey = name+'_icon'
if cache.get(skey): return None
if os.path.exists(srcIcon):
public.ExecShell("\cp -a -r " + srcIcon + " " + iconFile)
public.ExecShell(r"\cp -a -r " + srcIcon + " " + iconFile)
else:
if downFile:
public.ExecShell('wget -O ' + iconFile + ' ' + public.GetConfigValue('home') + downFile + '&')
else:
public.ExecShell('wget -O ' + iconFile + ' ' + public.get_url() + '/install/plugin/' + name + '/icon.png &')
cache.set(skey,1,86400)
#取分页
def GetPage(self,data,get):
+3 -2
View File
@@ -409,7 +409,7 @@ include /www/server/panel/vhost/openlitespeed/proxy/BTSITENAME/*.conf
#表单验证
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})$"
reg = r"^([\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')
@@ -2501,7 +2501,7 @@ server
#创建basedir
userIni = Path + '/.user.ini'
if os.path.exists(userIni): public.ExecShell("chattr -i "+userIni)
public.writeFile(userIni, 'open_basedir='+Path+'/:/tmp/:/proc/')
public.writeFile(userIni, 'open_basedir='+Path+'/:/tmp/')
public.ExecShell('chmod 644 ' + userIni)
public.ExecShell('chown root:root ' + userIni)
public.ExecShell('chattr +i '+userIni)
@@ -2677,6 +2677,7 @@ server
# sitename = path.split('/')[-1]
f = "/www/server/panel/vhost/openlitespeed/detail/{}.conf".format(sitename)
c = public.readFile(f)
if not c: return False
if f:
rep = '\nphp_admin_value\s*open_basedir.*'
result = re.search(rep, c)
+158 -43
View File
@@ -21,16 +21,25 @@ if sys.version_info[0] == 2:
sys.setdefaultencoding('utf8')
def M(table):
"""
@name 访问面板数据库
@author hwliang<hwl@bt.cn>
@table 被访问的表名(必需)
@return db.Sql object
ps: 默认访问data/default.db
"""
import db
sql = db.Sql()
return sql.table(table)
def HttpGet(url,timeout = 6,headers = {}):
"""
发送GET请求
@url 被请求的URL地址(必需)
@timeout 超时时间默认60秒
return string
@name 发送GET请求
@author hwliang<hwl@bt.cn>
@url 被请求的URL地址(必需)
@timeout 超时时间默认60秒
@return string
"""
if is_local(): return False
home = 'www.bt.cn'
@@ -53,6 +62,16 @@ def HttpGet(url,timeout = 6,headers = {}):
return s_body
def http_get_home(url,timeout,ex):
"""
@name Get方式使用优选节点访问官网
@author hwliang<hwl@bt.cn>
@param url 当前官网URL地址
@param timeout 用于测试超时时间
@param ex 上一次错误的响应内容
@return string 响应内容
如果已经是优选节点,将直接返回ex
"""
try:
home = 'www.bt.cn'
if url.find(home) == -1: return ex
@@ -72,6 +91,12 @@ def http_get_home(url,timeout,ex):
def set_home_host(host):
"""
@name 设置官网hosts
@author hwliang<hwl@bt.cn>
@param host IP地址
@return void
"""
ExecShell('sed -i "/www.bt.cn/d" /etc/hosts')
ExecShell("echo '' >> /etc/hosts")
ExecShell("echo '%s www.bt.cn' >> /etc/hosts" % host)
@@ -82,11 +107,11 @@ def httpGet(url,timeout=6):
def HttpPost(url,data,timeout = 6,headers = {}):
"""
发送POST请求
@url 被请求的URL地址(必需)
@data POST参数,可以是字符串或字典(必需)
@timeout 超时时间默认60秒
return string
发送POST请求
@url 被请求的URL地址(必需)
@data POST参数,可以是字符串或字典(必需)
@timeout 超时时间默认60秒
return string
"""
if is_local(): return False
home = 'www.bt.cn'
@@ -110,7 +135,18 @@ def HttpPost(url,data,timeout = 6,headers = {}):
return s_body
def http_post_home(url, data, timeout, ex):
def http_post_home(url,data,timeout,ex):
"""
@name POST方式使用优选节点访问官网
@author hwliang<hwl@bt.cn>
@param url(string) 当前官网URL地址
@param data(dict) POST数据
@param timeout(int) 用于测试超时时间
@param ex(string) 上一次错误的响应内容
@return string 响应内容
如果已经是优选节点,将直接返回ex
"""
try:
home = 'www.bt.cn'
if url.find(home) == -1: return ex
@@ -128,17 +164,26 @@ def http_post_home(url, data, timeout, ex):
return ex
except: return ex
def httpPost(url, data, timeout=6):
return HttpPost(url, data, timeout)
def httpPost(url,data,timeout=6):
"""
@name 发送POST请求
@author hwliang<hwl@bt.cn>
@param url 被请求的URL地址(必需)
@param data POST参数,可以是字符串或字典(必需)
@param timeout 超时时间默认60秒
@return string
"""
return HttpPost(url,data,timeout)
def check_home():
return True
def Md5(strings):
"""
生成MD5
@strings 要被处理的字符串
return string(32)
@name 生成MD5
@author hwliang<hwl@bt.cn>
@param strings 要被处理的字符串
@return string(32)
"""
if type(strings) == str:
strings = strings.encode()
@@ -152,9 +197,10 @@ def md5(strings):
def FileMd5(filename):
"""
生成文件的MD5
@filename 文件名
return string(32) or False
@name 生成文件的MD5
@author hwliang<hwl@bt.cn>
@param filename 文件名
@return string(32) or False
"""
if not os.path.isfile(filename): return False
import hashlib
@@ -171,9 +217,10 @@ def FileMd5(filename):
def GetRandomString(length):
"""
取随机字符串
@length 要获取的长度
return string(length)
@name 取随机字符串
@author hwliang<hwl@bt.cn>
@param length 要获取的长度
@return string(length)
"""
from random import Random
strings = ''
@@ -186,17 +233,32 @@ def GetRandomString(length):
def ReturnJson(status,msg,args=()):
"""
取通用Json返回
@status 返回状态
@msg 返回消息
return string(json)
@name 取通用Json返回
@author hwliang<hwl@bt.cn>
@param status 返回状态
@param msg 返回消息
@return string(json)
"""
return GetJson(ReturnMsg(status, msg, args))
def returnJson(status,msg,args=()):
"""
@name 取通用Json返回
@author hwliang<hwl@bt.cn>
@param status 返回状态
@param msg 返回消息
@return string(json)
"""
return ReturnJson(status,msg,args)
def ReturnMsg(status,msg,args = ()):
"""
@name 取通用dict返回
@author hwliang<hwl@bt.cn>
@param status 返回状态
@param msg 返回消息
@return dict {"status":bool,"msg":string}
"""
log_message = json.loads(ReadFile('BTPanel/static/language/' + GetLanguage() + '/public.json'))
keys = log_message.keys()
if type(msg) == str:
@@ -208,11 +270,23 @@ def ReturnMsg(status,msg,args = ()):
return {'status':status,'msg':msg}
def returnMsg(status,msg,args = ()):
"""
@name 取通用dict返回
@author hwliang<hwl@bt.cn>
@param status 返回状态
@param msg 返回消息
@return dict {"status":bool,"msg":string}
"""
return ReturnMsg(status,msg,args)
def GetFileMode(filename):
'''取文件权限'''
"""
@name 取文件权限字符串
@author hwliang<hwl@bt.cn>
@param filename 文件全路径
@return string 如:644/777/755
"""
stat = os.stat(filename)
accept = str(oct(stat.st_mode)[-3:])
return accept
@@ -1332,7 +1406,8 @@ def set_own(filename, user, group=None):
#校验路径安全
def path_safe_check(path,force=True):
checks = ['..','./','\\','%','$','^','&','*','~','#','"',"'",';','|','{','}','`']
if len(path) > 256: return False
checks = ['..','./','\\','%','$','^','&','*','~','"',"'",';','|','{','}','`']
for c in checks:
if path.find(c) != -1: return False
if force:
@@ -1573,22 +1648,22 @@ def en_hexb(data):
if type(result) != str: result = result.decode('utf-8')
return result
def upload_file_url(filename):
try:
if os.path.exists(filename):
data = ExecShell('/usr/bin/curl https://scanner.baidu.com/enqueue -F archive=@%s' % filename)
data = json.loads(data[0])
time.sleep(1)
import requests
default_headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
}
data_list = requests.get(url=data['url'], headers=default_headers, verify=False)
return (data_list.json())
else:
return False
except:
return False
# def upload_file_url(filename):
# try:
# if os.path.exists(filename):
# data = ExecShell('/usr/bin/curl https://scanner.baidu.com/enqueue -F archive=@%s' % filename)
# data = json.loads(data[0])
# time.sleep(1)
# import requests
# default_headers = {
# 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
# }
# data_list = requests.get(url=data['url'], headers=default_headers, verify=False)
# return (data_list.json())
# else:
# return False
# except:
# return False
#直接请求到PHP-FPM
#version php版本
@@ -1764,6 +1839,46 @@ def ip2long(ip):
iplong = 2 ** 24 * int(ips[0]) + 2 ** 16 * int(ips[1]) + 2 ** 8 * int(ips[2]) + int(ips[3])
return iplong
#获取sessionid
def get_session_id():
from BTPanel import request
return request.cookies.get('BT_PANEL_6')
def chdck_salt():
'''
@name 检查所有用户密码是否加盐,若没有则自动加上
@author hwliang<2020-07-08>
@return void
'''
if not M('sqlite_master').where('type=? AND name=? AND sql LIKE ?', ('table', 'users','%salt%')).count():
M('users').execute("ALTER TABLE 'users' ADD 'salt' TEXT",())
u_list = M('users').where('salt is NULL',()).field('id,username,password,salt').select()
for u_info in u_list:
salt = GetRandomString(12) #12位随机
pdata = {}
pdata['password'] = md5(md5(u_info['password']+'_bt.cn') + salt)
pdata['salt'] = salt
M('users').where('id=?',(u_info['id'],)).update(pdata)
def password_salt(password,username=None,uid=None):
'''
@name 为指定密码加盐
@author hwliang<2020-07-08>
@param password string(被md5加密一次的密码)
@param username string(用户名) 可选
@param uid int(uid) 可选
@return string
'''
chdck_salt()
if not uid:
if not username:
raise Exception('username或uid必需传一项')
uid = M('users').where('username=?',(username,)).getField('id')
salt = M('users').where('id=?',(uid,)).getField('salt')
return md5(md5(password+'_bt.cn')+salt)
#取通用对象
class dict_obj:
def __contains__(self, key):
+44 -24
View File
@@ -50,54 +50,74 @@ class system:
serviceName = 'nginx'
tmp['setup'] = False
phpversion = "54"
phpport = '888';
pstatus = False;
pauth = False;
phpport = '888'
pstatus = False
pauth = False
if os.path.exists(self.setupPath+'/nginx'):
data['webserver'] = 'nginx'
serviceName = 'nginx'
tmp['setup'] = os.path.exists(self.setupPath +'/nginx/sbin/nginx');
configFile = self.setupPath + '/nginx/conf/nginx.conf';
tmp['setup'] = os.path.exists(self.setupPath +'/nginx/sbin/nginx')
configFile = self.setupPath + '/nginx/conf/nginx.conf'
try:
if os.path.exists(configFile):
conf = public.readFile(configFile);
rep = "listen\s+([0-9]+)\s*;";
rtmp = re.search(rep,conf);
conf = public.readFile(configFile)
rep = "listen\s+([0-9]+)\s*;"
rtmp = re.search(rep,conf)
if rtmp:
phpport = rtmp.groups()[0];
phpport = rtmp.groups()[0]
if conf.find('AUTH_START') != -1: pauth = True;
if conf.find(self.setupPath + '/stop') == -1: pstatus = True;
configFile = self.setupPath + '/nginx/conf/enable-php.conf';
conf = public.readFile(configFile);
rep = "php-cgi-([0-9]+)\.sock";
rtmp = re.search(rep,conf);
configFile = self.setupPath + '/nginx/conf/enable-php.conf'
conf = public.readFile(configFile)
rep = "php-cgi-([0-9]+)\.sock"
rtmp = re.search(rep,conf)
if rtmp:
phpversion = rtmp.groups()[0];
phpversion = rtmp.groups()[0]
except:
pass;
elif os.path.exists(self.setupPath+'/apache'):
data['webserver'] = 'apache'
serviceName = 'httpd'
tmp['setup'] = os.path.exists(self.setupPath +'/apache/bin/httpd');
configFile = self.setupPath + '/apache/conf/extra/httpd-vhosts.conf';
tmp['setup'] = os.path.exists(self.setupPath +'/apache/bin/httpd')
configFile = self.setupPath + '/apache/conf/extra/httpd-vhosts.conf'
try:
if os.path.exists(configFile):
conf = public.readFile(configFile);
rep = "php-cgi-([0-9]+)\.sock";
rtmp = re.search(rep,conf);
conf = public.readFile(configFile)
rep = "php-cgi-([0-9]+)\.sock"
rtmp = re.search(rep,conf)
if rtmp:
phpversion = rtmp.groups()[0];
rep = "Listen\s+([0-9]+)\s*\n";
rtmp = re.search(rep,conf);
phpversion = rtmp.groups()[0]
rep = "Listen\s+([0-9]+)\s*\n"
rtmp = re.search(rep,conf)
if rtmp:
phpport = rtmp.groups()[0];
phpport = rtmp.groups()[0]
if conf.find('AUTH_START') != -1: pauth = True;
if conf.find(self.setupPath + '/stop') == -1: pstatus = True;
except:
pass
elif os.path.exists('/usr/local/lsws/bin/lswsctrl'):
data['webserver'] = 'openlitespeed'
serviceName = 'openlitespeed'
tmp['setup'] = os.path.exists(self.setupPath +'/apache/bin/httpd')
configFile = '/usr/local/lsws/bin/lswsctrl'
try:
if os.path.exists(configFile):
conf = public.readFile('/www/server/panel/vhost/openlitespeed/detail/phpmyadmin.conf')
rep = "/usr/local/lsws/lsphp(\d+)/bin/lsphp"
rtmp = re.search(rep,conf)
if rtmp:
phpversion = rtmp.groups()[0]
conf = public.readFile('/www/server/panel/vhost/openlitespeed/listen/888.conf')
rep = "address\s+\*\:(\d+)"
rtmp = re.search(rep,conf)
if rtmp:
phpport = rtmp.groups()[0]
if conf.find('AUTH_START') != -1: pauth = True
if conf.find(self.setupPath + '/stop') == -1: pstatus = True
except:
pass
tmp['type'] = data['webserver']
tmp['version'] = public.readFile(self.setupPath + '/'+data['webserver']+'/version.pl');
+14 -9
View File
@@ -7,7 +7,7 @@
# | Author: hwliang <hwl@bt.cn>
# +-------------------------------------------------------------------
import public,os,sys,db,time,json
import public,os,sys,db,time,json,re
from BTPanel import session,cache,json_header
from flask import request,redirect,g
@@ -19,14 +19,13 @@ class userlogin:
self.error_num(False)
if self.limit_address('?') < 1: return public.returnJson(False,'LOGIN_ERR_LIMIT'),json_header
post.username = post.username.strip()
password = public.md5(post.password.strip())
sql = db.Sql()
user_list = sql.table('users').field('id,username,password').select()
user_list = sql.table('users').field('id,username,password,salt').select()
userInfo = None
for u_info in user_list:
if u_info['username'] == post.username:
if public.md5(u_info['username']) == post.username:
userInfo = u_info
if 'code' in session:
if session['code'] and not 'is_verify_password' in session:
@@ -35,14 +34,18 @@ class userlogin:
public.WriteLog('TYPE_LOGIN','LOGIN_ERR_CODE',('****','****',public.GetClientIp()))
return public.returnJson(False,'CODE_ERR'),json_header
try:
s_pass = public.md5(public.md5(userInfo['password'] + '_bt.cn'))
if userInfo['username'] != post.username or s_pass != password:
if not userInfo['salt']:
public.chdck_salt()
userInfo = sql.table('users').where('id=?',(userInfo['id'],)).field('id,username,password,salt').find()
password = public.md5(post.password.strip() + userInfo['salt'])
if public.md5(userInfo['username']) != post.username or userInfo['password'] != password:
public.WriteLog('TYPE_LOGIN','LOGIN_ERR_PASS',('****','******',public.GetClientIp()))
num = self.limit_address('+')
return public.returnJson(False,'LOGIN_USER_ERR',(str(num),)),json_header
_key_file = "/www/server/panel/data/two_step_auth.txt"
if hasattr(post,'vcode'):
if self.limit_address('?',v="vcode") < 1: return public.returnJson(False,'您多次验证失败,禁止10分钟'),json_header
if self.limit_address('?',v="vcode") < 1: return public.returnJson(False,'You have failed verification many times, forbidden for 10 minutes'),json_header
import pyotp
secret_key = public.readFile(_key_file)
if not secret_key:
@@ -82,6 +85,8 @@ class userlogin:
def request_tmp(self,get):
try:
if not hasattr(get,'tmp_token'): return public.returnJson(False,'INIT_ARGS_ERR'),json_header
if len(get.tmp_token) != 64: return public.returnJson(False,'INIT_ARGS_ERR'),json_header
if not re.match(r"^\w+$",get.tmp_token):return public.returnJson(False,'INIT_ARGS_ERR'),json_header
save_path = '/www/server/panel/config/api.json'
data = json.loads(public.ReadFile(save_path))
if not 'tmp_token' in data or not 'tmp_time' in data: return public.returnJson(False,'VERIFICATION_FAILED'),json_header
@@ -91,7 +96,7 @@ class userlogin:
session['login'] = True
session['username'] = userInfo['username']
session['tmp_login'] = True
public.WriteLog('TYPE_LOGIN','LOGIN_SUCCESS',(userInfo['username'],public.GetClientIp()))
public.WriteLog('TYPE_LOGIN','LOGIN_SUCCESS',(userInfo['username'],public.GetClientIp()+ ":" + str(request.environ.get('REMOTE_PORT'))))
self.limit_address('-')
cache.delete('panelNum')
cache.delete('dologin')
-2
View File
@@ -45,7 +45,6 @@ def ExecShell(cmdstring, cwd=None, timeout=None, shell=True):
import datetime
import subprocess
import time
subprocess.Popen('echo > ' + logPath, cwd=cwd, stdin=subprocess.PIPE, shell=shell, bufsize=4096)
sub = subprocess.Popen(cmdstring+' &> '+logPath, cwd=cwd, stdin=subprocess.PIPE,shell=shell,bufsize=4096)
while sub.poll() is None:
@@ -434,7 +433,6 @@ def check502Task():
# 检查面板证书是否有更新
def check_panel_ssl():
# info_file=""
try:
while True:
lets_info = public.readFile("/www/server/panel/ssl/lets.info")
+23 -5
View File
@@ -67,7 +67,7 @@ echo "The root password set ${pwd} successuful"''';
def set_panel_pwd(password,ncli = False):
import db
sql = db.Sql()
result = sql.table('users').where('id=?',(1,)).setField('password',public.md5(password))
result = sql.table('users').where('id=?',(1,)).setField('password',public.password_salt(public.md5(password),uid=1))
username = sql.table('users').where('id=?',(1,)).getField('username')
if ncli:
print("|-%s: " % public.GetMsg("USER_NAME") + username)
@@ -159,12 +159,30 @@ history -c
'''
os.system(command)
print('\t\033[1;32m[done]\033[0m')
public.writeFile('/www/server/panel/install.pl',"True")
print("|-Please select user initialization method:")
print("="*50)
print(" (1) Display the initialization page when accessing the panel page")
print(" (2) A new account password is automatically generated randomly when first started")
print("="*50)
p_input = input("Please select the initialization method (default: 1):")
print(p_input)
if p_input in [2,'2']:
public.writeFile('/www/server/panel/aliyun.pl',"True")
s_file = '/www/server/panel/install.pl'
if os.path.exists(s_file): os.remove(s_file)
public.M('config').where("id=?",('1',)).setField('status',1)
else:
public.writeFile('/www/server/panel/install.pl',"True")
public.M('config').where("id=?",('1',)).setField('status',0)
port = public.readFile('data/port.pl').strip()
public.M('config').where("id=?",('1',)).setField('status',0)
print('========================================================')
print('\033[1;32m|-'+public.GetMsg("PANEL_TIPS")+'\033[0m')
print('\033[1;41m|-'+public.GetMsg("PANEL_INIT_ADD")+': http://{SERVERIP}:'+port+'/install\033[0m')
print('\033[1;32m|-The panel packaging is successful, please do not log in to the panel to do any other operations!\033[0m')
if not p_input in [2,'2']:
print('\033[1;41m|-Panel initialization address:http://{SERVERIP}:'+port+'/install\033[0m')
else:
print('\033[1;41m|-Get the initial account password command:bt default \033[0m')
#清空正在执行的任务
def CloseTask():