项目背景


需求分析

# 解决需求:
1、记录用户操作    实现方式1:改ssh客户端的源代码,10w+  实现方式2:修改已有python ssh 库,加入指令记录的功能(推荐)
2、实现权限管理: A  支付系统的机器(500) 10.0.1.11  root10.0.1.12  mysql     B   10.0.1.12 root 3、日志记录:记入到数据库
4、密码&秘钥:2种方式实现服务器登陆

数据库设计


from django.db import models
from django.contrib.auth.models import User# Create your models here.
class IDC(models.Model):"""    机房信息    """name = models.CharField(max_length=64, unique=True)def __str__(self):return self.nameclass Host(models.Model):"""存储所有主机信息"""hostname = models.CharField(max_length=64, unique=True)ip_addr = models.GenericIPAddressField(unique=True)port = models.IntegerField(default=22)idc = models.ForeignKey('IDC', on_delete='')enabled = models.BooleanField(default=True)def __str__(self):return '%s-%s' % (self.hostname, self.ip_addr)class HostGroup(models.Model):"""主机组"""name = models.CharField(max_length=64, unique=True)host_user_binds = models.ManyToManyField('HostUserBind')def __str__(self):return self.nameclass HostUser(models.Model):"""存储远程主机的用户信息"""auth_type_choices = ((0, 'ssh-password'), (1, 'ssh-key'))auth_type = models.SmallIntegerField(choices=auth_type_choices)username = models.CharField(max_length=32)password = models.CharField(blank=True, null=True, max_length=128)def __str__(self):return '%s-%s-%s' % (self.get_auth_type_display(), self.username, self.password)class Meta:unique_together = ('username', 'password')class HostUserBind(models.Model):"""绑定主机和用户"""host = models.ForeignKey('Host', on_delete='')host_user = models.ForeignKey('HostUser', on_delete='')def __str__(self):return '%s-%s' % (self.host, self.host_user)class Account(models.Model):"""堡垒机账户"""user = models.OneToOneField(User, on_delete='')  # 用户Django框架提供的用户验证模块name = models.CharField(max_length=64)host_user_bind = models.ManyToManyField('HostUserBind', blank=True)host_groups = models.ManyToManyField('HostGroup', blank=True)class SessionLog(models.Model):"""记录堡垒机用户登陆堡垒机信息的日志,同时用于生成记录用户指令文件的名称"""account = models.ForeignKey('Account', on_delete='')host_user_bind = models.ForeignKey('HostUserBind', on_delete='')start_date = models.DateTimeField(auto_now_add=True)end_date = models.DateTimeField(blank=True, null=True)def __str__(self):return '%s-%s' % (self.account, self.host_user_bind)class AuditLog(models.Model):"""记录堡垒机账户操作远程服务器指令的日志"""session = models.ForeignKey('SessionLog', on_delete='')cmd = models.TextField()  # 记录操作指令data = models.DateTimeField(auto_now_add=True)  # 自动添加当前时间def __str__(self):return '%s-%s' % (self.session, self.cmd)

linux中用strace命令来实现监测用户指令


命令说明:

strace命令是一个集诊断、调试、统计与一体的工具,我们可以使用strace对应用的系统调用和信号传递的跟踪结果来对应用进行分析,以达到解决问题或者是了解应用工作过程的目的。

-c 统计每一系统调用的所执行的时间,次数和出错的次数等.
-d 输出strace关于标准错误的调试信息.
-f 跟踪由fork调用所产生的子进程.
-ff 如果提供-o filename,则所有进程的跟踪结果输出到相应的filename.pid中,pid是各进程的进程号.
-F 尝试跟踪vfork调用.在-f时,vfork不被跟踪.
-h 输出简要的帮助信息.
-i 输出系统调用的入口指针.
-q 禁止输出关于脱离的消息.
-r 打印出相对时间关于,,每一个系统调用.
-t 在输出中的每一行前加上时间信息.
-tt 在输出中的每一行前加上时间信息,微秒级.
-ttt 微秒级输出,以秒了表示时间.
-T 显示每一调用所耗的时间.
-v 输出所有的系统调用.一些调用关于环境变量,状态,输入输出等调用由于使用频繁,默认不输出.
-V 输出strace的版本信息.
-x 以十六进制形式输出非标准字符串
-xx 所有字符串以十六进制形式输出.
-a column 设置返回值的输出位置.默认 为40.
-e expr 指定一个表达式,用来控制如何跟踪.格式:[qualifier=][!]value1[,value2]...
qualifier只能是 trace,abbrev,verbose,raw,signal,read,write其中之一.value是用来限定的符号或数字.默认的 qualifier是 trace.感叹号是否定符号.例如:-eopen等价于 -e trace=open,表示只跟踪open调用.而-etrace!=open 表示跟踪除了open以外的其他调用.有两个特殊的符号 all 和 none. 注意有些shell使用!来执行历史记录里的命令,所以要使用\\.
-e trace=set 只跟踪指定的系统 调用.例如:-e trace=open,close,rean,write表示只跟踪这四个系统调用.默认的为set=all.
-e trace=file 只跟踪有关文件操作的系统调用.
-e trace=process 只跟踪有关进程控制的系统调用.
-e trace=network 跟踪与网络有关的所有系统调用.
-e strace=signal 跟踪所有与系统信号有关的 系统调用
-e trace=ipc 跟踪所有与进程通讯有关的系统调用
-e abbrev=set 设定strace输出的系统调用的结果集.-v 等与 abbrev=none.默认为abbrev=all.
-e raw=set 将指定的系统调用的参数以十六进制显示.
-e signal=set 指定跟踪的系统信号.默认为all.如 signal=!SIGIO(或者signal=!io),表示不跟踪SIGIO信号.
-e read=set 输出从指定文件中读出 的数据.例如: -e read=3,5
-e write=set 输出写入到指定文件中的数据.
-o filename 将strace的输出写入文件filename
-p pid 跟踪指定的进程pid.
-s strsize 指定输出的字符串的最大长度.默认为32.文件名一直全部输出.
-u username 以username的UID和GID执行被跟踪的命令

