利用paramiko模块实现堡垒机+审计功能
paramiko模块是一个远程连接服务器,全真模拟ssh2协议的python模块,借助paramiko源码包中的demos目录下:demo.py和interactive.py两个模块实现简单的堡垒机+审计功能。编写的run_demo.py脚本,可以根据登陆堡垒机的用户信息在数据库查询该用户所有可以登陆的服务器列表,用户可以根据索引选择登陆。为防止用户退出脚本后不中断shell会话,导致不安全的因素,故在用户退出run_demo.py脚本时,会结束已经连接的shell会话,直接退出堡垒机。
一、修改paramiko源码模块的demo.py文件。
1.文件路径(具体情况具体~~,你懂得)
[root@CT7 demos]# pwd
/usr/share/doc/python-paramiko-1.15.1/demos
[root@CT7 demos]# cat demo.py
2.代码如下:
# Copyright (C) 2003-2007 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.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 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) == 0:
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.') #--modified this part,add note.#
def manual_auth(username, hostname,pw):
'''default_auth = 'p'
auth = input('Auth by (p)assword, (r)sa key, or (d)ss key? [%s] ' % default_auth)
if len(auth) == 0:
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) == 0:
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) == 0:
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, pw)'''
t.auth_password(username,pw) # setup logging
paramiko.util.log_to_file('demo.log') username = ''
if len(sys.argv) > 1:
hostname = sys.argv[1]
if hostname.find('@') >= 0:
username, hostname = hostname.split('@')
else:
hostname = input('Hostname: ')
if len(hostname) == 0:
print('*** Hostname required.')
sys.exit(1)
port = 22
if hostname.find(':') >= 0:
hostname, portstr = hostname.split(':')
port = int(portstr) # 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(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.') # get username
#--modified add note.
'''if username == '':
default_username = getpass.getuser()
username = input('Username [%s]: ' % default_username)
if len(username) == 0:
username = default_username'''
#--modified by myself add 3 lines.
username = sys.argv[2]
password = sys.argv[3]
ops_user = sys.argv[4] agent_auth(t, username)
if not t.is_authenticated():
manual_auth(username, hostname,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')
#--modified below line.
interactive.interactive_shell(chan,hostname,username,ops_user)
chan.close()
t.close() except Exception as e:
print('*** Caught exception: ' + str(e.__class__) + ': ' + str(e))
traceback.print_exc()
try:
t.close()
except:
pass
sys.exit(1)
二、修改paramiko源码模块的interactive.py文件。
1.文件路径(具体情况具体~~,你懂得)
[root@CT7 demos]# pwd
/usr/share/doc/python-paramiko-1.15.1/demos
[root@CT7 demos]# cat interactive.py
2.代码如下:
# Copyright (C) 2003-2007 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.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. import time
import socket
import sys
from paramiko.py3compat import u # windows does not have termios...
try:
import termios
import tty
has_termios = True
except ImportError:
has_termios = False #--modified this part,add hostname,username,ops_user.
def interactive_shell(chan,hostname,username,ops_user):
if has_termios:
posix_shell(chan,hostname,username,ops_user)
else:
windows_shell(chan) #--modified this part,add **kargs
def posix_shell(chan,hostname,username,ops_user):
date = time.strftime('%Y-%m-%d')
f = file('/tmp/audit_%s_%s.log' % (ops_user,date),'a+')
record = []
import select
oldtty = termios.tcgetattr(sys.stdin)
try:
tty.setraw(sys.stdin.fileno())
tty.setcbreak(sys.stdin.fileno())
chan.settimeout(0.0)
while True:
#--modified define datetime.
date = time.strftime("%Y-%m-%d %H:%M:%S")
r, w, e = select.select([chan, sys.stdin], [], [])
if chan in r:
try:
x = u(chan.recv(1024))
if len(x) == 0:
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(1)
if len(x) == 0:
break
record.append(x) #--modified
chan.send(x)
if x == '\r':
cmd = ''.join(record).split('\r')[-2]
#log = "%s|%s|%s|%s\n" %(hostname,date,username,cmd)
log = "%s |%s | %s | %s | %s\n" %(ops_user,hostname,username,date,cmd)
f.write(log)
f.flush()
f.close()
finally:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty) # thanks to Mike Looijmans for this code
def windows_shell(chan):
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(256)
if not data:
sys.stdout.write('\r\n*** EOF ***\r\n\r\n')
sys.stdout.flush()
break
sys.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:
break
chan.send(d)
except EOFError:
# user hit ^Z or F6
pass
三、编写登陆脚本,用户登陆堡垒机之后,自动运行脚本。脚本会根据用户信息到数据库中查询该用户可以登陆的所有机器,并展现给用户,用户只需要选择要登陆机器的序号即可完成登陆。
1.安装数据库,省略。
2.创建audit数据库,建立2个表,一个是用户信息表,一个服务器信息表,使用外键关联。(当用户删除后,对应的服务器信息也将删除。)
create database audit;
a.用户信息表,结构和数据。
CREATE TABLE `user_info` (
`employee_name` varchar(255) NOT NULL,
`department` varchar(255) DEFAULT NULL,
PRIMARY KEY (`employee_name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
b.服务器信息表,结构和数据。
CREATE TABLE `server_info` (
`employee_name` varchar(255) NOT NULL,
`ip_address` varchar(16) NOT NULL,
`user_name` varchar(16) NOT NULL,
`user_pass` varchar(32) NOT NULL,
KEY `employee_name` (`employee_name`),
CONSTRAINT `server_info_ibfk_1` FOREIGN KEY (`employee_name`) REFERENCES `user_info` (`employee_name`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1
3.编写登陆脚本run_demo.py,用户退出该脚本的时候也会退出该堡垒机(即脚本结束后,shell会话也会退出。)
代码如下:
#!/usr/bin/env python
import os,sys,MySQLdb employee_name = 'opslover'
s_id = os.getppid()
try:
conn = MySQLdb.connect(host='localhost',user='root',passwd='',db='audit',port=3306)
cur = conn.cursor()
cur.execute("select * from server_info where employee_name = '%s' " % employee_name)
result = cur.fetchall()
#print list(result)
#--change tuple to list
list_record = []
for record in result:
list_record.append(list(record))
#print list_record.index(list_record[1]),list_record[1]
while True:
os.system('clear')
print """
\033[35;1mWelcome to login jumpserver!\033[0m
Choose the Server to connect:"""
#print list_record
#--obtain ip's index ,choice index to login remote server
for line in list_record:
print list_record.index(line),line[1]
#print list_record
choice = raw_input("\033[32;1mPlease chose one you will login remote server:\033[0m").strip()
if choice == 'quit':
print type(s_id)
cmd = 'kill -9 %d' % s_id
os.system(cmd)
#if len(choice) == 0:continue
#if not choice.isdigit():continue
choice = int(choice)
if choice >= len(list_record):
print "\033[31:1mYou have no access to login remote server!!!"
continue
#print list_record
login_ops_user = list_record[choice][0]
login_server = list_record[choice][1]
login_user = list_record[choice][2]
login_pass = list_record[choice][3]
#print login_ops_user,login_server,login_user,login_pass
cmd = 'python /usr/share/doc/python-paramiko-1.15.1/demos/demo.py %s %s %s %s' %(login_server,login_user,login_pass,login_ops_user)
os.system(cmd)
#print choice
cur.close()
conn.close()
except MySQLdb.Error,e:
print 'MySQL Error Info:',e
4.添加用户环境变量,每次登陆堡垒机,自动执行该脚本。
[root@CT7 opslover]# grep python /home/opslover/.bashrc
python run_demo.py
四、模拟登陆演示。
1.使用opslover登陆堡垒机,显示此用户所有可以登陆的机器。
2.选择ip对应的索引即可完成登陆。(选择0)
3.执行exit退出远程服务器后,堡垒机会继续询问你要登陆哪台机器。选择1,继续。
4.执行exit退出远程服务器后,堡垒机会继续询问你要登陆哪台机器。此时用户可以退出,不再登陆远程服务器,执行quit即可,shell进程也会被退出。
五、审计结果查看。
模拟登陆过程中的所有操作都会被记录在/tmp/audit开头的文件,会以用户和日期分割文件。
这样就借用paramiko模块简单实现了堡垒机+审计的功能。有兴趣的可以根据自己的需要自行继续扩展修改。
利用paramiko模块实现堡垒机+审计功能的更多相关文章
- python远程连接paramiko 模块和堡垒机实现
paramiko使用 paramiko模块是基于python实现了SSH2远程安全连接,支持认证和密钥方式,可以实现远程连接.命令执行.文件传输.中间SSH代理功能 安装 pip install pa ...
- Python自动化运维之19、Paramiko模块和堡垒机实战
paramiko模块 paramiko是一个用于做远程控制的模块,使用该模块可以对远程服务器进行命令或文件操作,值得一说的是,fabric和ansible内部的远程管理就是使用的paramiko来现实 ...
- paramiko模块实现堡垒机
通过SSHClient 执行命令 """通过用户名密码验证""" import paramiko # 创建 SSH 对象 ssh = par ...
- Python修改paramiko模块开发运维审计保垒机
目前市面上,专门做IT审计堡垒机的厂商有很多,他们的产品都有一个特点,那就是基本上每台的售价都在20万以上.像我们做技术的,不可能每次待的公司都是大公司,那么在小公司,是不太可能投资20多万买一台硬件 ...
- Python----Paramiko模块和堡垒机实战
paramiko模块 paramiko是一个用于做远程控制的模块,使用该模块可以对远程服务器进行命令或文件操作,值得一说的是,fabric和ansible内部的远程管理就是使用的paramiko来现实 ...
- 利用SHELL的PROMPT_COMMAND添加日志审计功能,实时记录任何用户的操作到日志文件中
利用 PROMPT_COMMAND 实现命令审计功能:记录什么用户,在什么时间,做了什么操作,然后将查到的信息记录到一个文件里. 具体操作: 将以下内容追加到/etc/profile: ####### ...
- 利用Paramiko模块远程连接Linux
使用Paramiko模块模拟SSH远程连接到服务器,并执行命令.(支持Tab键补全) 1.安装相关模块: 1)安装 Crypto 模块: 下载源码包解压 安装: sudo python setup.p ...
- 审计系统---堡垒机python下ssh的使用
堡垒机python下ssh的使用 [堡垒机更多参考]http://www.cnblogs.com/alex3714/articles/5286889.html [paramiko的Demo实例]htt ...
- IronFort---基于Django和Websocket的堡垒机
WebSSH有很多,基于Django的Web服务也有很多,使用Paramiko在Python中进行SSH访问的就更多了.但是通过gevent将三者结合起来,实现通过浏览器访问的堡垒机就很少见了.本文将 ...
随机推荐
- thinkphp全站静态页实现方法!
1:在根目录下的全局index.php中加下面这行: define('HTML_PATH', './htm');//生成静态页面的文件位置 2:在项目的配置文件config.php中加下面这行: 'H ...
- 网络知识学习1---(基础知识:ISO/OSI七层模型和TCP/IP四层模型)
以下的内容和之后的几篇博客只是比较初级的介绍,想要深入学习的话建议自己钻研<TCP/IP详解 卷1:协议> 1.ISO/OSI七层模型 下四层是为数据传输服务的,物理层是真正的传输数 ...
- 【转载】PyQt QSetting保存设置
转载地址: http://blog.sina.com.cn/s/blog_4b5039210100h3zb.html 用户对应用程序经常有这样的要求:要求它能记住它的settings,比如窗口大小,位 ...
- Oracle 11g RAC 卸载CRS步骤
Oracle 11g之后提供了卸载grid和database的脚本,可以卸载的比较干净,不需要手动删除crs ##########如果要卸载RAC,需要先使用dbca删除数据库,在执行下面的操作### ...
- WinForm/MIS项目开发之中按钮级权限实践
一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...
- Python 元组
#不可变序列-----元组 tuple #元组和列表十分相似,元组和字符串一样都是不可变的. #元组由不同的元素组成,每个元素可以存储不同类型的数据,例如 #字符串.数字和元组 #元组通常代表一行数据 ...
- 单色半透明-兼容IE7
background: #000; width: 100%;height: 100%; filter: alpha(opacity=30); opacity: 0.3;
- IE8+兼容经验小结
最近一段时间,我都使用Flask+Bootstrap3的框架组合进行开发.本文就是在这种技术组合下,分享IE8+兼容性问题的解决方法.根据我的实践经验,如果你在写HTML/CSS时候是按照W3C推荐的 ...
- 洛谷 P1736 创意吃鱼法 Label:dp || 前缀和
题目描述 回到家中的猫猫把三桶鱼全部转移到了她那长方形大池子中,然后开始思考:到底要以何种方法吃鱼呢(猫猫就是这么可爱,吃鱼也要想好吃法 ^_*).她发现,把大池子视为01矩阵(0表示对应位置无鱼,1 ...
- db2 游标使用
游标一般用来迭代结果集中的行 为了在一个过程中处理一个游标的结果,需要做以下事情: 在存储过程块的开头部分 DECLARE 游标. 打开该游标. 将游标的结果取出到之前已声明的本地变量中(隐式游标处理 ...