一:堡垒机需求分析

注意:

虽然我们在中间使用防火墙服务器对流量进行拦截和转发也可以起到过滤作用,但是我们无法去获取到完整,正确的操作记录。因为无论是客户端还是服务器端(管理员可能会去修改记录,而且可能会出现一个账号多人用,无法知道是谁操作了这台服务器)我们都无法完全控制。所以,我们可以使用中间件,替客户去执行命令,并且记录操作记录到堡垒机的数据库中

history        可以查看操作记录
history -c 清空记录

注意:

由于所有密码都存在堡垒机中,所以我们要保证其环境的安全性,,最好还有一个备份堡垒机

补充:远程登录服务器的方法

(1)账号+密码

(2)A将公钥放在对方B服务器,A就可以直接登录B服务器

公钥登录方法测试:

1.在本机生成密匙,公钥(后缀.pub)和私钥

.将公钥远程拷贝或发生到对方服务器

.要想公钥在对方服务器生效,需要将发送过来的公钥放在用户家目录下的.ssh隐藏目录,若是不存在该目录,需要先生成该目录,然后再加文件拷贝进入

.将公钥在对方服务器中放入用户的家目录下的.ssh目录,并修改名为authorized_keys,才能生效

.测试远程ssh登录

二:数据表结构设计

from django.db import models
from django.contrib.auth.models import (
BaseUserManager, AbstractBaseUser,PermissionsMixin
)
#主机表:含有主机名,ip地址,端口,外联IDC机房
class Host(models.Model):
name = models.CharField(max_length=,unique=True)
ip_addr = models.GenericIPAddressField(unique=True)
port = models.SmallIntegerField(default=)
idc = models.ForeignKey("IDC") def __str__(self):
return self.name
#用户表,用于登录上面主机,若是只有用户密码,那么是一对多,但是包含公钥登录,所以结构变为多对多,第三张表我们需要自己去创建
class RemoteUser(models.Model):
'''远程登录用户:1.私钥,秘钥,2.用户密码'''
auth_type_choices = (
(,"ssh-password"),
(,"ssh-key"),
) auth_type = models.SmallIntegerField(choices=auth_type_choices) username = models.CharField(max_length=) password = models.CharField(max_length=,blank=True,null=True) class Meta:
unique_together = ("auth_type","username","password") def __str__(self):
return "%s:%s"%(self.username,self.password)
#主机对用户的多对多,第三张表,用于和堡垒机的账号表多对多关联。也会和主机组多对多关联
class HostToRemoteUser(models.Model):
host = models.ForeignKey("Host")
remote_user = models.ForeignKey("RemoteUser") class Meta:
unique_together = ("host","remote_user") def __str__(self):
return "%s %s"%(self.host,self.remote_user)
#主机组,类似于角色分组。对各个主机进行分组管理
class HostGroup(models.Model):
"""存储主机组"""
name = models.CharField(max_length=,unique=True)
#hosts = models.ManyToManyField("Host")
host_to_remote_users = models.ManyToManyField("HostToRemoteUser") def __str__(self):
return self.name
#堡垒机的账号管理,包括了权限管理
class UserProfileManager(BaseUserManager):
def create_user(self, email, name, password=None):
"""
Creates and saves a User with the given email, date of
birth and password.
"""
if not email:
raise ValueError('Users must have an email address') user = self.model(
email=self.normalize_email(email),
name=name,
) user.set_password(password)
user.save(using=self._db)
return user def create_superuser(self, email, name, password):
"""
Creates and saves a superuser with the given email, date of
birth and password.
"""
user = self.create_user(
email,
password=password,
name=name,
)
user.is_superuser = True
user.save(using=self._db)
return user #账号管理,关联主机分为单个主机用户表HostToRemoteUser(主要是对某些用户的分配主机只有一台或者过少,不需要分配主机用户组),和主机用户组HostGroup
class UserProfile(AbstractBaseUser,PermissionsMixin):
"""堡垒机账号"""
email = models.EmailField(
verbose_name='email address',
max_length=,
unique=True, )
name = models.CharField(max_length=, verbose_name="姓名")
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=True)
objects = UserProfileManager() host_to_remote_users = models.ManyToManyField("HostToRemoteUser",blank=True,null=True)
host_groups = models.ManyToManyField("HostGroup",blank=True,null=True) USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name'] def get_full_name(self):
# The user is identified by their email address
return self.email def get_short_name(self):
# The user is identified by their email address
return self.email def __str__(self): # __unicode__ on Python
return self.email

完善操作记录表

class AuditLog(models.Model):
'''日志记录'''
user = models.ForeignKey("UserProfile",verbose_name="堡垒机账号",blank=True,null=True)
host_to_remote_user = models.ForeignKey("HostToRemoteUser",verbose_name="主机账号",blank=True,null=True)
log_type_choices = (
(,"login"),
(,"cmd"),
(,"logout")
)
log_type = models.SmallIntegerField(choices=log_type_choices,default=)
content = models.CharField(max_length=,blank=True,null=True)
datetime = models.DateTimeField(auto_now_add=True,blank=True,null=True) def __str__(self):
return "%s %s %s"%(self.user,self.host_to_remote_user,self.content)