strace监控堡垒机用ssh连接其它服务器时输入的命令(在xsheel终端连接堡垒机后,监控堡垒机输入的指令):

将监控的命令结果输出到文件ssh_log:包含时间

通过Python subprocess连接远程主机(实时交互+免输入密码登陆)


openssh源码的linux版本下载

下载地址:http://www.openssh.com/portable.html

将源码与python代码一同上传至堡垒机所处的服务器中后,用堡垒机来连接其它服务器。

通过FTP软件将项目上传至堡垒机服务器上:

通过Python subprocess连接远程主机(实时交互+免输入密码登陆)


openssh源码的linux版本下载

下载地址:http://www.openssh.com/portable.html

将源码与python代码一同上传至堡垒机所处的服务器中后,用堡垒机来连接其它服务器。
通过FTP软件将项目上传至堡垒机服务器上。

上传sshpass-1.06.tar文件用于免密登陆的软件

  • 先编译成二进制,再安装:


    • 也可以通过apt一步安装到位:

    • 查看apt-get install所安装的软件所在位置:dpkg -L 软件名

    • 堡垒机无需再输入密码连接远程服务器:

代码实现堡垒机免密登陆远程服务器:

  • audit_shell.py文件:
#_*_coding:utf-8_*_
import os
import sysif __name__ == '__main__':# 将django添加到环境变量中os.environ.setdefault("DJANGO_SETTINGS_MODULE", "audit.settings")import djangodjango.setup()  # 手动注册django中所有的APPfrom audit_init.backend import user_interactiveobj = user_interactive.UserShell(sys.argv)    # 终端运行该脚本时传入的参数obj.start()
  • user_interactive.py文件:

from django.contrib.auth import authenticate
from audit_init import models
import subprocess
class UserShell():"""用户登陆堡垒机后的shell"""def __init__(self, sys_argv):self.sys_argv = sys_argvself.user = None    # 用来保存验证过的用户对象def auth(self):"""用Django自带认证系统进行用户验证"""count = 0while count < 3:    # 允许用户输入3次,username = input('请输入堡垒机账户名:').strip()password = input('请输入堡垒机密码:').strip()# 进行用户认证,通过则获得user对象,user = authenticate(username=username, password=password)if not user:count += 1print('用户不存在!请重新输入')else:self.user = userreturn Trueelse:print('输入次数超过3次,请稍候再试')def start(self):"""启动交互程序"""if self.auth(): # 如果登陆成功while True:host_groups = self.user.account.host_groups.all()   # 通过user表反向查询出所有主机组print(host_groups)for index, group in enumerate(host_groups):# group.host_user_binds.count() 计算一个组下有多少台主机print('%s, \t%s[%s]'%(index, group, group.host_user_binds.count()))print("%s.\t未分组机器[%s]" % (len(host_groups), self.user.account.host_user_binds.count()))try:choice = input('请选择组:').strip()if choice.isdigit():    # 如果输入的是数字choice = int(choice)host_bind_list = Noneif choice >= 0 and choice < len(host_groups):#selected_group = host_groups[choice]host_bind_list = selected_group.host_user_binds.all()elif choice == len(host_groups):#选择的未分组机器host_bind_list = self.user.account.host_user_binds.all()if host_bind_list:while True:for index, host in enumerate(host_bind_list):print('%s, \t%s'%(index, host))choice2= input('请选择主机:').strip()if choice2.isdigit():choice2 = int(choice2)  # 转换成int类型if choice2 >= 0 and choice2 < len(host_bind_list):selected_host = host_bind_list[choice2]# StrictHostKeyChecking=no表示第一次连接服务器时无需输入yes作为签名验证cmd = 'sshpass -p %s ssh %s@%s -p %s -o StrictHostKeyChecking=no ' % (selected_host.host_user.password, selected_host.host_user.username,selected_host.host.ip_addr, selected_host.host.port)subprocess.run(cmd, shell=True) # 启动shell程序窗口elif choice2 == 'b':breakexcept KeyboardInterrupt as e:pass
  • 该部分操作时引出的问题:
