Initial YakPanel commit
This commit is contained in:
BIN
class_v2/safeModelV2/__pycache__/freeipModel.cpython-314.pyc
Normal file
BIN
class_v2/safeModelV2/__pycache__/freeipModel.cpython-314.pyc
Normal file
Binary file not shown.
121
class_v2/safeModelV2/base.py
Normal file
121
class_v2/safeModelV2/base.py
Normal file
@@ -0,0 +1,121 @@
|
||||
#coding: utf-8
|
||||
import public,re,time,sys,os
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class safeBase:
|
||||
|
||||
__isUfw = False
|
||||
__isFirewalld = False
|
||||
_months = {'Jan':'01','Feb':'02','Mar':'03','Apr':'04','May':'05','Jun':'06','Jul':'07','Aug':'08','Sep':'09','Sept':'09','Oct':'10','Nov':'11','Dec':'12'}
|
||||
|
||||
def __init__(self):
|
||||
if os.path.exists('/usr/sbin/firewalld'): self.__isFirewalld = True
|
||||
if os.path.exists('/usr/sbin/ufw'): self.__isUfw = True
|
||||
|
||||
#转换时间格式
|
||||
def to_date(self,date_str):
|
||||
tmp = re.split(r'\s+',date_str)
|
||||
if len(tmp) < 3: return date_str
|
||||
s_date = str(datetime.now().year) + '-' + self._months.get(tmp[0]) + '-' + tmp[1] + ' ' + tmp[2]
|
||||
time_array = time.strptime(s_date, "%Y-%m-%d %H:%M:%S")
|
||||
time_stamp = int(time.mktime(time_array))
|
||||
return time_stamp
|
||||
|
||||
|
||||
def to_date2(self,date_str):
|
||||
tmp = date_str.split()
|
||||
if len(tmp) < 4: return date_str
|
||||
s_date = str(tmp[-1]) + '-' + self._months.get(tmp[1],tmp[1]) + '-' + tmp[2] + ' ' + tmp[3]
|
||||
return s_date
|
||||
|
||||
def to_date3(self,date_str):
|
||||
tmp = date_str.split()
|
||||
if len(tmp) < 4: return date_str
|
||||
s_date = str(datetime.now().year) + '-' + self._months.get(tmp[1],tmp[1]) + '-' + tmp[2] + ' ' + tmp[3]
|
||||
return s_date
|
||||
|
||||
def to_date4(self,date_str):
|
||||
tmp = date_str.split()
|
||||
if len(tmp) < 3: return date_str
|
||||
s_date = str(datetime.now().year) + '-' + self._months.get(tmp[0],tmp[0]) + '-' + tmp[1] + ' ' + tmp[2]
|
||||
return s_date
|
||||
|
||||
|
||||
#取防火墙状态
|
||||
def CheckFirewallStatus(self):
|
||||
if self.__isUfw:
|
||||
res = public.ExecShell('ufw status verbose')[0]
|
||||
if res.find('inactive') != -1: return False
|
||||
return True
|
||||
|
||||
if self.__isFirewalld:
|
||||
res = public.ExecShell("systemctl status firewalld")[0]
|
||||
if res.find('active (running)') != -1: return True
|
||||
if res.find('disabled') != -1: return False
|
||||
if res.find('inactive (dead)') != -1: return False
|
||||
else:
|
||||
res = public.ExecShell("/etc/init.d/iptables status")[0]
|
||||
if res.find('not running') != -1: return False
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_ssh_log_files(self,get):
|
||||
"""
|
||||
获取ssh日志文件
|
||||
"""
|
||||
s_key = 'secure'
|
||||
if not os.path.exists('/var/log/secure'):
|
||||
s_key = 'auth.log'
|
||||
if os.path.exists('/var/log/secure') and os.path.getsize('/var/log/secure') == 0:
|
||||
s_key = 'auth.log'
|
||||
|
||||
res = []
|
||||
spath = '/var/log/'
|
||||
for fname in os.listdir(spath):
|
||||
fpath = '{}{}'.format(spath,fname)
|
||||
if fname.find(s_key) == -1 or fname == s_key:
|
||||
continue
|
||||
|
||||
#debian解压日志
|
||||
if fname[-3:] in ['.gz','.xz']:
|
||||
if os.path.exists(fpath[:-3]):
|
||||
continue
|
||||
public.ExecShell("gunzip -c " + fpath + " > " + fpath[:-3])
|
||||
res.append(fpath[:-3])
|
||||
else:
|
||||
res.append(fpath)
|
||||
|
||||
res = sorted(res,reverse=True)
|
||||
res.insert(0,spath + s_key)
|
||||
return res
|
||||
|
||||
def get_ssh_log_files_list(self,get):
|
||||
"""
|
||||
获取ssh日志文件
|
||||
"""
|
||||
s_key = 'secure'
|
||||
if not os.path.exists('/var/log/secure'):
|
||||
s_key = 'auth.log'
|
||||
if os.path.exists('/var/log/secure') and os.path.getsize('/var/log/secure') == 0:
|
||||
s_key = 'auth.log'
|
||||
|
||||
res = []
|
||||
spath = '/var/log/'
|
||||
for fname in os.listdir(spath):
|
||||
fpath = '{}{}'.format(spath,fname)
|
||||
if fname.find(s_key) == -1 or fname == s_key:
|
||||
continue
|
||||
#debian解压日志
|
||||
if fname[-3:] in ['.gz','.xz']:
|
||||
continue
|
||||
if os.path.getsize(fpath) > 1024 * 1024 * 100:
|
||||
continue
|
||||
#判断文件数量为15个
|
||||
if len(res) > 15:
|
||||
break
|
||||
res.append(fpath)
|
||||
res = sorted(res,reverse=True)
|
||||
res.insert(0,spath + s_key)
|
||||
return res
|
||||
4168
class_v2/safeModelV2/firewallModel.py
Normal file
4168
class_v2/safeModelV2/firewallModel.py
Normal file
File diff suppressed because it is too large
Load Diff
102
class_v2/safeModelV2/freeipModel.py
Normal file
102
class_v2/safeModelV2/freeipModel.py
Normal file
@@ -0,0 +1,102 @@
|
||||
#coding: utf-8
|
||||
#-------------------------------------------------------------------
|
||||
# YakPanel
|
||||
#-------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 YakPanel(www.yakpanel.com) All rights reserved.
|
||||
#-------------------------------------------------------------------
|
||||
# Author: cjxin <cjxin@yakpanel.com>
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
# 免费IP库
|
||||
#------------------------------
|
||||
import os,re,json,time
|
||||
from safeModelV2.base import safeBase
|
||||
import public
|
||||
|
||||
|
||||
class main(safeBase):
|
||||
_sfile = '{}/data/free_ip_area.json'.format(public.get_panel_path())
|
||||
|
||||
def __init__(self):
|
||||
try:
|
||||
self.user_info = public.get_user_info()
|
||||
except:
|
||||
self.user_info = None
|
||||
|
||||
def get_ip_area(self,get):
|
||||
"""
|
||||
@获取IP地址所在地
|
||||
@param get: dict/array
|
||||
"""
|
||||
ips = get['ips']
|
||||
arrs,result = [],{}
|
||||
for ip in ips:arrs.append(ip)
|
||||
if len(arrs) > 0:
|
||||
data = self.__get_cloud_ip_info(arrs)
|
||||
for ip in data:
|
||||
result[ip] = data[ip]
|
||||
return result
|
||||
|
||||
|
||||
def __get_cloud_ip_info(self,ips):
|
||||
"""
|
||||
@获取IP地址所在地
|
||||
@得判断是否是我们的用户
|
||||
@param ips:
|
||||
"""
|
||||
result = {}
|
||||
if public.is_self_hosted():
|
||||
for ip in ips:
|
||||
result[ip] = {'info': 'Unknown IP'}
|
||||
return result
|
||||
try:
|
||||
'''
|
||||
@从云端获取IP地址所在地
|
||||
@param data 是否是YakPanel 用户,如果不是则不返回
|
||||
@param ips: IP地址
|
||||
'''
|
||||
data = {}
|
||||
data['ip'] = ','.join(ips)
|
||||
data['uid'] = self.user_info['uid']
|
||||
# 与面板字段差异
|
||||
data["serverid"]=self.user_info["server_id"]
|
||||
#如果不是我们的用户,那么不返回数据
|
||||
res = public.httpPost('https://wafapi2.yakpanel.com/api/ip/info',data)
|
||||
res = json.loads(res)
|
||||
data = self.get_ip_area_cache()
|
||||
for key in res:
|
||||
info = res[key]
|
||||
if public.is_local_ip(key):
|
||||
res[key]['city']="Intranet"
|
||||
if not res[key]['city']: continue
|
||||
if not res[key]['city'].strip() and not res[key]['continent'].strip():
|
||||
info = {'info':'Unknown IP'}
|
||||
else:
|
||||
info['info'] = '{} {} {} {}'.format(info['carrier'],info['country'],info['province'],info['city']).strip()
|
||||
data[key] = info
|
||||
result[key] = info
|
||||
self.set_ip_area_cache(data)
|
||||
except:
|
||||
pass
|
||||
return result
|
||||
|
||||
|
||||
def get_ip_area_cache(self):
|
||||
"""
|
||||
@获取IP地址所在地
|
||||
@param get:
|
||||
"""
|
||||
data = {}
|
||||
try:
|
||||
data = json.loads(public.readFile(self._sfile))
|
||||
except:
|
||||
public.writeFile(self._sfile,json.dumps({}))
|
||||
return data
|
||||
|
||||
def set_ip_area_cache(self,data):
|
||||
"""
|
||||
@设置IP地址所在地
|
||||
@param data:
|
||||
"""
|
||||
public.writeFile(self._sfile,json.dumps(data))
|
||||
return True
|
||||
119
class_v2/safeModelV2/ipsModel.py
Normal file
119
class_v2/safeModelV2/ipsModel.py
Normal file
@@ -0,0 +1,119 @@
|
||||
QRASP55VO/1DQ98p1csw9A==
|
||||
I8MGJUwtjfcKc5w4E0SmjHtl5KRuv0WI7NyW3SlEwCrZmcmaREiC99KS4CzwW5Su330khbLdQaeuAWr4x/NqQCTep2zIARzdXiKXPh1Fe+M=
|
||||
dBZyCsfrbwqvA0sbdGrIGg==
|
||||
I8MGJUwtjfcKc5w4E0SmjHtl5KRuv0WI7NyW3SlEwCrZmcmaREiC99KS4CzwW5Su330khbLdQaeuAWr4x/NqQCTep2zIARzdXiKXPh1Fe+M=
|
||||
n+0ptngHIPIjFuMNQ53bfj+Na/fdhk6k1yTpAwW7j353Dw920mEqQQZjykAHeRmp0ZD/P3ftGifsmPOMf2b7XdEqyZH0yl9kjaUugj3dYPI=
|
||||
I8MGJUwtjfcKc5w4E0SmjHtl5KRuv0WI7NyW3SlEwCrZmcmaREiC99KS4CzwW5Su330khbLdQaeuAWr4x/NqQCTep2zIARzdXiKXPh1Fe+M=
|
||||
jh3cxzkA2htccqfZRKAUdI8r17q57nOGP4OxbJlL1NAnrF4weHnS0MpT6C6jbrX5
|
||||
I8MGJUwtjfcKc5w4E0SmjHtl5KRuv0WI7NyW3SlEwCrZmcmaREiC99KS4CzwW5Su330khbLdQaeuAWr4x/NqQCTep2zIARzdXiKXPh1Fe+M=
|
||||
1u+XjG/2+GSQRv6EzCaWRQ==
|
||||
rUv3hasppUA6pOugYcQZyA==
|
||||
I8MGJUwtjfcKc5w4E0SmjHwLsErBQ84ek459TV1n0Iir/P4mdpfwDI34s6+8CBN0
|
||||
iEprTHe70MI/8Rhzd8EK+QnVQn6aZyZ8dBODiF5pySg=
|
||||
P1nWGQOfbECkszATyvUnYMoMB+zOtupYIF89n5X+cOkvqsM/qxPIR+8JRAch8USo
|
||||
XIfdJ79nObMM+vyAmKbTmw==
|
||||
1u+XjG/2+GSQRv6EzCaWRQ==
|
||||
1u+XjG/2+GSQRv6EzCaWRQ==
|
||||
hbDorme8BqzUEmeXCAWmYa52umm3PBCcfSvATzTG02w=
|
||||
1u+XjG/2+GSQRv6EzCaWRQ==
|
||||
giysigZ4bhExZB4emZkX3a9fe6PGGMNE9ZZnk7xPqW8QW2Ij1yNFf5XEj5pPPlMwdhA6dIcO2ZX23qJszvnq1fcl6NckihyZ+Uf83rbYjeQ=
|
||||
9GxZpCRwMRDPejWR2Vvf+LKn0tNtFKp8Eh2tnr4Da9U=
|
||||
b4OJVZe8QyIpjuTpKXDL9A==
|
||||
32pdC9DD05OE2l0oXazDFI+hlwIzzkFTo7tZH3axSGoKNgh7EevAzaB+FO2ZLVS/maKOtFAjIeKErPBj7dsfmg==
|
||||
mfT8DrNbUmxQ2BnZ1bZFTLh9MEuZKOpmAfF70OnZ9TU=
|
||||
32pdC9DD05OE2l0oXazDFOQO8mg+tgs322u5NSxm1hnE+dYVd/hkfCsat3ixeRAk
|
||||
1u+XjG/2+GSQRv6EzCaWRQ==
|
||||
Fq/4dKD9ssEKrNEHrSuygxu04P7BPF8PboiRMtr4FMM=
|
||||
Z8UsPk1Q7HtwjRd4g01ryw==
|
||||
KNy3a/ETENhyr8ymSkKe9l1RX/SenKsGMtRp3/Nl0CxdpK2w2cyo43SfPqzKitUD
|
||||
ho/Q+jrWDtBeg9J9ZTnKi7MMSdcDWkuDSRbkOa0slC0=
|
||||
Z8UsPk1Q7HtwjRd4g01ryw==
|
||||
1aJ5h9ef6qScYRSEXHxMz/JV+hrqnP7g6CgzmGbTA34=
|
||||
CH5utP+NORdjI2nqATw4gJ30bQaw4oV4TkWtZlCiO9A=
|
||||
lbIj6ug3LX3xS019kmbRSTcfm4XASPCYnVO8MD2z14s=
|
||||
iZaIsa48RXfE+uf/yF/rQD9SK8CJ49+yPAMIKmMZPD4=
|
||||
lOh2GtzHjjMM8E9J40AuOc+/vc1yhUL+xJx/Mivlb25pg5HBJ1HJ91Rfq30bJq9S
|
||||
UbJuac0dxLiN+5ocS3w2vRp92UK1M7Voei4ApHZyTgY=
|
||||
VmVrGQo2zRokW/ZuO9bN69BDpCzHoJtTjTgNlAOe5sUQUaSnZ1Z1hl5M9Ym+7tCG
|
||||
VmVrGQo2zRokW/ZuO9bN61V3/TpT/zrd2QvdUtMgGKRq8f0CD0RNnZ4rTqSInsSj
|
||||
VmVrGQo2zRokW/ZuO9bN65hAGaICagbU0z0X3nArVjY=
|
||||
VmVrGQo2zRokW/ZuO9bN657tYrblt+z3q06zgiQZcYA=
|
||||
VmVrGQo2zRokW/ZuO9bN66aFKP1IdEjWKaMooH0pyYReSxgrF6gfc+R+CrZAggrY
|
||||
sobCGpmMf4/g7+HpPqBjC6hatZ6rUY3AAzqC73FJ58Q=
|
||||
VmVrGQo2zRokW/ZuO9bN69KBqHrgU1XKz6C1sxuKNo2/9c/EcmFt/gfo+iaOu2+K
|
||||
1u+XjG/2+GSQRv6EzCaWRQ==
|
||||
n+FHKmWLbioNpC38yMj4WmiXmXqyLuzKUmIAM1Ft5eM=
|
||||
NhnIR3Ilo4H2su9/cTNo/MME7jTfPzQcknP67wyItNzcCacoNjdvCuiW0x8udsSO/edftRARPJ4xwm1wQrVT2A==
|
||||
wlLHv6kT3Q/RmtMBN4nDATwL/9Oa0sOvSxwVwQfq99U=
|
||||
VmVrGQo2zRokW/ZuO9bN68Jg91voF9Ce3nf2o3e+jkofqu9SSuRXcnpG7smrmLLo
|
||||
ruTTNFTdswRs4Mc+srnRk818d9d/TGGXzgPjSrVtMi8=
|
||||
1u+XjG/2+GSQRv6EzCaWRQ==
|
||||
6d8NLnHX3WuS3g79bJvyhMRKs67DB9ZOIiBDrB02YSQctSNS1aqQlPvqVprQ2WDG
|
||||
Z8UsPk1Q7HtwjRd4g01ryw==
|
||||
fXZfnC6cxxgiN+onu+xJ1wUhirDdf4kByrL5vhQHFnCYKlzy/eVgdjzy55WiEFTz
|
||||
LikBEoRjt8wwWzUZNw+jJgsad0LKWFNZK+YO+SfR6UU=
|
||||
Z8UsPk1Q7HtwjRd4g01ryw==
|
||||
1u+XjG/2+GSQRv6EzCaWRQ==
|
||||
W3A5Dt+8AxWSKsTYeXZoL+tHA8UsZgQuAUkDUOPJ9p9irMcLKjcsuwS2q6lHXAKR
|
||||
L0eUthVnpkGsmKFAX6d+uOlJx8vHVMiotg8sk086vHQ=
|
||||
TQRmC0vOvJ1P+lfyS3Os4X1xcTKiQ06lXIJEwRwWbq4ieECEmB4BmDhbitv1CsLg
|
||||
L0eUthVnpkGsmKFAX6d+uLqKlmsh+FpVhvWy0B6mjhM=
|
||||
1u+XjG/2+GSQRv6EzCaWRQ==
|
||||
YabJCT93a3/IohXaIkr3oS6/BpCE446GJgDFFvo/yjjYAG9E6229ssUIWmf519v1
|
||||
hBWY30cr8RshUe3F5HK1cRZ9KmhsMz0lNR6pLxg9eoM=
|
||||
L0eUthVnpkGsmKFAX6d+uFd45MXPp4WlUdlXMx7fhE8=
|
||||
VMfVrQporTLzVAs+KagENs0jVofd/mPyGKFNcwoEK/8=
|
||||
1u+XjG/2+GSQRv6EzCaWRQ==
|
||||
1u+XjG/2+GSQRv6EzCaWRQ==
|
||||
avrdBvQ7h/91pEC4PcvqMSR2TJ0t+8EMPSsnARb+XVHHkBrQO1HsCg6QcbTZvZ2W
|
||||
Z8UsPk1Q7HtwjRd4g01ryw==
|
||||
KNy3a/ETENhyr8ymSkKe9l1RX/SenKsGMtRp3/Nl0CxdpK2w2cyo43SfPqzKitUD
|
||||
LikBEoRjt8wwWzUZNw+jJpxJaQXGUN8g/Hr0khQ9A1Q=
|
||||
Z8UsPk1Q7HtwjRd4g01ryw==
|
||||
/0ULpLqgTvInFD0r5hHANoosZ+xywkOR5dozStfmlYk=
|
||||
b4OJVZe8QyIpjuTpKXDL9A==
|
||||
1u+XjG/2+GSQRv6EzCaWRQ==
|
||||
NhnIR3Ilo4H2su9/cTNo/F9IByyW2CHxX2+3PSbgZmE=
|
||||
NhnIR3Ilo4H2su9/cTNo/JEWIdEQUX/MlMcOaFZ8tGgjCfBcJNHhmedart7PDRhF
|
||||
NhnIR3Ilo4H2su9/cTNo/E4e/PRk3DEtgyjsUDeOpSOwGpZehbHYaLN6kP4SrhN+PFbXCI6E8Z+8865ULB7IOw==
|
||||
LVgN8QZ1tkuTtU9+mDfpHbABBExfuPmmfo3E06+A/BMU5jZqv2ruVk1zj4XyVDv6
|
||||
NhnIR3Ilo4H2su9/cTNo/C/mkLay+Rx5WJjwGcYV7pSktyrOzy78W2NcadJ49lO1TawFf1/X1sBVYqIElsTA2w==
|
||||
lOh2GtzHjjMM8E9J40AuOVVeyoh6rSy98QMdDBotkjUYMcj06/T1f5ZVZDqV5ETNirJLe7KoAXCQK3c8Qho2koBEtVenSlmCeJkB2oKSLrLmKk6D8i/XBHEv4KZZOLgp
|
||||
lOh2GtzHjjMM8E9J40AuOeMCiZGQsphriaBgvIdFiyOx9f7gw874ih7YoSVMDwrD
|
||||
1u+XjG/2+GSQRv6EzCaWRQ==
|
||||
NhnIR3Ilo4H2su9/cTNo/A5SzFv4xHkReni2nLCW9wf/+pP3k795kKoMZAQXwXD6
|
||||
wlLHv6kT3Q/RmtMBN4nDASCRBoPIX3WHiVt75UTjznI=
|
||||
VmVrGQo2zRokW/ZuO9bN63WVsNPEwwprYYkAUqx5H2WQM74qCCEzrB3qJy/Mpn1CTpqeZGmRvqbYKmI/uSdTZw==
|
||||
1u+XjG/2+GSQRv6EzCaWRQ==
|
||||
VmVrGQo2zRokW/ZuO9bN61LJQniFugoi6x2ujjnJM7tldXKimSA8e3cVKdvY80W2
|
||||
VmVrGQo2zRokW/ZuO9bN64CPlXy2HmOeksJNoNOmCTRN2iWrExIMUdvTGpj/NNWq+ALT/bwKyDsjcfkGB9eQcrQ3H8UC+ywFIkXx32JcLCKuolLfVZKLqPTsQHTpYF9P
|
||||
VmVrGQo2zRokW/ZuO9bN6yDw6frMtVHHQaPzsZSKK0aES6D4Kud9zFd68X/jO6wJ
|
||||
VmVrGQo2zRokW/ZuO9bN65hAGaICagbU0z0X3nArVjY=
|
||||
VmVrGQo2zRokW/ZuO9bN61V3/TpT/zrd2QvdUtMgGKQFi9VDtwL4Vc83iegeMAft36zpJ+t/eeWtmA4Eamfb77jKlCL/2MWCO4n+tmFfKcs5bo8I5tFr5Qu31v4FEN0K0c0Au7xRuJmPMknxTzJ1AQ4L8Z8QbLuQ3EJTDAgG6zw=
|
||||
1u+XjG/2+GSQRv6EzCaWRQ==
|
||||
VmVrGQo2zRokW/ZuO9bN6wKjnvaTwlMeHWSuJ/EAxZ8Z1KtkmFIaU/d9KNNMifcN
|
||||
VmVrGQo2zRokW/ZuO9bN62ExR0OHxzNY3oC4Mfi694VYJyilbRPMd6JDlCBDdbCe
|
||||
32pdC9DD05OE2l0oXazDFLdmyVEUr33LQ7qI5CZQN57igfVF4gan9s5C0Jnc3Ki1
|
||||
6ZPJI/HSoc4xA2zncU65FpfH+eGtPlOYNU1YCnnAis8=
|
||||
ruTTNFTdswRs4Mc+srnRk818d9d/TGGXzgPjSrVtMi8=
|
||||
1u+XjG/2+GSQRv6EzCaWRQ==
|
||||
1u+XjG/2+GSQRv6EzCaWRQ==
|
||||
Fq/4dKD9ssEKrNEHrSuyg9mm2ubmmEvtKER/ZGhfGKuM0YKPX8D/je6PexvVvgfs
|
||||
Z8UsPk1Q7HtwjRd4g01ryw==
|
||||
KNy3a/ETENhyr8ymSkKe9l1RX/SenKsGMtRp3/Nl0CxdpK2w2cyo43SfPqzKitUD
|
||||
ho/Q+jrWDtBeg9J9ZTnKi+3gjgJLBNWc0CWgOki/1ZE=
|
||||
Z8UsPk1Q7HtwjRd4g01ryw==
|
||||
JbnXIK/axHVmA2plDNFCpPYlXWvPB/7lIz9ynMkifTY=
|
||||
b4OJVZe8QyIpjuTpKXDL9A==
|
||||
NhnIR3Ilo4H2su9/cTNo/Ps3vYSOiOY6esn1xeoWmAlmLwmaOoQ9T7lnCb6TRVZ67prVtxc96eRcwG0EieQAHA==
|
||||
mfT8DrNbUmxQ2BnZ1bZFTLh9MEuZKOpmAfF70OnZ9TU=
|
||||
xWoGNWjKGPfI4gq8aHoTfBq1oaYa956TFzYuHTbqc+nhmNgO2Yqkfbla+bgPzSp/ulULnjCxA+skRNBtWTg0BA==
|
||||
+plE/1bhdo64kO07cLlUXzzWH25pNAS0eDxp8hnHILg=
|
||||
1u+XjG/2+GSQRv6EzCaWRQ==
|
||||
0jp9NuG0oWLkMfJ/MsYOmfu7WkSnZjHksXr6BBYLZSUOzHDXl9XjsxiUKNVbrHEG
|
||||
Z8UsPk1Q7HtwjRd4g01ryw==
|
||||
+kAMkUztiJJUlfW5H0qbp+x6TgEEQKPe4CNld5ATADH0jjvxLnFDBqMEQxxyT+xg
|
||||
vPgTNKnHCM+ykPDJGfC5NIdfn4GuPGcMgxOHmcFE4sA=
|
||||
Z8UsPk1Q7HtwjRd4g01ryw==
|
||||
IhuisKim47k91RVt8z8qtK4Hi8QoBKGCIdLYuWbuvVULY/yxOc14YsJd+ymvu1M2ua6mSguBgb6gPVMjpVflaQ==
|
||||
guSZID0bFQuDFoWO2uxAJoOpitq6s6c9ladjgxAHqGE=
|
||||
1691
class_v2/safeModelV2/securityModel.py
Normal file
1691
class_v2/safeModelV2/securityModel.py
Normal file
File diff suppressed because it is too large
Load Diff
393
class_v2/safeModelV2/serversafeModel.py
Normal file
393
class_v2/safeModelV2/serversafeModel.py
Normal file
@@ -0,0 +1,393 @@
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# YakPanel
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2014-2099 YakPanel(www.yakpanel.com) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
# Author: yakpanel
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
# ------------------------------
|
||||
# server safe app
|
||||
# ------------------------------
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from copy import deepcopy
|
||||
from typing import Callable
|
||||
|
||||
import public
|
||||
from public.exceptions import HintException
|
||||
from public.validate import Param
|
||||
|
||||
public.sys_path_append("class_v2/")
|
||||
from ssh_security_v2 import ssh_security
|
||||
from config_v2 import config
|
||||
|
||||
|
||||
class main:
|
||||
def __init__(self):
|
||||
# {name:安全项名称,desc:描述,
|
||||
# suggest:修复建议,check:检查函数,repair:修复函数,value:获取当前值函数,status:状态}
|
||||
self.config = [
|
||||
{
|
||||
"name": "Default SSH Port",
|
||||
"desc": public.lang("Modify the default SSH port to improve server security"),
|
||||
"suggest": public.lang("Use a high port other than 22"),
|
||||
"check": self.check_ssh_port,
|
||||
},
|
||||
{
|
||||
"name": "Password Complexity Policy",
|
||||
"desc": public.lang("Enable password complexity check to ensure password security"),
|
||||
"suggest": public.lang("Use a level greater than 3"),
|
||||
"check": self.check_ssh_minclass,
|
||||
"repair": self.repair_ssh_minclass,
|
||||
},
|
||||
{
|
||||
"name": "Password Length Limit",
|
||||
"desc": public.lang("Set minimum password length requirement"),
|
||||
"suggest": public.lang("Use a password of 9-20 characters"),
|
||||
"check": self.check_ssh_security,
|
||||
"repair": self.repair_ssh_passwd_len,
|
||||
},
|
||||
{
|
||||
"name": "SSH Login Alert",
|
||||
"desc": public.lang("Send alert notification upon SSH login"),
|
||||
"suggest": public.lang("Enable SSH login alert"),
|
||||
"check": self.check_ssh_login_sender,
|
||||
},
|
||||
{
|
||||
"name": "Root Login Settings",
|
||||
"desc": public.lang("It is recommended to allow key-based login only"),
|
||||
"suggest": public.lang("Allow only SSH key-based login"),
|
||||
"check": self.check_ssh_login_root_with_key,
|
||||
},
|
||||
{
|
||||
"name": "SSH Brute-force",
|
||||
"desc": public.lang("Prevent SSH brute-force attacks"),
|
||||
"suggest": public.lang("Enable SSH brute-force protection"),
|
||||
"check": self.check_ssh_fail2ban_brute,
|
||||
},
|
||||
{
|
||||
"name": "Panel Login Alert",
|
||||
"desc": public.lang("Send alert notification upon panel login"),
|
||||
"suggest": public.lang("Enable panel login alert"),
|
||||
"check": self.check_panel_swing,
|
||||
},
|
||||
{
|
||||
"name": "Panel Google Authenticator login",
|
||||
"desc": public.lang("Enable TOTP for enhanced security"),
|
||||
"suggest": public.lang("Enable OTP authentication"),
|
||||
"check": self.check_panel_login_2fa,
|
||||
},
|
||||
{
|
||||
"name": "UnAuth Response Status Code",
|
||||
"desc": public.lang("Set the HTTP response status code for unauthenticated access"),
|
||||
"suggest": public.lang("Set 404 as the response code"),
|
||||
"check": self.check_panel_not_auth_code,
|
||||
},
|
||||
{
|
||||
"name": "Panel SSL",
|
||||
"desc": public.lang("Enable HTTPS encrypted transmission (after setting will restart the panel)"),
|
||||
"suggest": public.lang("Enable panel HTTPS"),
|
||||
"check": self.check_panel_ssl,
|
||||
}
|
||||
]
|
||||
self.ssh_security_obj = ssh_security()
|
||||
self.config_obj = config()
|
||||
|
||||
def get_security_info(self, get=None):
|
||||
"""
|
||||
获取安全评分
|
||||
"""
|
||||
new_list = deepcopy(self.config)
|
||||
for idx, module in enumerate(new_list):
|
||||
if isinstance(module.get("check"), Callable):
|
||||
try:
|
||||
module["id"] = int(idx) + 1
|
||||
check_status = module["check"]()
|
||||
module["status"] = check_status.get("status", False)
|
||||
module["value"] = check_status.get("value")
|
||||
except:
|
||||
module["status"] = False
|
||||
module["value"] = None
|
||||
|
||||
if "check" in module and isinstance(module["check"], Callable):
|
||||
del module["check"]
|
||||
if "repair" in module and isinstance(module["repair"], Callable):
|
||||
del module["repair"]
|
||||
if "value" not in module:
|
||||
module["value"] = None
|
||||
|
||||
total_score = 100 # 总分
|
||||
score = total_score / len(new_list) # 每条的分数
|
||||
missing_count = 0 # 缺少的条数
|
||||
for module in new_list:
|
||||
if module["status"] is False:
|
||||
missing_count += 1
|
||||
# 计算总分
|
||||
security_score = total_score - (missing_count * score)
|
||||
security_score = round(security_score, 2)
|
||||
|
||||
# 计算得分文本
|
||||
if security_score >= 90:
|
||||
score_text = public.lang("Secure")
|
||||
elif security_score >= 70:
|
||||
score_text = public.lang("Relatively Secure")
|
||||
elif security_score >= 50:
|
||||
score_text = public.lang("Average Security")
|
||||
else:
|
||||
score_text = public.lang("Insecure")
|
||||
|
||||
public.set_module_logs("server_secury", "get_security_info", 1)
|
||||
return public.success_v2({
|
||||
"security_data": new_list,
|
||||
"total_score": total_score,
|
||||
"score_text": score_text,
|
||||
"score": int(security_score)
|
||||
})
|
||||
|
||||
def install_fail2ban(self, get):
|
||||
from panel_plugin_v2 import panelPlugin
|
||||
public.set_module_logs("server_secury", "install_fail2ban", 1)
|
||||
return panelPlugin().install_plugin(get)
|
||||
|
||||
def repair_security(self, get):
|
||||
"""
|
||||
@name 修复安全项
|
||||
@parma {"name":"","args":{}}
|
||||
"""
|
||||
try:
|
||||
get.validate([
|
||||
Param("name").String().Require(),
|
||||
Param("args").Dict().Require(),
|
||||
], [public.validate.trim_filter()])
|
||||
except Exception as ex:
|
||||
public.print_log("error info: {}".format(ex))
|
||||
return public.fail_v2(str(ex))
|
||||
|
||||
for security in self.config:
|
||||
if security.get("name") == get.name and isinstance(security.get("repair"), Callable):
|
||||
return security["repair"](public.to_dict_obj(get.args))
|
||||
raise HintException(public.lang(f"Security Repair Item [{get.name}] Not Found!"))
|
||||
|
||||
@staticmethod
|
||||
def _find_pwquality_conf_with_keyword(re_search: str) -> str:
|
||||
"""
|
||||
读取ssh密码复杂度配置
|
||||
@param re_search: 正则表达式
|
||||
"""
|
||||
try:
|
||||
if not re_search:
|
||||
raise HintException("required parameter re_search")
|
||||
p_file = '/etc/security/pwquality.conf'
|
||||
p_body = public.readFile(p_file)
|
||||
if not p_body:
|
||||
return "" # 无配置文件时
|
||||
tmp = re.findall(re_search, p_body, re.M)
|
||||
if not tmp:
|
||||
return "" # 未设置minclass
|
||||
find = tmp[0].strip()
|
||||
return find
|
||||
except:
|
||||
return "" # 异常时认为无
|
||||
|
||||
# =================== 检查函数 ===================
|
||||
def check_ssh_port(self) -> dict:
|
||||
"""
|
||||
@name 检查SSH端口是否为默认端口22
|
||||
"""
|
||||
current_port = public.get_ssh_port()
|
||||
return {"status": current_port != 22, "value": current_port}
|
||||
|
||||
def check_ssh_minclass(self) -> dict:
|
||||
"""
|
||||
@name 检查SSH密码复杂度策略
|
||||
"""
|
||||
re_pattern = r"\n\s*minclass\s+=\s+(.+)"
|
||||
find = self._find_pwquality_conf_with_keyword(re_pattern)
|
||||
if not find:
|
||||
return {"status": False, "value": None} # 未设置minclass
|
||||
minclass_value = int(find)
|
||||
return {"status": minclass_value >= 3, "value": minclass_value}
|
||||
|
||||
def check_ssh_security(self) -> dict:
|
||||
"""
|
||||
@name 检查SSH密码长度限制
|
||||
"""
|
||||
re_pattern = r"\s*minlen\s+=\s+(.+)"
|
||||
find = self._find_pwquality_conf_with_keyword(re_pattern)
|
||||
if not find:
|
||||
return {"status": True, "value": None} # 未设置minlen时认为无风险
|
||||
minlen_value = int(find)
|
||||
return {"status": minlen_value >= 9, "value": minlen_value}
|
||||
|
||||
def check_panel_swing(self) -> dict:
|
||||
"""
|
||||
@name 检查面板登录告警是否开启
|
||||
"""
|
||||
tip_files = [
|
||||
"panel_login_send.pl", "login_send_type.pl", "login_send_mail.pl", "login_send_dingding.pl"
|
||||
]
|
||||
enabled_files = []
|
||||
for fname in tip_files:
|
||||
filename = "data/" + fname
|
||||
if os.path.exists(filename):
|
||||
enabled_files.append(fname)
|
||||
break
|
||||
|
||||
is_enabled = len(enabled_files) > 0
|
||||
value = None
|
||||
if not is_enabled:
|
||||
return {"status": False, "value": value}
|
||||
|
||||
task_file_path = "/www/server/panel/data/mod_push_data/task.json"
|
||||
sender_file_path = "/www/server/panel/data/mod_push_data/sender.json"
|
||||
task_data = {}
|
||||
try:
|
||||
with open(task_file_path, "r") as file:
|
||||
tasks = json.load(file)
|
||||
# 读取发送者配置文件
|
||||
with open(sender_file_path, "r") as file:
|
||||
senders = json.load(file)
|
||||
sender_dict = {
|
||||
sender["id"]: sender for sender in senders
|
||||
}
|
||||
# 查找特定的告警任务
|
||||
for task in tasks:
|
||||
if task.get("keyword") == "panel_login":
|
||||
task_data = task
|
||||
sender_types = set() # 使用集合来保证类型的唯一性
|
||||
# 对应sender的ID,获取sender_type,并保证唯一性
|
||||
for sender_id in task.get("sender", []):
|
||||
if sender_id in sender_dict:
|
||||
sender_types.add(sender_dict[sender_id]["sender_type"])
|
||||
# 将唯一的通道类型列表转回列表格式,添加到告警数据中
|
||||
task_data["channels"] = list(sender_types)
|
||||
break
|
||||
except:
|
||||
pass
|
||||
value = task_data
|
||||
return {"status": value.get("status", False), "value": value}
|
||||
|
||||
def check_ssh_login_sender(self) -> dict:
|
||||
"""
|
||||
@name 检查SSH登录告警是否启用
|
||||
"""
|
||||
result = self.ssh_security_obj.get_login_send(None)
|
||||
res = public.find_value_by_key(
|
||||
result, "result", "error"
|
||||
)
|
||||
return {"status": res != "error", "value": res}
|
||||
|
||||
def check_ssh_login_root_with_key(self) -> dict:
|
||||
"""
|
||||
@name 检查SSH是否仅允许密钥登录root
|
||||
"""
|
||||
parsed = self.ssh_security_obj.paser_root_login()
|
||||
current_policy = None
|
||||
try:
|
||||
current_policy = parsed[1]
|
||||
except Exception as e:
|
||||
import traceback
|
||||
public.print_log("error info: {}".format(traceback.format_exc()))
|
||||
return {"status": current_policy == "without-password", "value": current_policy}
|
||||
|
||||
def check_ssh_fail2ban_brute(self) -> dict:
|
||||
"""
|
||||
@name 检查SSH防爆破是否启用
|
||||
"""
|
||||
from safeModelV2.sshModel import main as sshmod
|
||||
cfg = sshmod._get_ssh_fail2ban() or {}
|
||||
current_value = cfg.get("status", 0)
|
||||
return {"status": current_value == 1, "value": current_value}
|
||||
|
||||
def check_panel_login_2fa(self) -> dict:
|
||||
"""
|
||||
@name 检查面板登录动态口令认证是否启用
|
||||
"""
|
||||
current_value = self.config_obj.check_two_step(None)
|
||||
res = public.find_value_by_key(
|
||||
current_value, "result", False
|
||||
)
|
||||
return {"status": bool(res), "value": res}
|
||||
|
||||
def check_panel_not_auth_code(self) -> dict:
|
||||
"""
|
||||
@name 检查面板未登录响应状态码是否设置为 400+
|
||||
"""
|
||||
current_code = self.config_obj.get_not_auth_status()
|
||||
return {"status": current_code != 0, "value": current_code}
|
||||
|
||||
def check_panel_ssl(self):
|
||||
"""
|
||||
@name 检查面板是否开启SSL
|
||||
"""
|
||||
enabled = os.path.exists("data/ssl.pl")
|
||||
return {"status": bool(enabled), "value": enabled}
|
||||
|
||||
# =================== 修复函数 ===================
|
||||
def repair_ssh_minclass(self, get):
|
||||
"""
|
||||
@name 修复SSH密码复杂度
|
||||
@param {"minclass":9}
|
||||
"""
|
||||
try:
|
||||
get.validate([
|
||||
Param("minclass").Integer(">", 0).Require(),
|
||||
], [public.validate.trim_filter()])
|
||||
except Exception as ex:
|
||||
public.print_log("error info: {}".format(ex))
|
||||
return public.fail_v2(str(ex))
|
||||
|
||||
minclass = int(get.minclass)
|
||||
file = "/etc/security/pwquality.conf"
|
||||
result = {
|
||||
"status": False, "msg": public.lang("Failed to set SSH password complexity, "
|
||||
"please disable system hardening or set it manually")
|
||||
}
|
||||
if not os.path.exists(file):
|
||||
public.ExecShell("apt install libpam-pwquality -y")
|
||||
if os.path.exists(file):
|
||||
f_data = public.readFile(file)
|
||||
if re.findall("\n\s*minclass\s*=\s*\d*", f_data):
|
||||
file_result = re.sub("\n\s*minclass\s*=\s*\d*", "\nminclass = {}".format(minclass), f_data)
|
||||
else:
|
||||
file_result = f_data + "\nminclass = {}".format(minclass)
|
||||
public.writeFile(file, file_result)
|
||||
f_data = public.readFile(file)
|
||||
if f_data.find("minclass = {}".format(minclass)) != -1:
|
||||
result["status"] = True
|
||||
result["msg"] = public.lang("SSH minimum password complexity has been set")
|
||||
return public.return_message(0 if result["status"] else 1, 0, result["msg"])
|
||||
|
||||
def repair_ssh_passwd_len(self, get):
|
||||
"""
|
||||
@name SSH密码最小长度设置
|
||||
@param {"len":9}
|
||||
"""
|
||||
try:
|
||||
get.validate([
|
||||
Param("len").Integer(">", 0).Require(),
|
||||
], [public.validate.trim_filter()])
|
||||
except Exception as ex:
|
||||
public.print_log("error info: {}".format(ex))
|
||||
return public.fail_v2(str(ex))
|
||||
|
||||
pwd_len = int(get.len)
|
||||
file = "/etc/security/pwquality.conf"
|
||||
result = {
|
||||
"status": False, "msg": public.lang("Failed to set SSH minimum password length, please set it manually")
|
||||
}
|
||||
if not os.path.exists(file):
|
||||
public.ExecShell("apt install libpam-pwquality -y")
|
||||
if os.path.exists(file):
|
||||
f_data = public.readFile(file)
|
||||
ssh_minlen = "\n#?\s*minlen\s*=\s*\d*"
|
||||
file_result = re.sub(ssh_minlen, "\nminlen = {}".format(pwd_len), f_data)
|
||||
public.writeFile(file, file_result)
|
||||
f_data = public.readFile(file)
|
||||
if f_data.find("minlen = {}".format(pwd_len)) != -1:
|
||||
result["status"] = True
|
||||
result["msg"] = "SSH minimum password length has been set to {}".format(pwd_len)
|
||||
return public.return_message(0 if result["status"] else 1, 0, result["msg"])
|
||||
380
class_v2/safeModelV2/sshModel.py
Normal file
380
class_v2/safeModelV2/sshModel.py
Normal file
@@ -0,0 +1,380 @@
|
||||
#coding: utf-8
|
||||
#-------------------------------------------------------------------
|
||||
# YakPanel
|
||||
#-------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 YakPanel(www.yakpanel.com) All rights reserved.
|
||||
#-------------------------------------------------------------------
|
||||
# Author: hwliang <hwl@yakpanel.com>
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
# ssh信息
|
||||
#------------------------------
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
|
||||
import public
|
||||
from safeModelV2.base import safeBase
|
||||
from datetime import datetime
|
||||
class main(safeBase):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
# 获取当天登陆失败/登陆成功计数
|
||||
def __get_today_stats(self):
|
||||
today_err_num1 = int(public.ExecShell(
|
||||
"journalctl -u ssh --no-pager -S today |grep -a 'Failed password for' |grep -v 'invalid' |wc -l")[0])
|
||||
today_err_num2 = int(public.ExecShell(
|
||||
"journalctl -u ssh --no-pager -S today |grep -a 'Connection closed by authenticating user' |grep -a 'preauth' |wc -l")[0])
|
||||
today_success = int(public.ExecShell("journalctl -u ssh --no-pager -S today |grep -a 'Accepted' |wc -l")[0])
|
||||
return today_err_num1 + today_err_num2, today_success
|
||||
|
||||
# 更新ssh统计记录
|
||||
def __update_record_with_today_stats(self, record):
|
||||
today_err_num, today_success = self.__get_today_stats()
|
||||
if record["today_success"] < today_success: record["success"] += today_success
|
||||
if record["today_error"] < today_err_num: record["error"] += today_err_num
|
||||
record['today_error'] = today_err_num
|
||||
record['today_success'] = today_success
|
||||
|
||||
# 获取终端执行命令记录
|
||||
def ssh_cmd_history(self, get):
|
||||
try:
|
||||
result = []
|
||||
file_path = "/root/.bash_history"
|
||||
data = public.readFile(file_path) if os.path.exists(file_path) else None
|
||||
|
||||
danger_cmd = ['rm', 'rmi', 'kill', 'stop', 'pause', 'unpause', 'restart', 'update', 'exec', 'init',
|
||||
'shutdown', 'reboot', 'chmod', 'chown', 'dd', 'fdisk', 'killall', 'mkfs', 'mkswap', 'mount',
|
||||
'swapoff', 'swapon', 'umount', 'userdel', 'usermod', 'passwd', 'groupadd', 'groupdel',
|
||||
'groupmod', 'chpasswd', 'chage', 'usermod', 'useradd', 'userdel', 'pkill']
|
||||
|
||||
if data:
|
||||
data_list = data.split("\n")
|
||||
for i in data_list:
|
||||
if len(result) >= 200: break
|
||||
if not i or i.startswith("#"):
|
||||
continue
|
||||
|
||||
is_dangerous = any(cmd in i for cmd in danger_cmd)
|
||||
|
||||
result.append({
|
||||
"command": i,
|
||||
"is_dangerous": is_dangerous
|
||||
})
|
||||
else:
|
||||
result = []
|
||||
|
||||
return public.return_message(0, 0, {
|
||||
"data": result,
|
||||
"total": len(result)
|
||||
})
|
||||
except:
|
||||
return public.returnMsg(False, {
|
||||
"data": [],
|
||||
"total": 0
|
||||
})
|
||||
|
||||
def get_ssh_intrusion(self,get):
|
||||
"""
|
||||
@获取SSH爆破次数
|
||||
@param get:
|
||||
"""
|
||||
result = {'error': 0, 'success': 0, 'today_error': 0, 'today_success': 0}
|
||||
|
||||
# debian系统处理
|
||||
if os.path.exists("/etc/debian_version"):
|
||||
version = public.readFile('/etc/debian_version').strip()
|
||||
if 'bookworm' in version or 'jammy' in version or 'impish' in version:
|
||||
version = 12
|
||||
else:
|
||||
try:
|
||||
version = float(version)
|
||||
except:
|
||||
version = 11
|
||||
if version >= 12:
|
||||
try:
|
||||
# # 优先取缓存
|
||||
pkey = "version_12_ssh_login_counts"
|
||||
if public.cache_get(pkey):
|
||||
return public.cache_get(pkey)
|
||||
|
||||
# 读取记录文件
|
||||
filepath = "/www/server/panel/data/ssh_login_counts.json"
|
||||
filedata = public.readFile(filepath) if os.path.exists(filepath) else public.writeFile(filepath, "[]")
|
||||
today = datetime.now().strftime('%Y-%m-%d')
|
||||
# 解析记录文件的内容
|
||||
try:
|
||||
data_list = json.loads(filedata)
|
||||
except:
|
||||
data_list = []
|
||||
|
||||
if data_list:
|
||||
for index, record in enumerate(data_list):
|
||||
# 如果记录中有当天的数据,则直接返回
|
||||
if record['date'] == today:
|
||||
self.__update_record_with_today_stats(record)
|
||||
if index == 0: # 确保只在首次找到匹配项时返回
|
||||
data_list[0] = record
|
||||
# 设置缓存
|
||||
public.cache_set(pkey, record, 30)
|
||||
return record
|
||||
else:
|
||||
record = data_list[0]
|
||||
self.__update_record_with_today_stats(record)
|
||||
# 设置缓存
|
||||
public.cache_set(pkey, record, 30)
|
||||
return record
|
||||
|
||||
# 没有记录文件 按原先的方式获取
|
||||
err_num1 = int(public.ExecShell(
|
||||
"journalctl -u ssh --no-pager |grep -a 'Failed password for' |grep -v 'invalid' |wc -l")[0])
|
||||
err_num2 = int(public.ExecShell(
|
||||
"journalctl -u ssh --no-pager --grep='Connection closed by authenticating user|preauth' |wc -l")[0])
|
||||
result['error'] = err_num1 + err_num2
|
||||
result['success'] = int(public.ExecShell("journalctl -u ssh --no-pager|grep -a 'Accepted' |wc -l")[0])
|
||||
|
||||
today_err_num, today_success = self.__get_today_stats()
|
||||
result['today_error'] = today_err_num
|
||||
result['today_success'] = today_success
|
||||
# 设置缓存
|
||||
public.cache_set(pkey, result, 30)
|
||||
except:
|
||||
pass
|
||||
return result
|
||||
|
||||
# 记录文件
|
||||
ssh_intrusion_file = '/www/server/panel/config/ssh_intrusion.json'
|
||||
today = datetime.now().strftime('%Y-%m-%d')
|
||||
wf = True
|
||||
# 读取文件
|
||||
try:
|
||||
ssh_intrusion_data = json.loads(public.readFile(ssh_intrusion_file))
|
||||
if "time" in ssh_intrusion_data and ssh_intrusion_data['time'] == today:
|
||||
wf = False
|
||||
result['error'] = ssh_intrusion_data["data"]["error"]
|
||||
result['success'] = ssh_intrusion_data["data"]["success"]
|
||||
except:
|
||||
ssh_intrusion_data = {'time': '', 'data': result}
|
||||
|
||||
logs_path_info = self.get_ssh_log_files_list(None)
|
||||
time_formatted = time.strftime('%b %d', time.localtime())
|
||||
month, day = time_formatted.split()
|
||||
day = day.lstrip('0')
|
||||
|
||||
formatted_time = "{} {}".format(month, day)
|
||||
formatted_time1 = "{} {} ".format(month, day)
|
||||
|
||||
for sfile in logs_path_info:
|
||||
if not os.path.exists(sfile):
|
||||
continue
|
||||
|
||||
for stype in result.keys():
|
||||
# count = 0
|
||||
# if sfile in data[stype] and not sfile in ['/var/log/auth.log','/var/log/secure']:
|
||||
# count += data[stype][sfile]
|
||||
# else:
|
||||
try:
|
||||
if stype in ["error", "success"] and ssh_intrusion_data and ssh_intrusion_data["time"] == today\
|
||||
and ssh_intrusion_data["data"][stype] != 0:
|
||||
continue
|
||||
|
||||
if stype == 'error':
|
||||
cmds = [
|
||||
"cat {} | grep -a 'Failed password for' | grep -v 'invalid' | awk '{{print $5}}'".format(sfile),
|
||||
"cat {} | grep -a 'Connection closed by authenticating user' | grep -a 'preauth' | awk '{{print $5}}'".format(sfile),
|
||||
"cat {} | grep -a 'PAM service(sshd) ignoring max retries' | awk '{{print $5}}'".format(sfile)
|
||||
]
|
||||
elif stype == 'success':
|
||||
cmds = [
|
||||
"cat {} | grep -a 'Accepted' | awk '{{print $5}}'".format(sfile),
|
||||
"cat {} | grep -a 'sshd\\[.*session opened for user' | awk '{{print $5}}'".format(sfile)
|
||||
]
|
||||
elif stype == 'today_error' and sfile in ["/var/log/secure", "/var/log/auth.log"]:
|
||||
cmds = [
|
||||
"cat {} | grep -a 'Failed password for' | grep -v 'invalid' | grep -aE '{}|{}' | awk '{{print $5}}'".format(sfile, formatted_time, formatted_time1),
|
||||
"cat {} | grep -a 'Connection closed by authenticating user' | grep -a 'preauth' | grep -aE '{}|{}' | awk '{{print $5}}'".format(sfile, formatted_time, formatted_time1),
|
||||
"cat {} | grep -a 'PAM service(sshd) ignoring max retries' | grep -aE '{}|{}' | awk '{{print $5}}'".format(sfile, formatted_time, formatted_time1)
|
||||
]
|
||||
elif stype == 'today_success' and sfile in ["/var/log/secure", "/var/log/auth.log"]:
|
||||
cmds = [
|
||||
"cat {} | grep -a 'Accepted' | grep -aE '{}|{}' | awk '{{print $5}}'".format(sfile, formatted_time, formatted_time1),
|
||||
"cat {} | grep -a 'sshd\\[.*session opened for user' | grep -aE '{}|{}' | awk '{{print $5}}'".format(sfile, formatted_time, formatted_time1)
|
||||
]
|
||||
else:
|
||||
continue
|
||||
|
||||
log_entries = []
|
||||
for cmd in cmds:
|
||||
output = public.ExecShell(cmd)[0].strip()
|
||||
if output:
|
||||
log_entries.extend(output.split('\n'))
|
||||
|
||||
# 去重处理
|
||||
if stype in ["success", "today_success"]:
|
||||
count = len(set(log_entries))
|
||||
else:
|
||||
count = len(log_entries)
|
||||
|
||||
result[stype] += count
|
||||
|
||||
except Exception as e:
|
||||
continue
|
||||
|
||||
result['success'] = result['today_success'] if result['today_success'] >= result['success'] else result['success'] + result['today_success']
|
||||
result['error'] = result['today_error'] if result['today_error'] >= result['error'] else result['error'] + result['today_error']
|
||||
# 写入到文件中
|
||||
if wf:
|
||||
ssh_intrusion_data = {'time': today, 'data': result}
|
||||
public.writeFile(ssh_intrusion_file, json.dumps(ssh_intrusion_data))
|
||||
|
||||
return result
|
||||
# return public.return_message(0, 0, result)
|
||||
|
||||
def get_ssh_cache(self):
|
||||
"""
|
||||
@获取缓存ssh记录
|
||||
"""
|
||||
file = '{}/data/ssh_cache.json'.format(public.get_panel_path())
|
||||
cache_data = {'success': {}, 'error': {}, 'today_success': {}, 'today_error': {}}
|
||||
if not os.path.exists(file):
|
||||
public.writeFile(file, json.dumps(cache_data))
|
||||
return cache_data
|
||||
|
||||
try:
|
||||
data = json.loads(public.readFile(file))
|
||||
except:
|
||||
public.writeFile(file, json.dumps(cache_data))
|
||||
data = cache_data
|
||||
|
||||
return data
|
||||
|
||||
def set_ssh_cache(self,data):
|
||||
"""
|
||||
@设置ssh缓存
|
||||
"""
|
||||
file = '{}/data/ssh_cache.json'.format(public.get_panel_path())
|
||||
public.writeFile(file,json.dumps(data))
|
||||
return True
|
||||
|
||||
|
||||
def GetSshInfo(self,get):
|
||||
"""
|
||||
@获取SSH登录信息
|
||||
|
||||
"""
|
||||
port = public.get_sshd_port()
|
||||
status = public.get_sshd_status()
|
||||
isPing = True
|
||||
try:
|
||||
file = '/etc/sysctl.conf'
|
||||
conf = public.readFile(file)
|
||||
rep = r"#*net\.ipv4\.icmp_echo_ignore_all\s*=\s*([0-9]+)"
|
||||
tmp = re.search(rep,conf).groups(0)[0]
|
||||
if tmp == '1': isPing = False
|
||||
except:
|
||||
isPing = True
|
||||
from ssh_security_v2 import ssh_security
|
||||
data = {}
|
||||
data['port'] = port
|
||||
data['status'] = status
|
||||
data['ping'] = isPing
|
||||
data['config'] = ssh_security().get_config(None).get("message", {})
|
||||
data['firewall_status'] = self.CheckFirewallStatus()
|
||||
# data['error'] = self.get_ssh_intrusion(get)
|
||||
data['fail2ban'] = self._get_ssh_fail2ban()
|
||||
return public.return_message(0, 0, data)
|
||||
|
||||
def get_ssh_login_info(self, get):
|
||||
"""
|
||||
@获取SSH登录信息
|
||||
"""
|
||||
# return self.get_ssh_intrusion(get)
|
||||
|
||||
return public.return_message(0, 0, self.get_ssh_intrusion(get))
|
||||
|
||||
@staticmethod
|
||||
def _get_ssh_fail2ban():
|
||||
"""
|
||||
@name 获取fail2ban的服务和SSH防爆破状态
|
||||
@return:
|
||||
"""
|
||||
plugin_path = "/www/server/panel/plugin/fail2ban"
|
||||
result_data = {"status": 0, "installed": 1}
|
||||
if not os.path.exists("{}".format(plugin_path)):
|
||||
result_data['installed'] = 0
|
||||
return result_data
|
||||
|
||||
sock = "{}/fail2ban.sock".format(plugin_path)
|
||||
if not os.path.exists(sock):
|
||||
return result_data
|
||||
|
||||
s_file = '{}/plugin/fail2ban/config.json'.format(public.get_panel_path())
|
||||
if os.path.exists(s_file):
|
||||
try:
|
||||
data = json.loads(public.readFile(s_file))
|
||||
if 'sshd' in data:
|
||||
if data['sshd']['act'] == 'true':
|
||||
result_data['status'] = 1
|
||||
return result_data
|
||||
except:
|
||||
pass
|
||||
|
||||
return result_data
|
||||
|
||||
#改远程端口
|
||||
def SetSshPort(self,get):
|
||||
port = get.port
|
||||
if int(port) < 22 or int(port) > 65535: return public.returnMsg(False,'Port range must be between 22 and 65535!')
|
||||
ports = ['21','25','80','443','8080','888','8888']
|
||||
if port in ports: return public.returnMsg(False,'Please dont use default ports for common programs!')
|
||||
file = '/etc/ssh/sshd_config'
|
||||
conf = public.readFile(file)
|
||||
|
||||
rep = r"#*Port\s+([0-9]+)\s*\n"
|
||||
conf = re.sub(rep, "Port "+port+"\n", conf)
|
||||
public.writeFile(file,conf)
|
||||
|
||||
if self.__isFirewalld:
|
||||
public.ExecShell('firewall-cmd --permanent --zone=public --add-port='+port+'/tcp')
|
||||
public.ExecShell('setenforce 0')
|
||||
public.ExecShell('sed -i "s#SELINUX=enforcing#SELINUX=disabled#" /etc/selinux/config')
|
||||
public.ExecShell("systemctl restart sshd.service")
|
||||
elif self.__isUfw:
|
||||
public.ExecShell('ufw allow ' + port + '/tcp')
|
||||
public.ExecShell("service ssh restart")
|
||||
else:
|
||||
public.ExecShell('iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport '+port+' -j ACCEPT')
|
||||
public.ExecShell("/etc/init.d/sshd restart")
|
||||
|
||||
self.FirewallReload()
|
||||
public.M('firewall').where("ps=? or ps=? or port=?",('SSH remote management service','SSH remote service',port)).delete()
|
||||
public.M('firewall').add('port,ps,addtime',(port,'SSH remote service',time.strftime('%Y-%m-%d %X',time.localtime())))
|
||||
public.WriteLog("TYPE_FIREWALL", "FIREWALL_SSH_PORT",(port,))
|
||||
return public.return_message(0, 0,'Successfully modified')
|
||||
|
||||
|
||||
|
||||
def SetSshStatus(self,get):
|
||||
"""
|
||||
@设置SSH状态
|
||||
"""
|
||||
get.exists(["status"])
|
||||
if int(get['status'])==1:
|
||||
msg = public.getMsg('FIREWALL_SSH_STOP')
|
||||
act = 'stop'
|
||||
else:
|
||||
msg = public.getMsg('FIREWALL_SSH_START')
|
||||
act = 'start'
|
||||
|
||||
public.ExecShell("/etc/init.d/sshd "+act)
|
||||
public.ExecShell('service ssh ' + act)
|
||||
public.ExecShell("systemctl "+act+" sshd")
|
||||
public.ExecShell("systemctl "+act+" ssh")
|
||||
|
||||
public.WriteLog("TYPE_FIREWALL", msg)
|
||||
return public.return_message(0, 0,'SUCCESS')
|
||||
|
||||
|
||||
1343
class_v2/safeModelV2/syslogModel.py
Normal file
1343
class_v2/safeModelV2/syslogModel.py
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user