三:业务调用

(1)backend_manage.py:配置Django环境,便于使用

# coding:utf8
# __author: Administrator
# date: //
# /usr/bin/env python
import sys,os if __name__ == "__main__":
#Django环境配置
os.environ.setdefault(
"DJANGO_SETTINGS_MODULE", "crazyeye.settings")
import django
django.setup()
from backend import main  #注意项目中的模块导入,需要在Django环境配置之后,不然不会调用到Django环境而报错
interactive_obj = main.ArgvHandler(sys.argv)

进入backend目录

(2)main.py:对用户参数进行解析

# coding:utf8
# __author: Administrator
# date: //
# /usr/bin/env python
from backend import ssh_interactive class ArgvHandler(object):
def __init__(self,sys_argv):
self.sys_argv = sys_argv
self.call() def help_msg(self,error=''):
msg = '''
%s
run 启动用户交互程序
'''
exit(msg%error) def call(self):
'''根据用户参数,调用对应方法'''
if len(self.sys_argv) == :
self.help_msg()
if hasattr(self,self.sys_argv[]):
getattr(self,self.sys_argv[])(self.sys_argv[:])
else:
self.help_msg(error="调用方法[%s]不存在"%self.sys_argv[]) def run(self,*args,**kwargs):
ssh_interactive.SshHandler(self)  #开始交互

(3)ssh_interactive.py:启动堡垒机交互脚本

# coding:utf8
# __author: Administrator
# date: //
# /usr/bin/env python
from django.contrib.auth import authenticate
from backend import paramiko_ssh
from repository import models
import getpass class SshHandler(object):
'''
启动堡垒机交互脚本
'''
def __init__(self,argv_handler_instance):
self.argv_handler_instance = argv_handler_instance
self.interactive() def auth(self):
'''登录堡垒机账号'''
count =
while count < :
username = input("堡垒机账号>>>:").strip()
password = getpass.getpass("Password>>>:")
user = authenticate(username=username,password=password)
if user:
self.user = user
return True
count +=
return False def show_host_group(self):
'''显示所有可以操作的主机组和未分组'''
msg = '''
HostGroup_List:
%s
'''
Hgroup_list = self.user.host_groups.all()
g_list = []
for index,group in enumerate(Hgroup_list):
g_list.append("[%s]\t%s(%s台)"%(index,group.name,group.host_to_remote_users.count())) g_list.append('[z]\t未分组主机(%s台)'%(self.user.host_to_remote_users.count())) while True:
print(msg % ('\n\t'.join(g_list)))
choice = input("请选择主机组>>>:").strip()
if choice.isdigit():
choice = int(choice)
try:
selected_group = Hgroup_list[choice]
except IndexError:
continue
self.show_host_list(selected_group)
elif choice == "z":
self.show_host_list(None,False)
elif choice == "exit":
exit() def show_host_list(self,selected_group,group_type=True):
'''显示主机组下面的主机'''
if group_type:
H2R_ulist = selected_group.host_to_remote_users.all()
else:
H2R_ulist = self.user.host_to_remote_users.all() msg = '''
HostToRemoteUser_List:
%s
'''
h_list = []
for index,h2r in enumerate(H2R_ulist):
h_list.append("[%s]\t%s"%(index,h2r))
while True:
print(msg % ('\n\t'.join(h_list)))
choice = input("请选择操作主机>>>:").strip()
if choice.isdigit():
choice = int(choice)
try:
selected_host = H2R_ulist[choice]
except IndexError:
continue
return self.link(selected_host)
elif choice == "b":
return True
elif choice == "exit":
exit() def link(self,selected_host):
'''连接主机'''
self.models = models
paramiko_ssh.ssh_connect(self, selected_host) def interactive(self):
'''启动交互脚本'''
if self.auth():
print("登录成功")
self.show_host_group()
else:
print("登录失败")

(4)paramiko_ssh.py:开始连接远程主机,并将登陆,退出信息记录到数据库中