1、无法通过浏览器访问linux服务器上的djangoWeb项目:
解决方式:python manage.py runserver 0.0.0.0:8000   # 手动设置访问地址,同时清除浏览器中的修改化设置内容

通过修改SSH源码作唯一标识,完成主机识别


问题背景:

1台堡垒机同时远程连接多台服务器时,无法将两者的进程信息进行区分,导致获取不到对应的端口号。

解决方式:通过ssh连接时传入随机数作为唯一标识,进行进程号的获取,从而通过strace进行监控

  • 修改ssh源码(文件ssh.c中修改):

  • linux编译并安装openssh:


此处执行编译时可能报error: * zlib.h missing - please install first or check config.log *“这是由于缺少zlib-devel所致,只需安装zlib-devel即可,执行命令:yum install zlib-devel;
还有可能会包”OpenSSL headers missing - please install first or check config.log *“的错误,这是缺少openssl-devel所致,只需安装openssl-devel即可,执行命令:yum install openssl-devel

备注:当编译出错时,修改编译后,需要通过make clean清除原有的编译文件,保持环境干净.

  • 该阶段可能出现的报错:

  • 解决方式:校正时区
    timedatectl set-timezone “Asia/Shanghai”
    timedatectl set-timezone UTC #推荐使用和设置协调世界时,即UTC。
    yum -y install ntp
    systemctl status ntpd

  • 测试修改后的ssh源码是否能够通过sshpass连接服务器,并携带自定义的参数:

  • 可看到当前进程携带了自定义的参数用来进行过滤筛选:

修改python代码,筛选过滤出进程号,用来监控该进程号:

# 略……if choice2 >= 0 and choice2 < len(host_bind_list):selected_host = host_bind_list[choice2]# 生成随机字符串作为连接每台服务器后,监控进程的唯一标识import stringimport randoms = string.ascii_lowercase + string.digits  # 获得所有小写字母+数字random_tag = ''.join(random.sample(s, 10))   # 取样# StrictHostKeyChecking=no表示第一次连接服务器时无需输入yes作为签名验证cmd = 'sshpass -p %s /usr/local/openssh7/bin/ssh %s@%s -p %s -o StrictHostKeyChecking=no -Z %s'  % (selected_host.host_user.password, selected_host.host_user.username,selected_host.host.ip_addr, selected_host.host.port, random_tag)subprocess.run(cmd, shell=True) # 启动shell程序窗口# 略……
  • 运行脚本后,可获得堡垒机连接到不同服务器时对应的进程号:

编辑linux系统中的脚本文件.sh

  • 前提准备:对当前用户的sudo权限进行修改,免除运行.sh脚本时需要输入sudo密码进行安全验证的动作

注意:一定要在root用户下进行修改,否则会造成sudo无法使用,使得系统崩溃。

编写执行脚本:

#!/bin/bash

for i in $(seq 1 30);doecho $i $1  # $1表示传入脚本文件中的参数(随机字符串)# ``表示可以执行的命令,echo时,如果不添加``,则直接以字符串形式输出内容process_id = `ps -ef | grep $1 | grep -v sshpass | grep -v grep | grep -v 'session_tracker.sh' | awk '{print $2}'`# 输出得到的$process_id变量值echo "process: $process_id"# 如果$process_id不为空时if [ ! -z "$process_id" ];thenecho 'start run strace……'# 监控用户输入的指令内容,将内容输出到指定位置($0表示输入的第一个参数)strace -fp $process_id -t -o $(cd `dirname $0`; pwd)/nnnnnnnnn.log;break;fisleep 1
done;

运行脚本:

注:需先通过堡垒机的sshpass命令远程登陆一台服务器。

执行脚本命令:

会话监测日志

记录 堡垒机账户,登录主机的账户,登录的主机ip , 创建SessionLog 表,每次会话前创建一个sessionlog 记录, 把这个记录的ID传给session_tracker.sh ,因此 实现Session_tracker.sh 创建的会话日志 会以 sessionlog.id 命名 。

略……s = string.ascii_lowercase +string.digits   # 生成所有小写字符加数字
random_tag = ''.join(random.sample(s,10))   # 随机选择生成随机字符串
# 为session会话日志添加数据,获得session的Queryset对象
session_obj = models.SessionLog.objects.create(account=self.user.account,host_user_bind=selected_host)
# 格式化cmd命令,实现免密远程登陆
cmd = "sshpass -p %s /usr/local/openssh/bin/ssh %s@%s -p %s -o StrictHostKeyChecking=no -Z %s" %(selected_host.host_user.password,selected_host.host_user.username,selected_host.host.ip_addr,selected_host.host.port ,random_tag)#start strace ,and sleep 1 random_tag, session_obj.id
session_tracker_script = "/bin/sh %s %s %s " %(settings.SESSION_TRACKER_SCRIPT,random_tag,session_obj.id)# 执行脚本,循环检测是否连接到远程服务,记录用户发送给远程服务器的指令
session_tracker_obj =subprocess.Popen(session_tracker_script, shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)# 启动新的shell窗口,连接到远程服务器
ssh_channel = subprocess.run(cmd,shell=True)
print(session_tracker_obj.stdout.read(), session_tracker_obj.stderr.read())略……

