Files
yakpanel-core/class/ssh_security.py
2026-04-07 02:04:22 +05:30

906 lines
36 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#coding: utf-8
#-------------------------------------------------------------------
# YakPanel
#-------------------------------------------------------------------
# Copyright (c) 2015-2017 YakPanel(www.yakpanel.com) All rights reserved.
#-------------------------------------------------------------------
# Author: lkqiang <lkq@yakpanel.com>
#-------------------------------------------------------------------
# SSH 安全类
#------------------------------
import public,os,re,send_mail,json
from datetime import datetime
class ssh_security:
__type_list = ['ed25519','ecdsa','rsa', 'dsa']
__key_type_file = '{}/data/ssh_key_type.pl'.format(public.get_panel_path())
__key_files = ['/root/.ssh/id_ed25519','/root/.ssh/id_ecdsa','/root/.ssh/id_rsa','/root/.ssh/id_rsa_bt']
__type_files = {
"ed25519": "/root/.ssh/id_ed25519",
"ecdsa": "/root/.ssh/id_ecdsa",
"rsa": "/root/.ssh/id_rsa",
"dsa": "/root/.ssh/id_dsa"
}
open_ssh_login = public.get_panel_path() + '/data/open_ssh_login.pl'
__SSH_CONFIG='/etc/ssh/sshd_config'
__ip_data = None
__ClIENT_IP='/www/server/panel/data/host_login_ip.json'
__pyenv = 'python'
__REPAIR={"1":{"id":1,
"type":"file",
"harm":"High",
"repaired":"1",
"level":"3",
"name":"Make sure SSH MaxAuthTries is set between 3-6",
"file":"/etc/ssh/sshd_config",
"Suggestions":"Remove the MaxAuthTries comment symbol # in /etc/ssh/sshd_config, set the maximum number of failed password attempts 3-6 recommended 4",
"repair":"MaxAuthTries 4",
"rule":[{"re":"\nMaxAuthTries\\s*(\\d+)","check":{"type":"number","max":7,"min":3}}],
"repair_loophole":[{"re":"\n?#?MaxAuthTries\\s*(\\d+)","check":"\nMaxAuthTries 4"}]},
"2":{"id":2,
"repaired":"1",
"type":"file",
"harm":"High",
"level":"3",
"name":"SSHD Mandatory use of V2 security protocol",
"file":"/etc/ssh/sshd_config",
"Suggestions":"Set parameters in the /etc/ssh/sshd_config file as follows",
"repair":"Protocol 2",
"rule":[{"re":"\nProtocol\\s*(\\d+)",
"check":{"type":"number","max":3,"min":1}}],
"repair_loophole":[{"re":"\n?#?Protocol\\s*(\\d+)","check":"\nProtocol 2"}]},
"3":{"id":3,
"repaired":"1",
"type":"file",
"harm":"High",
"level":"3",
"name":"Set SSH idle exit time",
"file":"/etc/ssh/sshd_config",
"Suggestions":"Set ClientAliveInterval to 300 to 900 in /etc/ssh/sshd_config, which is 5-15 minutes, and set ClientAliveCountMax to 0-3",
"repair":"ClientAliveInterval 600 ClientAliveCountMax 2",
"rule":[{"re":"\nClientAliveInterval\\s*(\\d+)","check":{"type":"number","max":900,"min":300}}],
"repair_loophole":[{"re":"\n?#?ClientAliveInterval\\s*(\\d+)","check":"\nClientAliveInterval 600"}]},
"4":{"id":4,
"repaired":"1",
"type":"file",
"harm":"High",
"level":"3",
"name":"Make sure SSH LogLevel is set to INFO",
"file":"/etc/ssh/sshd_config",
"Suggestions":"Set parameters in the /etc/ssh/sshd_config file as follows (uncomment)",
"repair":"LogLevel INFO",
"rule":[{"re":"\nLogLevel\\s*(\\w+)","check":{"type":"string","value":["INFO"]}}],
"repair_loophole":[{"re":"\n?#?LogLevel\\s*(\\w+)","check":"\nLogLevel INFO"}]},
"5":{"id":5,
"repaired":"1",
"type":"file",
"harm":"High",
"level":"3",
"name":"Disable SSH users with empty passwords from logging in",
"file":"/etc/ssh/sshd_config",
"Suggestions":"Configure PermitEmptyPasswords to no in /etc/ssh/sshd_config",
"repair":"PermitEmptyPasswords no",
"rule":[{"re":"\nPermitEmptyPasswords\\s*(\\w+)","check":{"type":"string","value":["no"]}}],
"repair_loophole":[{"re":"\n?#?PermitEmptyPasswords\\s*(\\w+)","check":"\nPermitEmptyPasswords no"}]},
"6":{"id":6,
"repaired":"1",
"type":"file",
"name":"SSH uses the default port 22",
"harm":"High",
"level":"3",
"file":"/etc/ssh/sshd_config",
"Suggestions":"Set Port to 6000 to 65535 in / etc / ssh / sshd_config",
"repair":"Port 60151",
"rule":[{"re":"Port\\s*(\\d+)","check":{"type":"number","max":65535,"min":22}}],
"repair_loophole":[{"re":"\n?#?Port\\s*(\\d+)","check":"\nPort 65531"}]}}
__root_login_types = {'yes':'yes - keys and passwords','no':'no - no login','without-password':'without-password - only key login','forced-commands-only':'forced-commands-only - can only execute commands'}
def __init__(self):
if not public.M('sqlite_master').where('type=? AND name=?', ('table', 'ssh_login_record')).count():
public.M('').execute('''CREATE TABLE ssh_login_record (
id INTEGER PRIMARY KEY AUTOINCREMENT,
addr TEXT,
server_ip TEXT,
user_agent TEXT,
ssh_user TEXT,
login_time INTEGER DEFAULT 0,
close_time INTEGER DEFAULT 0,
video_addr TEXT);''')
public.M('').execute('CREATE INDEX ssh_login_record ON ssh_login_record (addr);')
if not os.path.exists(self.__ClIENT_IP):
public.WriteFile(self.__ClIENT_IP,json.dumps([]))
self.__mail=send_mail.send_mail()
self.__mail_config=self.__mail.get_settings()
self._check_pyenv()
try:
self.__ip_data = json.loads(public.ReadFile(self.__ClIENT_IP))
except:
self.__ip_data=[]
def _check_pyenv(self):
if os.path.exists('/www/server/panel/pyenv'):
self.__pyenv = 'btpython'
def get_ssh_key_type(self):
'''
获取ssh密钥类型
@author hwliang
:return:
'''
default_type = 'rsa'
if not os.path.exists(self.__key_type_file):
return default_type
new_type = public.ReadFile(self.__key_type_file)
if new_type in self.__type_list:
return new_type
return default_type
def return_python(self):
if os.path.exists('/www/server/panel/pyenv/bin/python'):return '/www/server/panel/pyenv/bin/python'
if os.path.exists('/usr/bin/python'):return '/usr/bin/python'
if os.path.exists('/usr/bin/python3'):return '/usr/bin/python3'
return 'python'
def return_profile(self):
if os.path.exists('/root/.bash_profile'): return '/root/.bash_profile'
if os.path.exists('/etc/profile'): return '/etc/profile'
fd = open('/root/.bash_profil', mode="w", encoding="utf-8")
fd.close()
return '/root/.bash_profil'
def return_bashrc(self):
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'
fd = open('/root/.bashrc', mode="w", encoding="utf-8")
fd.close()
return '/root/.bashrc'
def check_files(self):
try:
json.loads(public.ReadFile(self.__ClIENT_IP))
except:
public.WriteFile(self.__ClIENT_IP, json.dumps([]))
def get_ssh_port(self):
conf = public.readFile(self.__SSH_CONFIG)
if not conf: conf = ''
rep = r"#*Port\s+([0-9]+)\s*\n"
tmp1 = re.search(rep,conf)
port = '22'
if tmp1:
port = tmp1.groups(0)[0]
return port
# 主判断函数
def check_san_baseline(self, base_json):
if base_json['type'] == 'file':
if 'check_file' in base_json:
if not os.path.exists(base_json['check_file']):
return False
else:
if os.path.exists(base_json['file']):
ret = public.ReadFile(base_json['file'])
for i in base_json['rule']:
valuse = re.findall(i['re'], ret)
if i['check']['type'] == 'number':
if not valuse: return False
if not valuse[0]: return False
valuse = int(valuse[0])
if valuse > i['check']['min'] and valuse < i['check']['max']:
return True
else:
return False
elif i['check']['type'] == 'string':
if not valuse: return False
if not valuse[0]: return False
valuse = valuse[0]
if valuse in i['check']['value']:
return True
else:
return False
return True
def san_ssh_security(self,get):
data={"num":100,"result":[]}
result = []
ret = self.check_san_baseline(self.__REPAIR['1'])
if not ret: result.append(self.__REPAIR['1'])
ret = self.check_san_baseline(self.__REPAIR['2'])
if not ret: result.append(self.__REPAIR['2'])
ret = self.check_san_baseline(self.__REPAIR['3'])
if not ret: result.append(self.__REPAIR['3'])
ret = self.check_san_baseline(self.__REPAIR['4'])
if not ret: result.append(self.__REPAIR['4'])
ret = self.check_san_baseline(self.__REPAIR['5'])
if not ret: result.append(self.__REPAIR['5'])
ret = self.check_san_baseline(self.__REPAIR['6'])
if not ret: result.append(self.__REPAIR['6'])
data["result"]=result
if len(result)>=1:
data['num']=data['num']-(len(result)*10)
return data
################## SSH 登陆报警设置 ####################################
def send_mail_data(self,title,body,type=None):
try:
login_send_type_conf = "/www/server/panel/data/ssh_send_type.pl"
if not os.path.exists(login_send_type_conf):
return
# login_type = "mail"
else:
login_type = public.readFile(login_send_type_conf).strip()
if not login_type:
login_type = "mail"
object = public.init_msg(login_type.strip())
if not object:
return False
if login_type=="mail":
data={}
data['title'] = title
data['msg'] = body
object.push_data(data)
elif login_type=="wx_account":
object.send_msg(body)
else:
msg = public.get_push_info("SSH login warning",['>Send Content' + body])
object.push_data(msg)
except:
pass
#检测非UID为0的账户
def check_user(self):
ret = []
cfile = '/etc/passwd'
if os.path.exists(cfile):
f = open(cfile, 'r')
for i in f:
i = i.strip().split(":")
if i[2] == '0' and i[3] == '0':
if i[0] == 'root': continue
ret.append(i[0])
if ret:
data=''.join(ret)
public.run_thread(self.send_mail_data,args=(public.GetLocalIp()+' There is a backdoor user in the server',public.GetLocalIp()+' There is a backdoor user in the server '+data+' please check/etc/passwd',))
return True
else:
return False
#记录root 的登陆日志
#返回登陆IP
def return_ip(self,get):
self.check_files()
return public.returnMsg(True, self.__ip_data)
#添加IP白名单
def add_return_ip(self, get):
self.check_files()
if get.ip.strip() in self.__ip_data:
return public.returnMsg(False, public.lang("Already exists"))
else:
self.__ip_data.append(get.ip.strip())
public.writeFile(self.__ClIENT_IP, json.dumps(self.__ip_data))
return public.returnMsg(True, public.lang("Added successfully"))
def del_return_ip(self, get):
self.check_files()
if get.ip.strip() in self.__ip_data:
self.__ip_data.remove(get.ip.strip())
public.writeFile(self.__ClIENT_IP, json.dumps(self.__ip_data))
return public.returnMsg(True, public.lang("Successfully deleted"))
else:
return public.returnMsg(False, public.lang("IP does not exist"))
#取登陆的前50个条记录
def login_last(self):
self.check_files()
data=public.ExecShell('last -n 50')
data=re.findall(r"(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)",data[0])
if data>=1:
data2=list(set(data))
for i in data2:
if not i in self.__ip_data:
self.__ip_data.append(i)
public.writeFile(self.__ClIENT_IP, json.dumps(self.__ip_data))
return self.__ip_data
#获取ROOT当前登陆的IP
def get_ip(self):
data = public.ExecShell(''' who am i |awk ' {print $5 }' ''')
data = re.findall(r"(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)",data[0])
return data
def get_logs(self, get):
import page
page = page.Page();
count = public.M('logs').where('type=?', ('SSH security',)).count()
limit = 10
info = {}
info['count'] = count
info['row'] = limit
info['p'] = 1
if hasattr(get, 'p'):
info['p'] = int(get['p'])
info['uri'] = get
info['return_js'] = ''
if hasattr(get, 'tojs'):
info['return_js'] = get.tojs
data = {}
# 获取分页数据
data['page'] = page.GetPage(info, '1,2,3,4,5,8')
data['data'] = public.M('logs').where('type=?', (u'SSH security',)).order('id desc').limit(
str(page.SHIFT) + ',' + str(page.ROW)).field('log,addtime').select()
return data
def get_server_ip(self):
if os.path.exists('/www/server/panel/data/iplist.txt'):
data=public.ReadFile('/www/server/panel/data/iplist.txt')
return data.strip()
else:return '127.0.0.1'
#登陆的情况下
def login(self):
self.check_files()
self.check_user()
self.__ip_data = json.loads(public.ReadFile(self.__ClIENT_IP))
ip=self.get_ip()
if len(ip[0])==0:return False
try:
import time
mDate = time.strftime('%Y-%m-%d %X', time.localtime())
if ip[0] in self.__ip_data:
if public.M('logs').where('type=? addtime', ('SSH security',mDate,)).count():return False
public.WriteLog('SSH security', 'The server {} login IP is {}, login user is root'.format(public.GetLocalIp(),ip[0]))
return False
else:
if public.M('logs').where('type=? addtime', ('SSH security', mDate,)).count(): return False
self.send_mail_data('Server {} login alarm'.format(public.GetLocalIp()),'There is a login alarm on the server {}, the login IP is {}, the login user is root'.format(public.GetLocalIp(),ip[0]))
public.WriteLog('SSH security','There is a login alarm on the server {}, the login IP is {}, login user is root'.format(public.GetLocalIp(),ip [0]))
return True
except:
pass
#修复bashrc文件
def repair_bashrc(self):
data = public.ReadFile(self.return_bashrc())
if re.search(self.return_python() + ' /www/server/panel/class/ssh_security.py', data):
public.WriteFile(self.return_bashrc(),data.replace(self.return_python()+' /www/server/panel/class/ssh_security.py login',''))
#遗留的错误信息
datassss = public.ReadFile(self.return_bashrc())
if re.search(self.return_python(),datassss):
public.WriteFile(self.return_bashrc(),datassss.replace(self.return_python(),''))
#开启监控
def start_jian(self,get):
self.repair_bashrc()
data = public.ReadFile(self.return_profile())
if not re.search(self.return_python() + ' /www/server/panel/class/ssh_security.py', data):
cmd = '''shell="%s /www/server/panel/class/ssh_security.py login"
nohup `${shell}` &>/dev/null &
disown $!''' % (self.return_python())
public.WriteFile(self.return_profile(), data.strip() + '\n' + cmd)
return public.returnMsg(True, public.lang("Open successfully"))
return public.returnMsg(False, public.lang("Open failed"))
#关闭监控
def stop_jian(self,get):
data = public.ReadFile(self.return_profile())
if re.search(self.return_python()+' /www/server/panel/class/ssh_security.py', data):
cmd='''shell="%s /www/server/panel/class/ssh_security.py login"'''%(self.return_python())
data=data.replace(cmd, '')
cmd='''nohup `${shell}` &>/dev/null &'''
data=data.replace(cmd, '')
cmd='''disown $!'''
data=data.replace(cmd, '')
public.WriteFile(self.return_profile(),data)
#检查是否还存在遗留
if re.search(self.return_python()+' /www/server/panel/class/ssh_security.py', data):
public.WriteFile(self.return_profile(),data.replace(self.return_python()+' /www/server/panel/class/ssh_security.py login',''))
#遗留的错误信息
datassss = public.ReadFile(self.return_profile())
if re.search(self.return_python(),datassss):
public.WriteFile(self.return_profile(),datassss.replace(self.return_python(),''))
return public.returnMsg(True, public.lang("Closed successfully"))
else:
return public.returnMsg(True, public.lang("Closed successfully"))
#监控状态
def get_jian(self,get):
data = public.ReadFile(self.return_profile())
#if re.search(r'{}\/www\/server\/panel\/class\/ssh_security.py\s+login'.format(r".*python\s+"), data):
if re.search('/www/server/panel/class/ssh_security.py login', data):
return public.returnMsg(True, public.lang("1"))
else:
return public.returnMsg(False, public.lang("1"))
def set_password(self, get):
'''
开启密码登陆
get: 无需传递参数
'''
ssh_password = r'\n#?PasswordAuthentication\s\w+'
file = public.readFile(self.__SSH_CONFIG)
if not file: return public.returnMsg(False, public.lang("ERROR: sshd config configuration file does not exist, cannot continue!"))
if len(re.findall(ssh_password, file)) == 0:
file_result = file + '\nPasswordAuthentication yes'
else:
file_result = re.sub(ssh_password, '\nPasswordAuthentication yes', file)
self.wirte(self.__SSH_CONFIG, file_result)
self.restart_ssh()
public.WriteLog('SSH management', 'Enable password login')
return public.returnMsg(True, public.lang("Open successfully"))
def set_sshkey(self, get):
'''
设置ssh 的key
参数 ssh=rsa&type=yes
'''
ssh_type = ['yes', 'no']
ssh = get.ssh
if not ssh in ssh_type: return public.returnMsg(False, public.lang("ssh option failed"))
s_type = get.type
if not s_type in self.__type_list: return public.returnMsg(False, public.lang("Wrong encryption method"))
authorized_keys = '/root/.ssh/authorized_keys'
file = ['/root/.ssh/id_{}.pub'.format(s_type), '/root/.ssh/id_{}'.format(s_type)]
for i in file:
if os.path.exists(i):
public.ExecShell(r'sed -i "\~$(cat %s)~d" %s' % (file[0], authorized_keys))
os.remove(i)
os.system("ssh-keygen -t {s_type} -P '' -f /root/.ssh/id_{s_type} |echo y".format(s_type = s_type))
if os.path.exists(file[0]):
public.ExecShell('cat %s >> %s && chmod 600 %s' % (file[0], authorized_keys, authorized_keys))
rec = r'\n#?RSAAuthentication\s\w+'
rec2 = r'\n#?PubkeyAuthentication\s\w+'
file = public.readFile(self.__SSH_CONFIG)
if not file: return public.returnMsg(False, public.lang("ERROR: sshd config configuration file does not exist, cannot continue!"))
if len(re.findall(rec, file)) == 0: file = file + '\nRSAAuthentication yes'
if len(re.findall(rec2, file)) == 0: file = file + '\nPubkeyAuthentication yes'
file_ssh = re.sub(rec, '\nRSAAuthentication yes', file)
file_result = re.sub(rec2, '\nPubkeyAuthentication yes', file_ssh)
if ssh == 'no':
ssh_password = r'\n#?PasswordAuthentication\s\w+'
if len(re.findall(ssh_password, file_result)) == 0:
file_result = file_result + '\nPasswordAuthentication no'
else:
file_result = re.sub(ssh_password, '\nPasswordAuthentication no', file_result)
self.wirte(self.__SSH_CONFIG, file_result)
public.writeFile(self.__key_type_file, s_type)
self.restart_ssh()
public.WriteLog('SSH management', 'Set up SSH key authentication and successfully generate the key')
return public.returnMsg(True, public.lang("Open successfully"))
else:
public.WriteLog('SSH management', 'Failed to set SSH key authentication')
return public.returnMsg(False, public.lang("Open failed"))
# 取SSH信息
def get_msg_push_list(self,get):
"""
@name 获取消息通道配置列表
@auther: cjxin
@date: 2022-08-16
@
"""
cpath = 'data/msg.json'
try:
if 'force' in get or not os.path.exists(cpath):
public.downloadFile('{}/linux/panel/msg/msg.json'.format("https://node.yakpanel.com"),cpath)
except : pass
data = {}
if os.path.exists(cpath):
msgs = json.loads(public.readFile(cpath))
for x in msgs:
x['setup'] = False
x['info'] = False
key = x['name']
try:
obj = public.init_msg(x['name'])
if obj:
x['setup'] = True
x['info'] = obj.get_version_info(None)
except :
print(public.get_error_info())
pass
data[key] = x
return data
#取消告警
def clear_login_send(self,get):
login_send_type_conf = "/www/server/panel/data/ssh_send_type.pl"
os.remove(login_send_type_conf)
self.stop_jian(get)
return public.returnMsg(True, public.lang("Successfully cancel the login alarm"))
#设置告警
def set_login_send(self,get):
login_send_type_conf = "/www/server/panel/data/ssh_send_type.pl"
set_type=get.type.strip()
msg_configs = self.get_msg_push_list(get)
if set_type not in msg_configs.keys():
return public.returnMsg(False, public.lang("This send type is not supported"))
from panelMessage import panelMessage
pm = panelMessage()
obj = pm.init_msg_module(set_type)
if not obj:
return public.returnMsg(False, public.lang("The message channel is not installed."))
public.writeFile(login_send_type_conf, set_type)
self.start_jian(get)
return public.returnMsg(True, public.lang("Successfully set"))
#查看告警
def get_login_send(self, get):
login_send_type_conf = "/www/server/panel/data/ssh_send_type.pl"
if os.path.exists(login_send_type_conf):
send_type = public.readFile(login_send_type_conf).strip()
else:
send_type ="error"
return public.returnMsg(True, send_type)
def GetSshInfo(self):
# port = public.get_ssh_port()
pid_file = '/run/sshd.pid'
if os.path.exists(pid_file):
pid = int(public.readFile(pid_file))
status = public.pid_exists(pid)
else:
import system
panelsys = system.system()
version = panelsys.GetSystemVersion()
if os.path.exists('/usr/bin/apt-get'):
if os.path.exists('/etc/init.d/sshd'):
status = public.ExecShell("service sshd status | grep -P '(dead|stop)'|grep -v grep")
else:
status = public.ExecShell("service ssh status | grep -P '(dead|stop)'|grep -v grep")
else:
if version.find(' 7.') != -1 or version.find(' 8.') != -1 or version.find('Fedora') != -1:
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")
# return status;
if len(status[0]) > 3:
status = False
else:
status = True
return status
def stop_key(self, get):
'''
关闭key
无需参数传递
'''
is_ssh_status=self.GetSshInfo()
rec = r'\n\s*#?\s*RSAAuthentication\s+\w+'
rec2 = r'\n\s*#?\s*PubkeyAuthentication\s+\w+'
file = public.readFile(self.__SSH_CONFIG)
if not file: return public.returnMsg(False, public.lang("错误sshd_config配置文件不存在无法继续!"))
file_ssh = re.sub(rec, '\nRSAAuthentication no', file)
file_result = re.sub(rec2, '\nPubkeyAuthentication no', file_ssh)
self.wirte(self.__SSH_CONFIG, file_result)
if is_ssh_status:
self.set_password(get)
self.restart_ssh()
public.WriteLog('SSH management','Disable SSH key login')
return public.returnMsg(True, public.lang("Disable successfully"))
def get_config(self, get):
'''
获取配置文件
无参数传递
'''
result = {}
file = public.readFile(self.__SSH_CONFIG)
if not file: return public.returnMsg(False, public.lang("错误sshd_config配置文件不存在无法继续!"))
# ======== 以下在2022-10-12重构 ==========
# author : hwliang
# 是否开启RSA公钥认证
# 默认开启(最新版openssh已经不支持RSA公钥认证)
# yes = 开启
# no = 关闭
result['rsa_auth'] = 'yes'
rec = r'^\s*RSAAuthentication\s*(yes|no)'
rsa_find = re.findall(rec, file, re.M|re.I)
if rsa_find and rsa_find[0].lower() == 'no': result['rsa_auth'] = 'no'
# 获取是否开启公钥认证
# 默认关闭
# yes = 开启
# no = 关闭
result['pubkey'] = 'no'
if self.get_key(get)['msg']: # 先检查是否存在可用的公钥
pubkey = r'^\s*PubkeyAuthentication\s*(yes|no)'
pubkey_find = re.findall(pubkey, file, re.M|re.I)
if pubkey_find and pubkey_find[0].lower() == 'yes': result['pubkey'] = 'yes'
# 是否开启密码登录
# 默认开启
# yes = 开启
# no = 关闭
result['password'] = 'yes'
ssh_password = r'^\s*PasswordAuthentication\s*([\w\-]+)'
ssh_password_find = re.findall(ssh_password, file, re.M|re.I)
if ssh_password_find and ssh_password_find[0].lower() == 'no': result['password'] = 'no'
#是否允许root登录
# 默认允许
# yes = 允许
# no = 不允许
# without-password = 允许,但不允许使用密码登录
# forced-commands-only = 允许,但只允许执行命令,不能使用终端
result['root_is_login'] = 'yes'
result['root_login_type'] = 'yes'
root_is_login=r'^\s*PermitRootLogin\s*([\w\-]+)'
root_is_login_find = re.findall(root_is_login, file, re.M|re.I)
if root_is_login_find and root_is_login_find[0].lower() != 'yes':
result['root_is_login'] = 'no'
result['root_login_type'] = root_is_login_find[0].lower()
result['root_login_types'] = self.__root_login_types
return result
def set_root(self, get):
'''
开启密码登陆
get: 无需传递参数
'''
p_type = 'yes'
if 'p_type' in get: p_type = get.p_type
if p_type not in self.__root_login_types.keys():
return public.returnMsg(False, public.lang("Parameter passing error!"))
ssh_password = r'^\s*#?\s*PermitRootLogin\s*([\w\-]+)'
file = public.readFile(self.__SSH_CONFIG)
src_line = re.search(ssh_password, file,re.M)
new_line = 'PermitRootLogin {}'.format(p_type)
if not src_line:
file_result = file + '\n{}'.format(new_line)
else:
file_result = file.replace(src_line.group(),new_line)
self.wirte(self.__SSH_CONFIG, file_result)
self.restart_ssh()
msg = 'Set the root login method as: {}'.format(self.__root_login_types[p_type])
public.WriteLog('SSH management',msg)
return public.returnMsg(True, msg)
def stop_root(self, get):
'''
开启密码登陆
get: 无需传递参数
'''
ssh_password = r'\n\s*PermitRootLogin\s+\w+'
file = public.readFile(self.__SSH_CONFIG)
if len(re.findall(ssh_password, file)) == 0:
file_result = file + '\nPermitRootLogin no'
else:
file_result = re.sub(ssh_password, '\nPermitRootLogin no', file)
self.wirte(self.__SSH_CONFIG, file_result)
self.restart_ssh()
public.WriteLog('SSH management','Set the root login method to: no')
return public.returnMsg(True, public.lang("Disable successfully"))
def stop_password(self, get):
'''
关闭密码访问
无参数传递
'''
file = public.readFile(self.__SSH_CONFIG)
ssh_password = r'\n#?PasswordAuthentication\s\w+'
file_result = re.sub(ssh_password, '\nPasswordAuthentication no', file)
self.wirte(self.__SSH_CONFIG, file_result)
self.restart_ssh()
public.WriteLog('SSH management','Disable password access')
return public.returnMsg(True, public.lang("Closed successfully"))
def get_key(self, get):
'''
获取key 无参数传递
'''
key_type = self.get_ssh_key_type()
if key_type in self.__type_files.keys():
key_file = self.__type_files[key_type]
key = public.readFile(key_file)
return public.returnMsg(True,key)
return public.returnMsg(True, public.lang(""))
def download_key(self, get):
'''
@name 下载密钥
'''
download_file = ''
key_type = self.get_ssh_key_type()
if key_type in self.__type_files.keys():
if os.path.exists(self.__type_files[key_type]):
download_file = self.__type_files[key_type]
else:
for file in self.__key_files:
if not os.path.exists(file): continue
download_file = file
break
if not download_file: return public.returnMsg(False, public.lang("Key file not found!"))
from flask import send_file
filename = "{}_{}".format(public.GetHost(),os.path.basename(download_file))
return send_file(download_file,download_name=filename)
def wirte(self, file, ret):
result = public.writeFile(file, ret)
return result
def restart_ssh(self):
'''
重启ssh 无参数传递
'''
version = public.readFile('/etc/redhat-release')
act = 'restart'
if not os.path.exists('/etc/redhat-release'):
public.ExecShell('service ssh ' + act)
elif version.find(' 7.') != -1 or version.find(' 8.') != -1:
public.ExecShell("systemctl " + act + " sshd.service")
else:
public.ExecShell("/etc/init.d/sshd " + act)
#检查是否设置了钉钉
def check_dingding(self, get):
'''
检查是否设置了钉钉
'''
#检查文件是否存在
if not os.path.exists('/www/server/panel/data/dingding.json'):return False
dingding_config=public.ReadFile('/www/server/panel/data/dingding.json')
if not dingding_config:return False
#解析json
try:
dingding=json.loads(dingding_config)
if dingding['dingding_url']:
return True
except:
return False
#开启SSH双因子认证
def start_auth_method(self, get):
'''
开启SSH双因子认证
'''
#检查是否设置了钉钉
import ssh_authentication
ssh_class=ssh_authentication.ssh_authentication()
return ssh_class.start_ssh_authentication_two_factors()
#关闭SSH双因子认证
def stop_auth_method(self, get):
'''
关闭SSH双因子认证
'''
#检查是否设置了钉钉
import ssh_authentication
ssh_class=ssh_authentication.ssh_authentication()
return ssh_class.close_ssh_authentication_two_factors()
#获取SSH双因子认证状态
def get_auth_method(self, get):
'''
获取SSH双因子认证状态
'''
#检查是否设置了钉钉
import ssh_authentication
ssh_class=ssh_authentication.ssh_authentication()
return ssh_class.check_ssh_authentication_two_factors()
#判断so文件是否存在
def check_so_file(self, get):
'''
判断so文件是否存在
'''
import ssh_authentication
ssh_class=ssh_authentication.ssh_authentication()
return ssh_class.is_check_so()
#下载so文件
def get_so_file(self, get):
'''
下载so文件
'''
import ssh_authentication
ssh_class=ssh_authentication.ssh_authentication()
return ssh_class.download_so()
#获取pin
def get_pin(self, get):
'''
获取pin
'''
import ssh_authentication
ssh_class=ssh_authentication.ssh_authentication()
return public.returnMsg(True, ssh_class.get_pin())
def get_login_record(self,get):
if os.path.exists(self.open_ssh_login):
return public.returnMsg(True, public.lang(""))
else:
return public.returnMsg(False, public.lang(""))
def start_login_record(self,get):
if os.path.exists(self.open_ssh_login):
return public.returnMsg(True, public.lang(""))
else:
public.writeFile(self.open_ssh_login,"True")
return public.returnMsg(True, public.lang(""))
def stop_login_record(self,get):
if os.path.exists(self.open_ssh_login):
os.remove(self.open_ssh_login)
return public.returnMsg(True, public.lang(""))
else:
return public.returnMsg(True, public.lang(""))
# 获取登录记录列表
def get_record_list(self, get):
if 'limit' in get:
limit = int(get.limit.strip())
else:
limit = 12
import page
page = page.Page()
count = public.M('ssh_login_record').order("id desc").count()
info = {}
info['count'] = count
info['row'] = limit
info['p'] = 1
if hasattr(get, 'p'):
info['p'] = int(get['p'])
info['uri'] = get
info['return_js'] = ''
if hasattr(get, 'tojs'):
info['return_js'] = get.tojs
data = {}
# 获取分页数据
data['page'] = page.GetPage(info, '1,2,3,4,5,8')
data['data'] = public.M('ssh_login_record').order('id desc').limit(
str(page.SHIFT) + ',' + str(page.ROW)).select()
return data
def get_file_json(self,get):
if os.path.exists(get.path):
ret=json.loads(public.ReadFile(get.path))
return ret
else:
return ''
if __name__ == '__main__':
import sys
type = sys.argv[1]
if type=='login':
try:
aa = ssh_security()
aa.login()
except:pass
else:
pass