#!/usr/bin/env python

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 paramiko try:
import interactive
except ImportError:
from . import interactive def agent_auth(transport, username):
"""
Attempt to authenticate to the given transport using any of the private
keys available from an SSH agent.
""" agent = paramiko.Agent()
agent_keys = agent.get_keys()
if len(agent_keys) == :
return for key in agent_keys:
print("Trying ssh-agent key %s" % hexlify(key.get_fingerprint()))
try:
transport.auth_publickey(username, key)
print("... success!")
return
except paramiko.SSHException:
print("... nope.") def manual_auth(username, hostname,password,t):
default_auth = "p"
# auth = input(
# "Auth by (p)assword, (r)sa key, or (d)ss key? [%s] " % default_auth
# )
auth = default_auth if len(auth) == :
auth = default_auth if auth == "r":
default_path = os.path.join(os.environ["HOME"], ".ssh", "id_rsa")
path = input("RSA key [%s]: " % default_path)
if len(path) == :
path = default_path
try:
key = paramiko.RSAKey.from_private_key_file(path)
except paramiko.PasswordRequiredException:
password = getpass.getpass("RSA key password: ")
key = paramiko.RSAKey.from_private_key_file(path, password)
t.auth_publickey(username, key)
elif auth == "d":
default_path = os.path.join(os.environ["HOME"], ".ssh", "id_dsa")
path = input("DSS key [%s]: " % default_path)
if len(path) == :
path = default_path
try:
key = paramiko.DSSKey.from_private_key_file(path)
except paramiko.PasswordRequiredException:
password = getpass.getpass("DSS key password: ")
key = paramiko.DSSKey.from_private_key_file(path, password)
t.auth_publickey(username, key)
else:
#pw = getpass.getpass("Password for %s@%s: " % (username, hostname))
'''动态密码'''
t.auth_password(username, password) def ssh_connect(ssh_interactive_handler,selected_host):  #获取ssh_interactive_handler句柄,含有models等信息,selected_host是用户选择的主机账号
'''动态获取hostname,port,name,password,ssh_key'''
hostname = selected_host.host.ip_addr
port = selected_host.host.port
username = selected_host.remote_user.username
password = selected_host.remote_user.password # now connect
try:
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() try:
t = paramiko.Transport(sock)
try:
t.start_client()
except paramiko.SSHException:
print("*** SSH negotiation failed.")
sys.exit() 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()
else:
print("*** Host key OK.") if not t.is_authenticated():
manual_auth(username, hostname,password,t)
if not t.is_authenticated():
print("*** Authentication failed. :(")
t.close()
sys.exit() chan = t.open_session()
chan.get_pty()
chan.invoke_shell()
chan.ssh_handler = ssh_interactive_handler
chan.selected_host = selected_host
print("*** Here we go!\n")
     #登陆
ssh_interactive_handler.models.AuditLog.objects.create(
user=ssh_interactive_handler.user,
host_to_remote_user=selected_host,
log_type=0,
content="*** Login ***"
)
interactive.interactive_shell(chan)  #启动会话
chan.close()
t.close()

     #退出
ssh_interactive_handler.models.AuditLog.objects.create(
user=chan.ssh_handler.user,
host_to_remote_user=chan.selected_host,
log_type=2,
content="*** Logout ***"
) except Exception as e:
print("*** Caught exception: " + str(e.__class__) + ": " + str(e))
traceback.print_exc()
try:
t.close()
except:
pass
sys.exit()

(5)interactive.py:启动会话,和主机进行交互记录命令到数据库

# Copyright (C) -  Robey Pointer <robeypointer@gmail.com>
#
# This file is part of paramiko.
#
# Paramiko is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free
# Software Foundation; either version 2.1 of the License, or (at your option)
# any later version.
#
# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
# Temple Place, Suite , Boston, MA - USA. import socket
import sys
from paramiko.py3compat import u
import time # windows does not have termios...
try:
import termios
import tty has_termios = True
except ImportError:
has_termios = False def interactive_shell(chan):
if has_termios:
posix_shell(chan)
else:
windows_shell(chan) def posix_shell(chan):  #linux使用select框架循环
import select oldtty = termios.tcgetattr(sys.stdin)
try:
tty.setraw(sys.stdin.fileno())
tty.setcbreak(sys.stdin.fileno())
chan.settimeout(0.0) cmd = []
while True:
r, w, e = select.select([chan, sys.stdin], [], [])
if chan in r:
try:
x = u(chan.recv())
if len(x) == :
sys.stdout.write("\r\n*** EOF\r\n")
break
sys.stdout.write(x)
sys.stdout.flush()
except socket.timeout:
pass
if sys.stdin in r:
x = sys.stdin.read()
if x == "\r":
print('input>',"".join(cmd))
chan.ssh_handler.models.AuditLog.objects.create(
user=chan.ssh_handler.user,
host_to_remote_user=chan.selected_host,
log_type=,
content=''.join(cmd)
)
cmd.clear()
if len(x) == :
break
cmd.append(x)
chan.send(x)
except (EOFError,OSError):
pass
finally:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty) # thanks to Mike Looijmans for this code
def windows_shell(chan):  #windows使用多线程和socket方式交互
import threading sys.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()
if not data:
sys.stdout.write("\r\n*** EOF ***\r\n\r\n")
sys.stdout.flush()
break
sys.stdout.write(data.decode())
sys.stdout.flush() writer = threading.Thread(target=writeall, args=(chan,))
writer.start() try:
cmd = []
while True:
d = sys.stdin.read()
if d == "\n":
chan.ssh_handler.models.AuditLog.objects.create(
user=chan.ssh_handler.user,
host_to_remote_user=chan.selected_host,
log_type=,
content=''.join(cmd)
)
cmd.clear()
cmd.append(d)
if not d:
break
chan.send(d)
except (EOFError,OSError):
pass

四:linux服务器上测试

(1)为项目创建一个公共用户,为堡垒机用户提供。密码设置简单

useradd crazyeye
passwd crazyeye
输入密码:

(2)正常启用项目

python backend_manage.py run

(3)需要用户登录账号后立即执行程序,减少用户权限

去操作用户家目录下的.bashrc文件,进行配置

# .bashrc

# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi # User specific aliases and functions
python3 /home/crazyeye/crazyeye/backend_manage.py run  #用户登录后自动执行
exit  #防止用户出现不可预期的错误导致上面的程序退出,而去操作账号下的其他数据,我们需要让上面程序结束后,账号退出即可

五.实现web页面对堡垒机进行操作

linux下安装shellinabox实现web登录服务器

{% extends "index.html" %}
{% block right-content-container %}
<!--Page Title-->
<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
<div id="page-title">
<h1 class="page-header text-overflow">Web Ssh</h1> <!--Searchbox-->
<div class="searchbox">
<div class="input-group custom-search-form">
<input type="text" class="form-control" placeholder="Search..">
<span class="input-group-btn">
<button class="text-muted" type="button"><i class="demo-pli-magnifi-glass"></i></button>
</span>
</div>
</div>
</div>
<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
<!--End page title--> <!--Page content-->
<!--===================================================-->
<div id="page-content">
<div class="row">
<div class="col-lg-12">
<iframe src="http://192.168.218.129:4200/" width="100%" style="padding-bottom:100%;background-color:white;"></iframe>
</div>
</div>
</div>
<!--===================================================-->
<!--End page content-->
{% endblock %}

前端代码:使用iframe

六.实现批量命令和文件功能

(0)添加数据表和业务流程图

1..数据表增加

class Task(models.Model):
'''批量任务'''
task_type_choice = (
('cmd',"批量命令"),
('file_transfer','文件传输'),
) task_type = models.CharField(choices=task_type_choice,max_length=)
user = models.ForeignKey("UserProfile")
content = models.CharField(max_length=) date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return "%s %s"%(self.task_type,self.content) class TaskLogDetail(models.Model):
'''存储每台主机执行结果'''
task = models.ForeignKey("Task")
host_to_remote_user = models.ForeignKey("HostToRemoteUser")
result = models.TextField(verbose_name="任务结果") status_choices = ((, 'initialized'), (, 'sucess'), (, 'failed'), (, 'timeout'))
status = models.SmallIntegerField(choices=status_choices) date = models.DateTimeField(auto_now_add=True)

2.流程图

(1)批量命令展示

(2)批量文件操作展示

(3)全部代码展示

1.公共组件

<div class="col-lg-3">
<div class="panel">
<div class="panel-heading">
<div class="panel-control">
<a title="" data-html="true" data-container="body" data-original-title="<p class='h4 text-semibold'>Information</p><p style='width:150px'>This is an information bubble to help the user.</p>" href="#" class="demo-psi-information icon-lg icon-fw unselectable text-info add-tooltip"></a>
</div>
<h3 class="panel-title">主机列表</h3>
</div>
<div class="bord-btm">
<div class="list-group bord-no">
{% for hostGroup in request.user.host_groups.all %}
<a class="list-group-item" onclick="toggel_host(this);" href="#">{{ hostGroup.name }}<span class="badge badge-success">{{ hostGroup.host_to_remote_users.count }}</span></a>
<ul class="list-group" style="margin-bottom: 0px;display: none;">
{% for hostUser in hostGroup.host_to_remote_users.all %}
<li class="list-group-item">
<input id="demo-form-checkbox-{{ hostUser.id }}" tag="host-selected" class="magic-checkbox" type="checkbox" value="{{ hostUser.id }}">
<label for="demo-form-checkbox-{{ hostUser.id }}">{{ hostUser.host }}@{{ hostUser.remote_user.username }}</label>
</li>
{% endfor %}
</ul>
{% endfor %}
<a class="list-group-item" onclick="toggel_host(this);" href="#">未分组主机<span class="badge badge-success">{{ request.user.host_to_remote_users.count }}</span></a>
<ul class="list-group" style="margin-bottom: 0px;display: none;">
{% for hostUser in request.user.host_to_remote_users.all %}
<li class="list-group-item">
<input id="demo-form-checkbox-{{ hostUser.id }}" tag="host-selected" class="magic-checkbox" type="checkbox" value="{{ hostUser.id }}">
<label for="demo-form-checkbox-{{ hostUser.id }}">{{ hostUser.host }}@{{ hostUser.remote_user.username }}</label>
</li>
{% endfor %}
</ul>
</div>
</div> </div> </div>

host_list_compentent.html主机列表