注意: python3 manage.py migrate –fake # 伪装执行完成数据库记录更改

解析命令日志文件:

略……

访问堡垒机服务器时自动启动的脚本文件:


通过修改脚本文件来设置启动内容项。

将项目移动至local目录下作为生产环境的位置:

可能的报错问题:

解决方式:

无需添加sudo命令来执行其它命令:

退出程序时应该一并退出堡垒机服务器:

代码级别的控制:

总结:

此方式不适用对用户操作的方式无法有效记录!!!


paramiko模块监控用户命令输入


paramiko模块命令监控演示:

1、从github上下载该模块
2、改写interactive.py 中的代码,添加一行print(‘->’, x)代码:

3、执行该demo.py模块文件:

拼接命令:

修改paramiko源码:

  • ssh_interactive.py
import base64
from binascii import hexlify
import getpass
import os
import select
import socket
import sys
import time
import traceback
from paramiko.py3compat import input
import paramikotry:import interactive
except ImportError:from . import interactivedef manual_auth(t, username,password):t.auth_password(username, password)def ssh_session(bind_host_user, user_obj):# now connecthostname = bind_host_user.host.ip_addrport = bind_host_user.host.portusername = bind_host_user.host_user.usernamepassword = bind_host_user.host_user.passwordtry:sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.connect((hostname, port))except Exception as e:print("*** Connect failed: " + str(e))traceback.print_exc()sys.exit(1)try:t = paramiko.Transport(sock)try:t.start_client()except paramiko.SSHException:print("*** SSH negotiation failed.")sys.exit(1)try:keys = paramiko.util.load_host_keys(os.path.expanduser("~/.ssh/known_hosts"))except IOError:try:keys = paramiko.util.load_host_keys(os.path.expanduser("~/ssh/known_hosts"))except IOError:print("*** Unable to open host keys file")keys = {}# check server's host key -- this is important.key = t.get_remote_server_key()if hostname not in keys:print("*** WARNING: Unknown host key!")elif key.get_name() not in keys[hostname]:print("*** WARNING: Unknown host key!")elif keys[hostname][key.get_name()] != key:print("*** WARNING: Host key has changed!!!")sys.exit(1)else:print("*** Host key OK.")if not t.is_authenticated():manual_auth(t, username, password)if not t.is_authenticated():print("*** Authentication failed. :(")t.close()sys.exit(1)chan = t.open_session()chan.get_pty()chan.invoke_shell()print("*** Here we go!\n")from audit_init import models# 创建会话日志session_obj = models.SessionLog.objects.create(account=user_obj.account,host_user_bind=bind_host_user,)interactive.interactive_shell(chan, session_obj)    # 启动shell终端chan.close()t.close()except Exception as e:print("*** Caught exception: " + str(e.__class__) + ": " + str(e))traceback.print_exc()try:t.close()except:passsys.exit(1)
  • interactive.py
import socket
import sys
from paramiko.py3compat import u
from audit_init import models
# windows does not have termios...
try:import termiosimport ttyhas_termios = True
except ImportError:has_termios = Falsedef interactive_shell(chan, session_obj):if has_termios:posix_shell(chan,session_obj)else:windows_shell(chan)def posix_shell(chan, session_obj):import selectoldtty = termios.tcgetattr(sys.stdin)try:tty.setraw(sys.stdin.fileno())tty.setcbreak(sys.stdin.fileno())chan.settimeout(0.0)flag = Falsecmd = ''while True:r, w, e = select.select([chan, sys.stdin], [], [])if chan in r:try:x = u(chan.recv(1024))if len(x) == 0: # 未输入指令状态/ctrl+c退出shell终端时sys.stdout.write("\r\n*** EOF\r\n")breakif flag:cmd+=xflag=Falsesys.stdout.write(x)sys.stdout.flush()except socket.timeout:passif sys.stdin in r:x = sys.stdin.read(1)if len(x) == 0:breakif x == '\r':models.AuditLog.objects.create(session=session_obj, cmd=cmd)    # 将用户输入的命令写入数据表cmd=''  # 清空elif x == '\t':flag = Trueelse:cmd += xchan.send(x)finally:termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)# thanks to Mike Looijmans for this code
def windows_shell(chan):import threadingsys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.\r\n\r\n")def writeall(sock):while True:data = sock.recv(256)if not data:sys.stdout.write("\r\n*** EOF ***\r\n\r\n")sys.stdout.flush()breaksys.stdout.write(data)sys.stdout.flush()writer = threading.Thread(target=writeall, args=(chan,))writer.start()try:while True:d = sys.stdin.read(1)if not d:breakchan.send(d)except EOFError:# user hit ^Z or F6pass
  • user_interactive.py
略……if choice2 >= 0 and choice2 < len(host_bind_list):selected_host = host_bind_list[choice2]# 生成随机字符串作为连接每台服务器后,监控进程的唯一标识from audit_init.backend.ssh_interactive import ssh_sessionssh_session(selected_host, self.user)
略……
  • 在admin组件中展示特定字段:
