mirror of
https://github.com/aaPanel/aaPanel.git
synced 2026-09-29 21:04:52 +02:00
v6.8.12
1. Add disk IO information to the homepage 2. Add a clear list button in the upload window 3. Optimize the background task overhead of the panel 4. Optimize the disk information caching mechanism 5. Panel Pro edition is online 6. Optimize panel resource usage 7. Optimize SSL certificate renewal 8. Fix the problem of reporting an error when the security entrance is empty 9. Fix the problem of infinite recursion when copying directories in extreme cases
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# 宝塔Linux面板
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
# Author: linxiao
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 数据库备份权限检测
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
import os, re, public, panelMysql
|
||||
|
||||
_title = 'Database backup permission detection'
|
||||
_version = 1.0 # 版本
|
||||
_ps = "Check whether the MySQL root user has database backup permissions" # 描述
|
||||
_level = 3 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_date = '2020-09-19' # 最后更新时间
|
||||
_ignore = os.path.exists("data/warning/ignore/sw_database_priv.pl")
|
||||
_tips = [
|
||||
"To temporarily access the database without authorization, it is recommended to restore all permissions of the root user.",
|
||||
]
|
||||
|
||||
_help = ''
|
||||
|
||||
|
||||
def check_run():
|
||||
"""检测root用户是否具备数据库备份权限
|
||||
|
||||
@author linxiao<2020-9-18>
|
||||
@return (bool, msg)
|
||||
"""
|
||||
mycnf_file = '/etc/my.cnf'
|
||||
if not os.path.exists(mycnf_file):
|
||||
return True, 'Risk-free'
|
||||
mycnf = public.readFile(mycnf_file)
|
||||
port_tmp = re.findall(r"port\s*=\s*(\d+)", mycnf)
|
||||
if not port_tmp:
|
||||
return True, 'Risk-free'
|
||||
if not public.ExecShell("lsof -i :{}".format(port_tmp[0]))[0]:
|
||||
return True, 'Risk-free'
|
||||
|
||||
base_backup_privs = ["Lock_tables_priv", "Select_priv"]
|
||||
select_sql = "Select {} FROM mysql.user WHERE user='root' and " \
|
||||
"host=SUBSTRING_INDEX((select current_user()),'@', " \
|
||||
"-1);".format(",".join(base_backup_privs))
|
||||
select_result = panelMysql.panelMysql().query(select_sql)
|
||||
if not select_result:
|
||||
return False, "The root user has insufficient authority to execute mysqldump backup."
|
||||
select_result = select_result[0]
|
||||
for priv in select_result:
|
||||
if priv.lower() != "y":
|
||||
return False, "The root user has insufficient authority to execute mysqldump backup."
|
||||
return True, 'Risk-free'
|
||||
@@ -18,7 +18,7 @@ import os,sys,re,public
|
||||
_title = 'System directory permissions'
|
||||
_version = 1.0 # 版本
|
||||
_ps = "Checks if the System directory permissions are correct" # 描述
|
||||
_level = 2 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_level = 0 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_date = '2020-08-05' # 最后更新时间
|
||||
_ignore = os.path.exists("data/warning/ignore/sw_dir_mode.pl")
|
||||
_tips = [
|
||||
@@ -45,8 +45,8 @@ def check_run():
|
||||
['/usr/local',755,'root'],
|
||||
['/etc',755,'root'],
|
||||
['/etc/passwd',644,'root'],
|
||||
['/etc/shadow',000,'root'],
|
||||
['/etc/gshadow',000,'root'],
|
||||
['/etc/shadow',600,'root'],
|
||||
['/etc/gshadow',600,'root'],
|
||||
['/etc/cron.deny',600,'root'],
|
||||
['/etc/anacrontab',600,'root'],
|
||||
['/var',755,'root'],
|
||||
@@ -56,6 +56,7 @@ def check_run():
|
||||
['/var/spool/cron/crontabs/root',600,'root'],
|
||||
['/www',755,'root'],
|
||||
['/www/server',755,'root'],
|
||||
['/www/wwwroot',755,'root'],
|
||||
['/root',550,'root'],
|
||||
['/mnt',755,'root'],
|
||||
['/home',755,'root'],
|
||||
@@ -67,15 +68,15 @@ def check_run():
|
||||
]
|
||||
|
||||
not_mode_list = []
|
||||
for d in dir_list:
|
||||
if not os.path.exists(d[0]): continue
|
||||
u_mode = public.get_mode_and_user(d[0])
|
||||
if u_mode['user'] != d[2]:
|
||||
not_mode_list.append("{} Current permissions: {} : {} Security permissions: {} : {}".format(d[0],u_mode['mode'],u_mode['user'],d[1],d[2]))
|
||||
if int(u_mode['mode']) != d[1]:
|
||||
not_mode_list.append("{} Current permissions: {} : {} Security permissions: {} : {}".format(d[0],u_mode['mode'],u_mode['user'],d[1],d[2]))
|
||||
# for d in dir_list:
|
||||
# if not os.path.exists(d[0]): continue
|
||||
# u_mode = public.get_mode_and_user(d[0])
|
||||
# if u_mode['user'] != d[2]:
|
||||
# not_mode_list.append("{} 当前权限: {} : {} 安全权限: {} : {}".format(d[0],u_mode['mode'],u_mode['user'],d[1],d[2]))
|
||||
# if int(u_mode['mode']) != d[1]:
|
||||
# not_mode_list.append("{} 当前权限: {} : {} 安全权限: {} : {}".format(d[0],u_mode['mode'],u_mode['user'],d[1],d[2]))
|
||||
|
||||
if not_mode_list:
|
||||
return False,'The following system file or directory permissions are incorrect: <br />' + ("<br />".join(not_mode_list))
|
||||
# if not_mode_list:
|
||||
# return False,'以下关键文件或目录权限错误: <br />' + ("<br />".join(not_mode_list))
|
||||
|
||||
return True,'Risk-free'
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# 宝塔Linux面板
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
# Author: linxiao
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# FTP弱口令检测
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
import os, re#, public
|
||||
|
||||
_title = 'FTP service weak password detection'
|
||||
_version = 1.0 # 版本
|
||||
_ps = "Detect weak passwords for the enabled FTP service" # 描述
|
||||
_level = 2 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_date = '2020-09-19' # 最后更新时间
|
||||
_ignore = os.path.exists("data/warning/ignore/sw_ftp_pass.pl")
|
||||
_tips = [
|
||||
"Please go to [FTP] page to change FTP password",
|
||||
"Note: Please do not use too simple account and password, so as not to cause security risks",
|
||||
"Use [Fail2ban] plug-in to protect FTP service"
|
||||
]
|
||||
|
||||
_help = ''
|
||||
_topic = "ftp"
|
||||
|
||||
|
||||
def check_run():
|
||||
"""检测FTP弱口令
|
||||
|
||||
@author linxiao<2020-9-19>
|
||||
@return (bool, msg)
|
||||
"""
|
||||
|
||||
ftp_list = public.M("ftps").field("name,password,status").select()
|
||||
if not ftp_list:
|
||||
return True, 'Risk-free'
|
||||
weak_pass_ftp = []
|
||||
for ftp_info in ftp_list:
|
||||
status = ftp_info["status"]
|
||||
if status == "0" or status == 0:
|
||||
continue
|
||||
login_name = ftp_info["name"]
|
||||
login_pass = ftp_info["password"]
|
||||
if not is_strong_password(login_pass):
|
||||
weak_pass_ftp.append(login_name)
|
||||
|
||||
if weak_pass_ftp:
|
||||
return False, "The following FTP service password settings are too simple and pose security risks: <br />" + \
|
||||
"<br />".join(weak_pass_ftp)
|
||||
return True, 'Risk-free'
|
||||
|
||||
|
||||
def is_strong_password(password):
|
||||
"""判断密码复杂度是否安全
|
||||
|
||||
非弱口令标准:长度大于等于7,分别包含数字、小写、大写、特殊字符。
|
||||
@password: 密码文本
|
||||
@return: True/False
|
||||
@author: linxiao<2020-9-19>
|
||||
"""
|
||||
|
||||
if len(password) < 7:
|
||||
return False
|
||||
|
||||
import re
|
||||
digit_reg = "[0-9]" # 匹配数字 +1
|
||||
lower_case_letters_reg = "[a-z]" # 匹配小写字母 +1
|
||||
upper_case_letters_reg = "[A-Z]" # 匹配大写字母 +1
|
||||
special_characters_reg = r"((?=[\x21-\x7e]+)[^A-Za-z0-9])" # 匹配特殊字符 +1
|
||||
|
||||
regs = [digit_reg,
|
||||
lower_case_letters_reg,
|
||||
upper_case_letters_reg,
|
||||
special_characters_reg]
|
||||
|
||||
grade = 0
|
||||
for reg in regs:
|
||||
if re.search(reg, password):
|
||||
grade += 1
|
||||
|
||||
if grade == 4 or (grade >= 2 and len(password) >= 9):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# passwords = ["000000", "aaaaaaa", "Ab2aaaaaa"]
|
||||
# for p in passwords:
|
||||
# if is_strong_password(p):
|
||||
# print("密码:{} 安全性高。".format(p))
|
||||
# else:
|
||||
# print("密码:{} 安全性弱, 建议更换密码。".format(p))
|
||||
@@ -18,7 +18,7 @@ import os,sys,re,public
|
||||
_title = 'SSH user login notification'
|
||||
_version = 1.0 # 版本
|
||||
_ps = "Check whether SSH user login notification is enabled" # 描述
|
||||
_level = 1 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_level = 0 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_date = '2020-08-05' # 最后更新时间
|
||||
_ignore = os.path.exists("data/warning/ignore/sw_login_message.pl")
|
||||
_tips = [
|
||||
@@ -28,6 +28,12 @@ _tips = [
|
||||
_help = ''
|
||||
|
||||
|
||||
def return_bashrc():
|
||||
if os.path.exists('/root/.bashrc'):return '/root/.bashrc'
|
||||
if os.path.exists('/etc/bashrc'):return '/etc/bashrc'
|
||||
if os.path.exists('/etc/bash.bashrc'):return '/etc/bash.bashrc'
|
||||
return '/root/.bashrc'
|
||||
|
||||
def check_run():
|
||||
'''
|
||||
@name 开始检测
|
||||
@@ -35,9 +41,9 @@ def check_run():
|
||||
@return tuple (status<bool>,msg<string>)
|
||||
'''
|
||||
|
||||
data = public.ReadFile('/etc/bashrc')
|
||||
data = public.ReadFile(return_bashrc())
|
||||
if not data: return True,'Risk-free'
|
||||
if re.search('python /www/server/panel/class/ssh_security.py login', data):
|
||||
if re.search('ssh_security.py login', data):
|
||||
return True,'Risk-free'
|
||||
else:
|
||||
return False,'SSH user login notification is not configured, so it is impossible to know whether the server has been illegally logged in in the first place'
|
||||
@@ -18,7 +18,7 @@ import os,sys,re,public
|
||||
_title = 'Risk User'
|
||||
_version = 1.0 # 版本
|
||||
_ps = "Detect if there is a risk user in the system user list" # 描述
|
||||
_level = 2 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_level = 0 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_date = '2020-08-05' # 最后更新时间
|
||||
_ignore = os.path.exists("data/warning/ignore/sw_login_user.pl")
|
||||
_tips = [
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# MySQL端口安全检测
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
import os,sys,re,public
|
||||
import os,sys,re,public,json
|
||||
|
||||
_title = 'MySQL security'
|
||||
_version = 1.0 # 版本
|
||||
@@ -50,9 +50,17 @@ def check_run():
|
||||
return True,'MySQL is not installed'
|
||||
if not public.ExecShell("lsof -i :{}".format(port_tmp[0]))[0]:
|
||||
return True,'MySQL is not installed'
|
||||
result = public.check_port_stat(int(port_tmp[0]),public.GetClientIp())
|
||||
result = public.check_port_stat(int(port_tmp[0]),public.GetLocalIp())
|
||||
if result == 0:
|
||||
return True,'Risk-free'
|
||||
|
||||
return False,'The current MySQL port: {}, which can be accessed by any server, which may cause MySQL to be cracked by brute force, posing security risks'.format(port_tmp[0])
|
||||
|
||||
fail2ban_file = '/www/server/panel/plugin/fail2ban/config.json'
|
||||
if os.path.exists(fail2ban_file):
|
||||
try:
|
||||
fail2ban_config = json.loads(public.readFile(fail2ban_file))
|
||||
if 'mysql' in fail2ban_config.keys():
|
||||
if fail2ban_config['mysql']['act'] == 'true':
|
||||
return True,'Fail2ban is enabled'
|
||||
except: pass
|
||||
|
||||
return False,'当前MySQL端口: {},可被任意服务器访问,这可能导致MySQL被暴力破解,存在安全隐患'.format(port_tmp[0])
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# 宝塔Linux面板
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
# Author: linxiao
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 数据库备份权限检测
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
import os, re, public, panelMysql
|
||||
|
||||
_title = 'Database backup permission detection'
|
||||
_version = 1.0 # 版本
|
||||
_ps = "Check whether the MySQL root user has database backup permissions" # 描述
|
||||
_level = 3 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_date = '2020-09-19' # 最后更新时间
|
||||
_ignore = os.path.exists("data/warning/ignore/sw_database_priv.pl")
|
||||
_tips = [
|
||||
"To temporarily access the database without authorization, it is recommended to restore all permissions of the root user.",
|
||||
]
|
||||
|
||||
_help = ''
|
||||
|
||||
|
||||
def check_run():
|
||||
"""检测root用户是否具备数据库备份权限
|
||||
|
||||
@author linxiao<2020-9-18>
|
||||
@return (bool, msg)
|
||||
"""
|
||||
mycnf_file = '/etc/my.cnf'
|
||||
if not os.path.exists(mycnf_file):
|
||||
return True, 'Risk-free'
|
||||
mycnf = public.readFile(mycnf_file)
|
||||
port_tmp = re.findall(r"port\s*=\s*(\d+)", mycnf)
|
||||
if not port_tmp:
|
||||
return True, 'Risk-free'
|
||||
if not public.ExecShell("lsof -i :{}".format(port_tmp[0]))[0]:
|
||||
return True, 'Risk-free'
|
||||
|
||||
base_backup_privs = ["Lock_tables_priv", "Select_priv"]
|
||||
select_sql = "Select {} FROM mysql.user WHERE user='root' and " \
|
||||
"host=SUBSTRING_INDEX((select current_user()),'@', " \
|
||||
"-1);".format(",".join(base_backup_privs))
|
||||
select_result = panelMysql.panelMysql().query(select_sql)
|
||||
if not select_result:
|
||||
return False, "The root user has insufficient authority to execute mysqldump backup."
|
||||
select_result = select_result[0]
|
||||
for priv in select_result:
|
||||
if priv.lower() != "y":
|
||||
return False, "The root user has insufficient authority to execute mysqldump backup."
|
||||
return True, 'Risk-free'
|
||||
@@ -17,7 +17,7 @@ import os,sys,re,public
|
||||
_title = 'Panel password'
|
||||
_version = 1.0 # 版本
|
||||
_ps = "Check whether the panel account password is safe" # 描述
|
||||
_level = 3 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_level = 0 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_date = '2020-08-04' # 最后更新时间
|
||||
_ignore = os.path.exists("data/warning/ignore/sw_panel_pass.pl")
|
||||
_tips = [
|
||||
@@ -44,6 +44,7 @@ def check_run():
|
||||
return False,'The default password of the panel has not been modified, and there is a security risk'
|
||||
|
||||
lower_pass_txt = '''12123
|
||||
china
|
||||
test
|
||||
test12
|
||||
test11
|
||||
@@ -1097,6 +1098,8 @@ winner
|
||||
p1 = password_salt(public.md5(lp),uid=1)
|
||||
if p1 == find['password']:
|
||||
return False,'The current panel password is too simple and there is a security risk'
|
||||
if not is_strong_password(find["password"]):
|
||||
return False, 'The current panel password is too simple and there is a security risk'
|
||||
return True,'Risk-free'
|
||||
|
||||
salt = None
|
||||
@@ -1113,5 +1116,41 @@ def password_salt(password,username=None,uid=None):
|
||||
global salt
|
||||
if not salt:
|
||||
salt = public.M('users').where('id=?',(uid,)).getField('salt')
|
||||
if salt:
|
||||
salt = salt[0]
|
||||
else:
|
||||
salt = ""
|
||||
return public.md5(public.md5(password+'_bt.cn')+salt)
|
||||
|
||||
|
||||
|
||||
def is_strong_password(password):
|
||||
"""判断密码复杂度是否安全
|
||||
|
||||
非弱口令标准:长度大于等于7,分别包含数字、小写、大写、特殊字符。
|
||||
@password: 密码文本
|
||||
@return: True/False
|
||||
@author: linxiao<2020-9-19>
|
||||
"""
|
||||
|
||||
if len(password) < 7:
|
||||
return False
|
||||
|
||||
import re
|
||||
digit_reg = "[0-9]" # 匹配数字 +1
|
||||
lower_case_letters_reg = "[a-z]" # 匹配小写字母 +1
|
||||
upper_case_letters_reg = "[A-Z]" # 匹配大写字母 +1
|
||||
special_characters_reg = r"((?=[\x21-\x7e]+)[^A-Za-z0-9])" # 匹配特殊字符 +1
|
||||
|
||||
regs = [digit_reg,
|
||||
lower_case_letters_reg,
|
||||
upper_case_letters_reg,
|
||||
special_characters_reg]
|
||||
|
||||
grade = 0
|
||||
for reg in regs:
|
||||
if re.search(reg, password):
|
||||
grade += 1
|
||||
|
||||
if grade == 4 or (grade == 3 and len(password) >= 9):
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -18,7 +18,7 @@ import os,sys,re,public
|
||||
_title = 'ICMP detection'
|
||||
_version = 1.0 # 版本
|
||||
_ps = "Check whether ICMP access is allowed (Block ICMP)" # 描述
|
||||
_level = 1 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_level = 0 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_date = '2020-08-05' # 最后更新时间
|
||||
_ignore = os.path.exists("data/warning/ignore/sw_ping.pl")
|
||||
_tips = [
|
||||
|
||||
+33
-1046
File diff suppressed because it is too large
Load Diff
@@ -13,12 +13,12 @@
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
|
||||
import os,sys,re,public
|
||||
import os,sys,re,public,json
|
||||
|
||||
_title = 'SSH security'
|
||||
_version = 1.0 # 版本
|
||||
_ps = "Check whether the SSH port of the current server is safe" # 描述
|
||||
_level = 2 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_level = 1 # 风险级别: 1.提示(低) 2.警告(中) 3.危险(高)
|
||||
_date = '2020-08-04' # 最后更新时间
|
||||
_ignore = os.path.exists("data/warning/ignore/sw_ssh_port.pl")
|
||||
_tips = [
|
||||
@@ -72,7 +72,16 @@ def check_run():
|
||||
status = public.ExecShell("systemctl status sshd.service | grep 'dead'|grep -v grep")
|
||||
else:
|
||||
status = public.ExecShell("/etc/init.d/sshd status | grep -e 'stopped' -e '已停'|grep -v grep")
|
||||
|
||||
|
||||
fail2ban_file = '/www/server/panel/plugin/fail2ban/config.json'
|
||||
if os.path.exists(fail2ban_file):
|
||||
try:
|
||||
fail2ban_config = json.loads(public.readFile(fail2ban_file))
|
||||
if 'sshd' in fail2ban_config.keys():
|
||||
if fail2ban_config['sshd']['act'] == 'true':
|
||||
return True,'Fail2ban is enable'
|
||||
except: pass
|
||||
|
||||
if len(status[0]) > 3:
|
||||
status = False
|
||||
else:
|
||||
@@ -83,7 +92,7 @@ def check_run():
|
||||
if port != '22':
|
||||
return True,'The default SSH port has been modified'
|
||||
|
||||
result = public.check_port_stat(int(port),public.GetClientIp())
|
||||
result = public.check_port_stat(int(port),public.GetLocalIp())
|
||||
if result == 0:
|
||||
return True,'Rick-free'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user