<script>
function toggel_host(ths){
$(ths).next().toggle();
} function ChangeFileType(ths){
if ($(ths).val() == "send"){
$("#local_file").show();
}else{
$("#local_file").hide();
}
} function ShowError(title,content,type) {
$(".modal-toggle").find(".modal-title").html(title);
$(".modal-toggle").find(".bootbox-body").html(content);
$(".modal-toggle").find(type).show();
$(".modal-toggle").find(".modal").show();
$(".modal-toggle").show();
} function submitData(ths,cmd_type){
if ($(ths).hasClass('disabled')){
return false;
}
var host_list = [];
$("[tag='host-selected']:checked").each(function(){
host_list.push($(this).val());
}) if (host_list.length == ){
ShowError("Error Before Submit","<h3>未选中主机</h1>",".btn-danger");
return false;
} var task_arguments = {}; if (cmd_type == 'cmd'){
var cmd = $("input[name='cmd']").val().trim()
if(cmd.length == ){
ShowError("Error Before Submit","<h3>请输入要执行的命令</h1>",".btn-danger");
return false;
} task_arguments = {
'task_type' : 'cmd',
'cmd': cmd,
}
}else{
task_arguments['task_type'] = 'file_transfer'; if ($("#server_file_path").val().trim().length == ){
ShowError("Error Before Submit","<h3>请输入服务端文件路径</h1>",".btn-danger");
return false;
} if ($("#select_file_type").val() == "recv"){
task_arguments['transfer_type'] = "recv";
task_arguments['server_file_path'] = $("#server_file_path").val().trim();
}else{
if ($("#local_file_path").val().trim().length == ){
ShowError("Error Before Submit","<h3>请输入本地文件路径</h1>",".btn-danger");
return false;
}
task_arguments['transfer_type'] = "send";
task_arguments['server_file_path'] = $("#server_file_path").val().trim();
task_arguments['local_file_path'] = $("#local_file_path").val().trim();
}
} task_arguments['selected_hosts'] = host_list;
$(ths).addClass('disabled'); $.post(
"{% url 'batch_task_mgr' %}",
{'task_data':JSON.stringify(task_arguments),'csrfmiddlewaretoken':'{{ csrf_token }}'},
function(callback){
$("#task_result_container").empty();
$.each(callback['selected_hosts'],function(index,obj){
var ul_inner = '<li class="list-group-item" for="'+obj['id']+'"><span class="badge badge-primary">wait</span>'+obj['host_to_remote_user__host__ip_addr']+' '+obj['host_to_remote_user__host__name']+' '+obj['host_to_remote_user__remote_user__username']+'</li><pre>initilize...</pre>';
$("#task_result_container").append(ul_inner);
});
TimerFlag = setInterval(GetTask,,callback['task_id']);
},
'json'
)
} function GetTask(task_id){
$.get(
'{% url "get_task" %}',
{'id':task_id},
function(callback){
var All_get = true;
$.each(callback,function(index,obj){
$ele = $("#task_result_container li[for='"+obj['id']+"']");
$ele.next().text(obj['result']);
if(obj['status'] == ){
$ele.children().first().removeClass("badge-primary").addClass("badge-success").text("finished");
}else if (obj['status'] == ){
$ele.children().first().removeClass("badge-primary").addClass("badge-warning").text("warning");
}else{
All_get = false;
}
});
if (All_get){
$("#submit_btn").removeClass("disabled");
clearInterval(TimerFlag);
}
},
'json'
);
} $(function(){
$(".modal-toggle").find(".btn-danger").click(function(){
$(this).parents(".modal-toggle").hide();
});
})
</script>

multitask_js_compentent.html共用js

<div class="col-lg-7">
<div class="panel">
<div class="panel-heading">
<h3 class="panel-title">批量执行命令结果展示</h3>
</div>
<div class="panel-body">
<ul class="list-group" id="task_result_container">
</ul>
</div>
</div>
</div>

task_result_compentent.html任务结果展示

2.前端展示:批量命令,批量文件

{% extends "index.html" %}
{% block right-content-container %}
<!--Page Title-->
<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
<div id="page-title">
<h1 class="page-header text-overflow">Host Manage</h1> <!--Searchbox-->
<div class="searchbox">
<div class="input-group custom-search-form">
<input type="text" class="form-control" placeholder="Search..">
<span class="input-group-btn">
<button class="text-muted" type="button"><i class="demo-pli-magnifi-glass"></i></button>
</span>
</div>
</div>
</div>
<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
<!--End page title--> <!--Page content-->
<!--===================================================-->
<div id="page-content">
<div class="row">
{% include "include/host_list_compentent.html" %}
<div class="col-lg-7">
<div class="panel">
<div class="panel-heading">
<h3 class="panel-title">批量执行命令</h3>
</div>
<div class="panel-body">
<form class="form-inline">
<div class="input-group mar-btm col-lg-12">
<input type="text" name="cmd" placeholder="input your cmd...." class="form-control">
<span class="input-group-btn" style="width:32px;">
<button class="btn btn-mint" id="submit_btn" onclick="submitData(this,'cmd');" type="button">执行命令</button>
</span>
</div>
</form>
</div>
</div>
</div>
{% include "include/task_result_compentent.html" %}
</div>
</div>
<!--===================================================-->
<!--End page content--> <div class="modal-toggle" style="display: none;">
<div class="bootbox modal fade in" tabindex="-1" role="dialog" style="padding-right: 17px;">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<i class="pci-cross pci-circle"></i>
</button>
<h4 class="modal-title"></h4>
</div>
<div class="modal-body">
<div class="bootbox-body"></div>
</div>
<div class="modal-footer">
<button data-bb-handler="success" type="button" class="btn btn-success" style="display: none;">Success!</button>
<button data-bb-handler="danger" type="button" class="btn btn-danger" style="display: none;">Danger!</button>
<button data-bb-handler="main" type="button" class="btn btn-primary" style="display: none;">Confirm</button>
</div>
</div>
</div>
</div>
<div class="modal-backdrop fade in"></div>
</div> {% include "include/multitask_js_compentent.html" %}
{% endblock %}