from django.contrib import admin
from audit_init import models
# Register your models here.
class AuditLogAdmin(admin.ModelAdmin):# 在admin后台展示的字段list_display = ['session', 'cmd', 'date']# 在admin后台搜索的条件字段list_filter = ['date', 'session']class SessionLogAdmin(admin.ModelAdmin):list_display = ['id','account','host_user_bind','start_date','end_date']list_filter = ['start_date','account']admin.site.register(models.IDC)
admin.site.register(models.HostGroup)
admin.site.register(models.Host)
admin.site.register(models.HostUser)
admin.site.register(models.HostUserBind)
admin.site.register(models.Account)
admin.site.register(models.SessionLog, SessionLogAdmin)
admin.site.register(models.AuditLog, AuditLogAdmin)

将前端模版嵌入堡垒机


static路径问题:

settings配置文件:


# 静态资源位置加载,可兼容多个路径
STATIC_URL = '/static/' # 静态资源入口
STATICFILES_DIRS = (os.path.join(BASE_DIR,'statics'), # 允许多个静态资源路径
)

母版改造:

  • base.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title> audit | AI堡垒机</title><link href="http://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700&amp;subset=latin" rel="stylesheet"><link href="/static/css/bootstrap.min.css" rel="stylesheet"><link href="/static/css/nifty.min.css" rel="stylesheet"><link href="/static/premium/icon-sets/icons/line-icons/premium-line-icons.min.css" rel="stylesheet"><link href="/static/premium/icon-sets/icons/solid-icons/premium-solid-icons.min.css" rel="stylesheet"><link href="/static/css/pace.min.css" rel="stylesheet"><script src="/static/js/pace.min.js"></script><script src="/static/js/jquery.min.js"></script><script src="/static/js/bootstrap.min.js"></script><script src="/static/js/nifty.min.js"></script>
</head><body>
{% block body %}{% endblock %}</body>
</html>
  • index.html
{% extends 'base.html' %}
{% block body %}
略……{% endblock%}

登陆与退出


urls:

 url(r'^$', views.index),url(r'^login.html/$', views.acc_login),url(r'^login_out.html/$', views.acc_login_out),

views:

def index(request):return render(request, 'index.html',)def acc_login(request):"""登录:param request::return:"""if request.method == 'POST':# 登陆成功print('11')username = request.POST.get('username')password = request.POST.get('password')# 使用django自带的用户认证user = authenticate(username=username, password=password)if user:# 将用户封装到request中的session中响应给浏览器login(request, user)return redirect('/')# 登陆失败return render(request, 'login.html',)def acc_login_out(request):"""退出登陆:param request::return:"""logout(request)return redirect('/login.html/')

login.html

{% extends 'base.html' %}
{% block body %}<div id="container" class="cls-container"><!-- LOGIN FORM --><!--===================================================--><div class="cls-content"><div class="cls-content-sm panel"><div class="panel-body"><div class="mar-ver pad-btm"><h1 class="h3">用户登陆</h1></div><form action="/login.html/" method="POST" ><div class="form-group"><input type="text" class="form-control" placeholder="Username" autofocus name="username"></div><div class="form-group"><input type="password" class="form-control" placeholder="Password" name="password"></div><div class="checkbox pad-btm text-left"><input id="demo-form-checkbox" class="magic-checkbox" type="checkbox"><label for="demo-form-checkbox">Remember me</label></div><button class="btn btn-primary btn-lg btn-block" type="submit">登陆</button></form></div><div class="pad-all"><a href="#" class="btn-link mar-rgt">忘记密码 ?</a></div></div></div></div>{% endblock %}

index.html:

从request中获得session中的用户名#}
<div class="username hidden-xs">{{ request.user }}</div><div class="pad-all text-right">
<a href="/login_out.html/" class="btn btn-primary"><i class="pli-unlock icon-fw"></i>退出
</a>

控制导航栏高亮显示


url:

 url(r'^host_list/$', views.host_list, name='host_list'),

views:


def host_list(request):return render(request, 'host_list.html',)

index.html:

省略页面改造步骤

