利用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将三者结合起来,实现通过浏览器访问的堡垒机就很少见了.本文将 ...
随机推荐
- nginx.conf配置文件里的upstream加入健康检查
查看NGINX启用了那些模块: # ./nginx -V Tengine version: Tengine/ (nginx/) built by gcc (Red Hat -) (GCC) TLS S ...
- 无废话ExtJs 入门教程十九[API的使用]
无废话ExtJs 入门教程十九[API的使用] extjs技术交流,欢迎加群(201926085) 首先解释什么是 API 来自百度百科的官方解释:API(Application Programmin ...
- Chart图表
这东西挺直观 封装个类 public class aaa { private string name; public string Name { get { return name; } set { ...
- C# 的EF框架怎么连接Oracle数据库
安装odp.net ODP.NET你不需要安装Oracle,不需要配置oracle.key文件,不需要配置TnsNames.Ora文件 不需要配置环境变量:完全的傻瓜式的在没有安装oracle数据库或 ...
- 2016 Multi-University Training Contest 2
8/13 2016 Multi-University Training Contest 2官方题解 数学 A Acperience(CYD)题意: 给定一个向量,求他减去一个 α(>=0)乘以 ...
- POJ 2431Expedition
Description A group of cows grabbed a truck and ventured on an expedition deep into the jungle. Bein ...
- CodeForces 515B. Drazil and His Happy Friends
B. Drazil and His Happy Friends time limit per test 2 seconds memory limit per test 256 megabytes in ...
- python中获取指定目录下所有文件名列表的程序
http://blog.csdn.net/rumswell/article/details/9818001 # -*- coding: utf-8 -*-#~ #------------------- ...
- 【BZOJ1503】[HAOI2007]反素数ant 搜索
结论题...网上讲的好的很多... #include <iostream> using namespace std; ]={,,,,,,,,,},num=; long long ans,n ...
- Android -- 启动另外一个Activity的方式(2s自动启动)
1. 使用Handler 并且可以设置进入和退出的动画效果 Class < ? > activityClass; Class [ ] paramTypes = { Integer.TY ...