host_mgr.html前端批量命令

{% extends "index.html" %}
{% block right-content-container %}
<!--Page Title-->
<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
<div id="page-title">
<h1 class="page-header text-overflow">File Tranfer</h1> <!--Searchbox-->
<div class="searchbox">
<div class="input-group custom-search-form">
<input type="text" class="form-control" placeholder="Search..">
<span class="input-group-btn">
<button class="text-muted" type="button"><i class="demo-pli-magnifi-glass"></i></button>
</span>
</div>
</div>
</div>
<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
<!--End page title--> <!--Page content-->
<!--===================================================-->
<div id="page-content">
<div class="row">
{% include "include/host_list_compentent.html" %}
<div class="col-lg-7">
<div class="panel">
<div class="panel-heading">
<h3 class="panel-title">批量操作文件</h3>
</div>
<div class="panel-body">
<form class="form-horizontal">
<div class="form-group">
<label class="col-sm-2 control-label">文件操作:</label>
<div class="col-sm-10">
<select class="form-control" onchange="ChangeFileType(this);" id="select_file_type">
<option value="recv">接收文件</option>
<option value="send">发送文件</option>
</select>
</div>
</div>
<div class="form-group" id="server_file">
<label class="col-sm-2 control-label">服务器文件路径:</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="server_file_path" placeholder="Service file path">
</div>
</div>
<div class="form-group" style="display: none;" id="local_file" >
<label class="col-sm-2 control-label">本地文件路径:</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="local_file_path" placeholder="Local file path">
</div>
</div>
<div class="panel-footer text-right">
<button class="btn btn-success" onclick="submitData(this,'file_transfer');" id="submit_btn" type="button">Submit</button>
</div>
</form>
</div>
</div>
</div>
{% include "include/task_result_compentent.html" %}
</div>
</div>
<!--===================================================-->
<!--End page content--> <div class="modal-toggle" style="display: none;">
<div class="bootbox modal fade in" tabindex="-1" role="dialog" style="padding-right: 17px;">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<i class="pci-cross pci-circle"></i>
</button>
<h4 class="modal-title"></h4>
</div>
<div class="modal-body">
<div class="bootbox-body"></div>
</div>
<div class="modal-footer">
<button data-bb-handler="success" type="button" class="btn btn-success" style="display: none;">Success!</button>
<button data-bb-handler="danger" type="button" class="btn btn-danger" style="display: none;">Danger!</button>
<button data-bb-handler="main" type="button" class="btn btn-primary" style="display: none;">Confirm</button>
</div>
</div>
</div>
</div>
<div class="modal-backdrop fade in"></div>
</div> {% include "include/multitask_js_compentent.html" %}
{% endblock %}

file_transfer.html批量文件

3.url和views视图文件

from django.conf.urls import url
from web import views urlpatterns = [
url(r"dashbroad.html",views.dashbroad),
url(r"login.html", views.acc_login),
url(r"web_ssh.html", views.web_ssh,name="web_ssh"),
url(r"host_mgr.html", views.host_mgr, name="host_mgr"),
url(r"file_transfer.html", views.file_transfer, name="file_transfer"),
url(r"batch_task_mgr.html", views.batch_task_mgr, name="batch_task_mgr"),
url(r"batch_task_mgr.html", views.batch_task_mgr, name="batch_task_mgr"),
url(r"get_task.html", views.get_task, name="get_task"),
]

urls.py

from django.shortcuts import render,redirect,HttpResponse
from django.contrib.auth import authenticate,login,logout
from django.contrib.auth.decorators import login_required
from repository import models
import json
# Create your views here. @login_required
def dashbroad(request): return render(request,"web/dashbroad.html") def acc_login(request):
error_msg = ""
if request.method == "POST":
username = request.POST.get("username")
password = request.POST.get("password")
user = authenticate(username=username,password=password)
if user:
login(request,user)
return redirect("/web/dashbroad.html")
else:
error_msg = "Wrong Username Or Password" return render(request,"login.html",{"error_msg":error_msg,}) @login_required
def web_ssh(request): return render(request,"web/web_ssh.html") @login_required
def host_mgr(request): return render(request,"web/host_mgr.html") @login_required
def file_transfer(request): return render(request,"web/file_transfer.html") def conv(date_obj):
return date_obj.strftime("%Y-%m-%d %H:%M:%S") @login_required
def batch_task_mgr(request):
from backend.multitask import Multitask task_obj = Multitask(request) respone = {
'task_id':task_obj.task_obj.id,
'selected_hosts':list(task_obj.task_obj.tasklogdetail_set.all().values(
'id',
'host_to_remote_user__host__ip_addr',
'host_to_remote_user__host__name',
'host_to_remote_user__remote_user__username'
))
} return HttpResponse(json.dumps(respone)) @login_required
def get_task(request):
task_log_obj = models.TaskLogDetail.objects.filter(task_id=request.GET.get("id")).values("id","status","result",'date') log_data = json.dumps(list(task_log_obj),default=conv) return HttpResponse(log_data)