略……<script>$(function () {{#第一种显示高亮的函数#}/*$("#mainnav-menu a").click(function () {$(this).parent().addClass('active-link')});*/{##第一种显示高亮的函数:通过属性选择器获得前当URL对应的a标签,从而控制a标签的高亮#}$("#mainnav-menu a[href='{{ request.path }}']").parent().addClass('active-link');});</script>略……

Django自带用户认证


使用认证装饰器@login_required

# views:from django.contrib.auth.decorators import login_required
# 登陆认证过滤装饰器
@login_required
def index(request):return render(request, 'index.html',)def acc_login(request):"""登录:param request::return:"""if request.method == 'POST':# 登陆成功print('11')username = request.POST.get('username')password = request.POST.get('password')# 使用django自带的用户认证user = authenticate(username=username, password=password)if user:# 将用户封装到request中的session中响应给浏览器login(request, user)# 从url中获取next中的地址,可以重新载入登陆前的url地址return redirect(request.GET.get('next') or '/') # 登陆失败return render(request, 'login.html',)

settings配置文件:

LOGIN_URL = '/login/'   # 用户认证装饰器默认跳转的页面url为 /login/

主机列表开发


html改造:

{% extends 'index.html' %}{% block page_title %}主机列表
{% endblock %}{% block current_location %}主机列表
{% endblock %}{% block page_content %}<div class="panel col-lg-3"><div class="panel-heading"><h3 class="panel-title"><trans oldtip="Panel with header" newtip="带标头的面板" style="">主机组</trans></h3></div><div class="panel-body"><p><div class="panel-body"><!--List Group with Badges--><!--===================================================--><ul class="list-group">{% for group in request.user.account.host_groups.all %}<li class="list-group-item" onclick="GetHostlist({{ group.id }}, this)"><span
                                class="badge badge-primary">{{ group.host_user_binds.count }}</span>{{ group.name }}</li>{% endfor %}<li class="list-group-item" onclick="GetHostlist(-1, this)"><span
                            class="badge badge-primary">{{ request.user.account.host_user_binds.count }}</span>未分组主机</li></ul></div></p></div></div><div class="panel col-lg-9"><div class="panel-heading"><h3 class="panel-title"><trans oldtip="Panel with header" newtip="带标头的面板" style="">主机列表</trans></h3></div><!-- Striped Table --><!--===================================================--><div class="panel-body"><div class="table-responsive"><table class="table table-striped"><thead><tr><th>Hostname</th><th>IP</th><th>IDC</th><th>Port</th><th>Username</th><th>Login</th><th>Token</th></tr></thead><tbody id="hostlist"></tbody></table></div></div><!--===================================================--><!-- End Striped Table --></div>
{% endblock %}

js部分:

<script>function GetHostlist(gid, self) {$.get("{% url 'get_host_list' %}", {'gid': gid}, function (callback) {var data = JSON.parse(callback);console.log(data)var trs = ''$.each(data, function (index, i) {var tr = "<tr><td>" + i.host__hostname + "</td><td>" + i.host__ip_addr + "</td><td>" + i.host__idc__name+ "</td><td>" + i.host__port + "</td><td>" + i.host_user__username + "</td><td>"trs += tr$("#hostlist").html(trs);})});$(self).addClass("active").siblings().removeClass('active');}</script>

urls:

    url(r'^api/hostlist/$', views.get_host_list, name='get_host_list'),

views:

@login_required
def get_host_list(request):gid = request.GET.get('gid')if gid:if gid == '-1': # 未分组host_list = request.user.account.host_user_binds.all()else:group_obj = request.user.account.host_groups.get(id = gid)host_list = group_obj.host_user_binds.all()import jsondata = json.dumps(list(host_list.values('id', 'host__hostname', 'host__ip_addr', 'host__port','host_user__username')))print(data)return HttpResponse(data)

shellinabox使用


Shellinabox 是一个利用 Ajax 技术构建的基于 Web 的远程Terminal 模拟器,也就是说安装了该软件之后,不需要开启 ssh服务,通过 Web 网页就可以对远程主机进行维护操作了,出于安全考虑, Shellinabox 默认强制使用了https协议,这是个挺有趣的技术,因而就在rhel6上面折腾了下,下面记录了主要的操作步骤:

安装

下载地址:https://github.com/shellinabox/shellinabox安装准备:(提前装好C环境)yum -y install gcc
netstate 工具安装: yum -y install net-tools一:编译安装Shellinabox
[root@rhel6 ~]# cd /usr/local/src/tarbag/
[root@rhel6 tarbag]# wgethttp://shellinabox.googlecode.com/files/shellinabox-2.10.tar.gz
[root@rhel6 tarbag]# tar -zxvf shellinabox-2.10.tar.gz -C ../software/
[root@rhel6 tarbag]# cd ../software/shellinabox-2.10/
[root@rhel6 shellinabox-2.10]# ./configure --prefix=/usr/local/shellinabox
[root@rhel6 shellinabox-2.10]# make && make install二:试启动Shellinabox,可以加上--help参数查看启动选项,这里可以看到默认Shellinabox采用https方式启动
[root@rhel6 ~]# /usr/local/shellinabox/bin/shellinaboxd
Cannot read valid certificate from "certificate.pem". Check file permissions and file format.[root@rhel6 ~]# /usr/local/shellinabox/bin/shellinaboxd -b -t  //-b选项代表在后台启动,-t选项表示不使用https方式启动,默认以nobody用户身份,监听TCP4200端口[root@rhel6 ~]# netstat -ntpl |grep shell  # 查看端口号
tcp        0      0 0.0.0.0:4200                0.0.0.0:*                   LISTEN      16823/shellinaboxd

使用:

1、关闭防火墙:
systemctl   stop firewalled.service
systemctl   disable firewalled.service2、浏览器访问:ip+端口号

后台生成token在前端页面


html

<script>function GetToken(self, bind_host_id) {   # 生成token的函数$.post("{% url 'get_token' %}", {'bind_host_id':bind_host_id, 'csrfmiddlewaretoken':'{{csrf_token}}'}, # 通过ajax使用反向url生成的形式发送post请求function (callback) {console.log(callback)var data = JSON.parse(callback)$(self).parent().next().text(data.token)})}function GetHostlist(gid, self) {$.get("{% url 'get_host_list' %}", {'gid': gid}, function (callback) { # 通过ajax使用反向url生成的形式发送get请求var data = JSON.parse(callback);console.log(data)var trs = ''var tr= ''$.each(data, function (index, i) {tr = "<tr><td>" + i.host__hostname + "</td><td>" + i.host__ip_addr + "</td><td>" + i.host__idc__name+ "</td><td>" + i.host__port + "</td><td>" + i.host_user__username +"</td><td><a class='btn btn-info' onclick=GetToken(this,'"+i.id+"')>Token</a>Login</td><td><td></tr>" # 调用GetToken函数来生成tokentrs += tr})$("#hostlist").html(trs);});$(self).addClass("active").siblings().removeClass('active');}</script>

url:

 url(r'^api/token/$', views.get_token, name='get_token'),

views:


@login_required
def get_token(request):"""生成token并返回"""from django.utils import timezonebind_host_id = request.POST.get('bind_host_id')# 这里需要使用timezone.now来获取本地当前时间,而非datatime().datatime(),因为关系到需要自适应时区的问题,否则报错time_obj =  timezone.now() - datetime.timedelta(seconds=300) # 获得5分钟前的时间exist_token_objs = models.Token.objects.filter(account_id=request.user.account.id,host_user_bind_id=bind_host_id,date__gt=time_obj )if exist_token_objs: # has token alreadytoken_data ={'token':exist_token_objs[0].val}else:token_val = ''.join(random.sample(string.ascii_lowercase+string.digits,8))token_obj = models.Token.objects.create(host_user_bind_id = bind_host_id,account = request.user.account,val = token_val)token_data = {'token':token_val}return HttpResponse(json.dumps(token_data))

model

class Token(models.Model):host_user_bind = models.ForeignKey("HostUserBind", on_delete='')val = models.CharField(max_length=128,unique=True)account = models.ForeignKey("Account", on_delete='')expire = models.IntegerField("超时时间(s)",default=300)date = models.DateTimeField(auto_now_add=True)def __str__(self):return "%s-%s" %(self.host_user_bind,self.val)

批量命令页面开发(全选功能+数量统计)


html

{% extends 'index.html' %}{% block page_title %}主机列表
{% endblock %}{% block current_location %}主机列表
{% endblock %}{% block page_content %}{% csrf_token %}<div id="page-content"><div class="panel col-lg-3"><div class="panel-heading"><h3 class="panel-title"><trans oldtip="Panel with header" newtip="带标头的面板" style="">主机组 <label id="selected_hosts"></label></trans></h3></div><div class="panel-body"><p><div class="panel-body"><ul class="list-group " id="host_groups">{% for group in request.user.account.host_groups.all %}<li class="list-group-item "><span class="badge badge-primary">{{ group.host_user_binds.count }}</span><input type="checkbox" onclick="checkAll(this)"># 循环主机名<a onclick="DisplayHostList(this)">{{ group.name }}</a><ul class="hide">{% for bind_host in group.host_user_binds.all %}# 循环主机ip地址<input type="checkbox" value="{{ bind_host.id }}"onclick="showCheckHostCount()"><li>{{ bind_host.host.ip_addr }}</li>{% endfor %}</ul></li>{% endfor %}<li class="list-group-item" onclick="DisplayHostList(this)"><input type="checkbox" onclick="checkAll(this)"><span class="badge badge-primary">{{ request.user.account.host_user_binds.count }}</span>未分组主机<ul class="hide">{% for bind_host in request.user.account.host_user_binds.all %}<li>{{ bind_host.host.ip_addr }}</li>{% endfor %}</ul></li></ul></div></p></div></div><div class="panel col-lg-9"><div class="panel-heading"><h3 class="panel-title"><trans oldtip="Panel with header" newtip="带标头的面板" style="">命令</trans></h3></div><div class="panel-body">ddd</div></div><div class="panel col-lg-9"><div class="panel-heading"><h3 class="panel-title"><trans oldtip="Panel with header" newtip="带标头的面板" style="">命令</trans></h3></div><div class="panel-body">ddd</div></div></div><script># 控制隐藏和显示切换function DisplayHostList(self) {$(self).next().toggleClass('hide')}function checkAll(self) {{#全选功能 复选框#}$(self).parent().find('ul :checkbox').prop('checked', $(self).prop('checked'))showCheckHostCount()}{#统计选中的数量#}function showCheckHostCount() {var selected_host_count = $('#host_groups ul').find(':checked').length;$('#selected_hosts').text(selected_host_count);return selected_host_count}</script>{% endblock %}

(项目)审计系统(堡垒机)相关推荐

  1. 审计系统---堡垒机项目之表结构设计

    [更多参考] https://github.com/317828332/CrazyEye 缺点: 界面丑陋, 只能追踪命令,命令的解析还不是很完善,不能明了的展示 对于文件的上传[rz],下载[sz] ...

  2. 审计系统---堡垒机python下ssh的使用

    堡垒机python下ssh的使用 [堡垒机更多参考]http://www.cnblogs.com/alex3714/articles/5286889.html [paramiko的Demo实例]htt ...

  3. 常见安全设备总结(IDS、IPS、上网行为管理、网闸、漏扫、日志审计、数据库审计、堡垒机等)

    常见安全设备总结(IDS.IPS.上网行为管理.网闸.漏扫.日志审计.数据库审计.堡垒机等) 一.网络结构 二.防火墙.IPS 1.防火墙 2.IPS 三.上网行为管理.网闸 1.上网行为管理 2.网 ...

  4. jumpserver堡垒机 (资源)

    23.5 jumpserver介绍 • 官网www.jumpserver.org • 跳板机概述: 跳板机就是一台服务器,开发戒运维人员在维护过程中首先要统一登录到这台服务器,然后再登录到目标 设备迚 ...

  5. jumpserver堡垒机 (资源

    23.5 jumpserver介绍 • 官网www.jumpserver.org • 跳板机概述: 跳板机就是一台服务器,开发戒运维人员在维护过程中首先要统一登录到这台服务器,然后再登录到目标 设备迚 ...

  6. python画城堡_Python 13 简单项目-堡垒机

    本节内容 项目实战:运维堡垒机开发 前景介绍 到目前为止,很多公司对堡垒机依然不太感冒,其实是没有充分认识到堡垒机在IT管理中的重要作用的,很多人觉得,堡垒机就是跳板机,其实这个认识是不全面的,跳板功 ...

  7. python系统工作原理_Python之路——堡垒机原理及其简单实现

    1 堡垒机基本概述 其从功能上讲,它综合了核心系统运维和安全审计管控两大主干功能,从技术实现上讲,通过切断终端计算机对网络和服务器资源的直接访问,而采用协议代理的方式,接管了终端计算机对网络和服务器的 ...

  8. 主机管理+堡垒机系统开发

    本节内容 需求讨论 构架设计 表结构设计 程序开发 1.需求讨论 实现对用户的权限管理,能访问哪些机器,在被访问的机器上有哪些权限 实现可以通过web页面对指定主机列表 进行 批量发布命令.文件 实现 ...

  9. Jumpserver堡垒机部署(完整过程)

    文章目录 一.跳板机.堡垒机 1.跳板机 2.堡垒机(为什么需要堡垒机) 2.1堡垒机的作用 2.2堡垒机的运维思想 2.3堡垒机的核心功能 2.4堡垒机应用的场景 2.5企业角度看堡垒机 2.6管理 ...

  10. php开源堡垒机,开源堡垒机在开发环境中的使用方案-麒麟开源堡垒机

    一.部署说明: 开发环境主要使用开发人员的PC或笔记本终端进行开发,开发完成后,将代码交付相应的负责人,负责人编译测试后,将代码上传到CVS备份,将程序上传到生产环境使用.这种管理模式主要存在如下问题 ...

最新文章

  1. Average Score39届亚洲赛牡丹江站A题
  2. iphone4/iphone5/iphone6/iphone6Plus响应式布局适配代码
  3. android 广播观察者,BroadcastReceiver和Activity之间的通信 – android
  4. Java线程详解(5)-线程的同步与锁
  5. 多进程实现生产者消费者
  6. APP技巧:电脑登录微信,要删除这5个文件!否则别人能查看聊天记录
  7. cuda编程性能 分析工具 nvprof的使用
  8. mysql 导入 sqlite_Mysql 数据导入SQlite
  9. php windowcrlf和unix,文件格式unix与dos转换,CRLF与LF的区别查看
  10. SQL Server 2008 R2 中英文 开发版/企业版/标准版 链接地址
  11. 计算各个城市实际地区生产总值(附各个城市实际GDP)
  12. 本地通过secureCRT连接虚拟机中CentOS7
  13. 关于城市旅游的HTML网页设计 HTML+CSS+JS学生旅游网页设计与实现
  14. 2021年清北等重点高校都有哪些冬令营?最全汇总看这里!
  15. 信号的频谱分析实验matlab,实验三 用FFT对信号进行频谱分析及MATLAB程序
  16. java工程设计选题管理系统_基于javaee的毕设选题测试及管理系统的设计与实现 毕设.doc...
  17. [IOI2018]-day1 简要题解
  18. python中绘制柱形图、饼形图等
  19. java linest_Java运行环境搭建的图文教程
  20. HCIE 面试资料-STP/RSTP/MSTP

热门文章

  1. NVIDIA Jetson TX2 安装 Astra相机的ros驱动源码 错误总结
  2. 知识点四 图论:dijkstra (HDU 2544 +HDU 1874)
  3. Android WebView播放flash(判断是否安装flash插件)
  4. 【Java学习笔记】2023_03_10Java基础
  5. ANSVC无功补偿装置在南京某高等院校中的应用-安科瑞华楠
  6. SA-NET: Shuffle attention for DCNN 论文学习
  7. 用python写银行叫号系统(这个是学校的实训题目,真的没什么技术含量)
  8. 哪里有云南ip服务器,云南那些服务商可以提供云南本地ip服务器
  9. 2023在家赚钱怎么做,有什么适合在家做的副业项目
  10. 基于时域表示的序列数据分类方法(一)——基于距离度量的序列数据分类方法