views.py

4.后台backend模块的多任务管理类

# coding:utf8
# __author: Administrator
# date: //
# /usr/bin/env python
from repository import models
import json,subprocess
from django import conf class Multitask(object):
def __init__(self,request):
self.request = request
self.run_task() def run_task(self):
'''解析参数,调用方法'''
self.task_data = json.loads(self.request.POST.get("task_data"))
task_type = self.task_data.get("task_type")
if hasattr(self,task_type):
task_func = getattr(self,task_type)
task_func()
else:
print("cannot find task ",task_type) def cmd(self):
'''执行批量命令'''
#先将任务添加到Task中
task_obj = models.Task.objects.create(
task_type = "cmd",
user = self.request.user,
content=self.task_data.get('cmd')
)
#向TaskLogDetail中批量添加数据
task_log_list = []
for host_remote_user_id in set(self.task_data.get("selected_hosts")):
task_log_list.append(
models.TaskLogDetail(
task=task_obj,
host_to_remote_user_id=host_remote_user_id,
result="init...",
status=
)
) models.TaskLogDetail.objects.bulk_create(task_log_list) shell_cmd = "python %s/backend/task_runner.py %s"%(conf.settings.BASE_DIR,task_obj.id)
cmd_process = subprocess.Popen(shell_cmd,shell=True) self.task_obj = task_obj def file_transfer(self):
'''批量操作文件'''
# 先将任务添加到Task中
task_obj = models.Task.objects.create(
task_type="file_transfer",
user=self.request.user,
content=json.dumps(self.task_data)
)
# 向TaskLogDetail中批量添加数据
task_log_list = []
for host_remote_user_id in set(self.task_data.get("selected_hosts")):
task_log_list.append(
models.TaskLogDetail(
task=task_obj,
host_to_remote_user_id=host_remote_user_id,
result="init...",
status=
)
) models.TaskLogDetail.objects.bulk_create(task_log_list) shell_cmd = "python %s/backend/task_runner.py %s" % (conf.settings.BASE_DIR, task_obj.id)
cmd_process = subprocess.Popen(shell_cmd, shell=True) self.task_obj = task_obj

multitask.py

5.脚本task_runner.py

# coding:utf8
# __author: Administrator
# date: //
# /usr/bin/env python
import paramiko,os,sys,json
from concurrent.futures import ThreadPoolExecutor def ssh_cmd(task_obj):
host_to_user_obj = task_obj.host_to_remote_user ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try:
ssh.connect(host_to_user_obj.host.ip_addr, host_to_user_obj.host.port, host_to_user_obj.remote_user.username, host_to_user_obj.remote_user.password,timeout=)
stdin, stdout, stderr = ssh.exec_command(task_obj.task.content)
stdout_res = stdout.read()
stderr_res = stderr.read() task_obj.result = stdout_res + stderr_res if stderr_res:
task_obj.status =
else:
task_obj.status =
except Exception as e:
task_obj.status =
task_obj.result = e
finally:
ssh.close()
task_obj.save() def ssh_file(task_log_obj,task_obj):
file_cmd = json.loads(task_obj.content)
opration_res = '' try:
t = paramiko.Transport(
(task_log_obj.host_to_remote_user.host.ip_addr, task_log_obj.host_to_remote_user.host.port))
t.connect(username=task_log_obj.host_to_remote_user.remote_user.username,
password=task_log_obj.host_to_remote_user.remote_user.password)
sftp = paramiko.SFTPClient.from_transport(t)
if file_cmd.get("transfer_type") == "recv":
file_name = "%s-%s-%s"%(task_log_obj.host_to_remote_user.host.ip_addr,task_log_obj.host_to_remote_user.remote_user.username,os.path.basename(file_cmd.get("server_file_path")))
local_path = os.path.join(conf.settings.DOWN_FILE_PATH,str(task_obj.id),file_name)
if not os.path.exists(local_path):
try:
os.makedirs(os.path.dirname(local_path))
except Exception:
pass
sftp.get(file_cmd.get("server_file_path"), local_path)
t.close()
opration_res = "file [%s] recv success! path [%s]"%(file_cmd.get("server_file_path"),local_path)
else:
file_path = file_cmd.get("local_file_path")
if os.path.isdir(file_path):
file_path = os.path.join(file_path,os.path.basename(file_cmd.get("server_file_path")))
sftp.put(file_path,file_cmd.get("server_file_path"))
t.close()
opration_res = "file [%s] send [%s] success!"%(file_path,file_cmd.get("server_file_path")) task_log_obj.result = opration_res
task_log_obj.status =
except Exception as e:
print(e)
task_log_obj.status =
task_log_obj.result = e
finally:
task_log_obj.save() if __name__ == "__main__": BaseDir = os.path.dirname(os.path.dirname(os.path.abspath(os.path.abspath(__file__))))
#将路径放入系统路径
sys.path.append(BaseDir)
# 加载Django环境
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "crazyeye.settings")
import django
django.setup() from django import conf if len(sys.argv) == :
exit("task id not provided!")
else:
task_id = sys.argv[]
from repository import models task_obj = models.Task.objects.get(id=task_id) pool = ThreadPoolExecutor() if task_obj.task_type == "cmd":
for task_log_obj in task_obj.tasklogdetail_set.all():
pool.submit(ssh_cmd,task_log_obj)
else:
for task_log_obj in task_obj.tasklogdetail_set.all():
pool.submit(ssh_file,task_log_obj,task_obj) pool.shutdown(wait=True)

task_runner.py

python---堡垒机开发的更多相关文章

  1. Day12 - 堡垒机开发

    Python之路,Day12 - 那就做个堡垒机吧   本节内容 项目实战:运维堡垒机开发 前景介绍 到目前为止,很多公司对堡垒机依然不太感冒,其实是没有充分认识到堡垒机在IT管理中的重要作用的,很多 ...

  2. Python之路-python(堡垒机)

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

  3. python堡垒机

    堡垒机 windows下安装python3的paramiko模块后一些报错总结: error: Unable to find vcvarsall.bat [官网对此问题的描述] : https://d ...

  4. Python 堡垒机介绍

    堡垒机说明 由于运维行业流动性很高,也为了防止有人在服务中残留后门,照成安全隐患,在这里我们使用堡垒机保证服务器管理安全. 我们知道运维人员在登陆服务时需要登陆用户,从客户端到服务端的过程中堡垒机,将 ...

  5. python 堡垒机讲解及实例

    paramiko模块,该模块基于SSH用于连接远程服务器并执行相关操作. SSHClient:用于连接远程服务器并执行基本命令 #coding:utf-8 import paramiko ssh=pa ...

  6. Python开发一个堡垒机

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

  7. Python之路,Day12 - 那就做个堡垒机吧

    Python之路,Day12 - 那就做个堡垒机吧   本节内容 项目实战:运维堡垒机开发 前景介绍 到目前为止,很多公司对堡垒机依然不太感冒,其实是没有充分认识到堡垒机在IT管理中的重要作用的,很多 ...

  8. Python 13 简单项目-堡垒机

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

  9. Python之堡垒机

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

  10. Python之路,Day13 - 堡垒机

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

随机推荐

  1. 手机端学习助手的说明书需求以及团队PM选择

    1.产品的背景 课堂上知识容量大.密度高,学生不能立刻掌握所学知识点,同时,网上资料冗杂繁复,指向性不强,导致学生不能高效的学习,为了充分利用学生的课余时间,培养学生自学能力,辅助老师教学,我们小组希 ...

  2. C# Linq找不到行或已更改

    前段时间工作中的一个新需求,有机会用到了Linq to SQL.使用后的第一感觉,就是方便很多,也为整个项目节约了一大把的开发时间,甚至代码量也少了很多.不过在程序的实际运行中,始终会遇到一些莫名其妙 ...

  3. asp.net 错误24

    错误 24 “xxx.Web.xxx.xxx”不包含“xxName”的定义,并且找不到可接受类型为“xxx.Web.xxxr.xxx”的第一个参数的扩展方法“xxxName”(是否缺少 using 指 ...

  4. win10频繁提示证书即将过期怎么办

    最近几天每次开机都会提示许可证即将过期 ”Windows+R”打开“运行”窗口,输入“slmgr.vbs -xpr”并点击“确定”,弹出的窗口确实显示过期时间在本月1.29过期 百度各种激活方法后,发 ...

  5. JS单例模式在工作中的使用

    为了尽可能的减少全局变量的污染,在写js的时候可以采用单例模式,形式如下: 比如有一个js叫demo.js,那么我们可以在js里这样写: var demo = {} 这样做的目的是将整个js当成一个对 ...

  6. Linux下更改正确国内时间

    Linux使用小Tips 整理些Linux些常遇到的问题. Linux下设置时间 提供两种最根本有效的方式,就是更改时区.这里以更改为国内上海时间例子,其他地方时区同理. 方法一 备份文件 mv /e ...

  7. mybatis plugin作为一款优秀的mybatis跳转插件

    阅读目录: 1. 简介2. 下载mybatis plugin插件3. 安装mybatis plugin插件4. 启动并验证5.说明1. 简介 mybatis plugin作为一款优秀的mybatis跳 ...

  8. 【BZOJ1046】上升序列(动态规划,贪心)

    [BZOJ1046]上升序列(动态规划,贪心) 题面 BZOJ 洛谷 题解 我一开始看错题了,一度以为是字典序最小的序列. 最后发现它要求的字典序是位置的字典序最小. 那就很好办了. 设\(f[i]\ ...

  9. codevs2464超级麻将

    题目链接http://codevs.cn/problem/2464/ 题目描述 Description 很多人都知道玩麻将,当然也有人不知道,呵呵,不要紧,我在这里简要地介绍一下麻将规则: 普通麻将有 ...

  10. 【转】器件为什么只听英文Datasheet的话

    浅谈为什么要阅读英文数据手册 ——带你Go Through Datasheet 系列 Unfortunately!从事软硬件(固件)开发的工程师都知道,我们所用的元器件,特别是高端器件和芯片,都是来自 ...