您的位置:首页 > 编程语言 > Python开发

paramiko-python-(3)审计服务器开发

2017-03-08 15:26 501 查看
源码都粘贴出来,有点长,但是真正改动的就只有几行

环境配置:我的另一片博客推荐用指令安装,然后下载相应的源码(我们需要源码里的demo),源码版本与安装的版本最好一样。我安装的是2.2,下载的是2.1,之前无脑用1.7一直报错。

#用源码安装后进入终端用以下指令查看paramiko的版本
>>> import paramiko
>>> paramiko.__version__
'2.1.2'


paramiko源码里有几个例子,稍微改一下就成了一个简版的审计服务器。我们的目的就是【使用者】使用我们的几台重要的主机,我们追踪记录操作者的痕迹信息,在简版中我们记录时间,使用的指令等。

demo.py
interactive.py
#这两个文件是我们需要的


创建堡垒机账户

#登录root
sudo su
#创建一个新账户
adduser access_gateway
#之后设置密码等信息就行(假设创建了一个账户access_gateway)


使【使用者】一登陆这个堡垒机账户就启动一个脚本,让其选择登录那个客户机

#/home/access_gateway/.bashrc文件末尾添加一句指令
/usr/bin/python /home/access_gateway/menu.py
#前者可以用指令which python获知
#后者是菜单文件的路径


供【使用者】选择的菜单文件menu.py

#!/usr/bin/env python

import os,sys

msg = """
\033[42;1mWelcome to Use the NCU Auditing System!\033[0m
"""
print msg

host_dict = {
#条件有限,只有【'huawei':'139.159.217.176'】这个选项有用
'huawei':'139.159.217.176',
'root':'192.168.180.128',
'wanghao':'10.0.1.139'
}

while True:
for hostname, ip in host_dict.items():
print hostname,ip
try:
host = raw_input('Please Input Host Name:')
if host == '':
continue
elif host == 'quit':
print 'GoodBye(T_T)'
break
elif not host_dict.has_key(host):
print 'No host matched, try again.'
continue
else:
print 'Going to connect %s with ip [%s]'%(host, host_dict[host])
os.system('python demo.py %s'%host_dict[host])
sys.exit(1)
except KeyboardInterrupt:
continue
except EOFError:
continue


源码的修改

#!/usr/bin/env python

import base64
from binascii import hexlify
import getpass
import os
import select
import socket
import sys
import threading
import time
import traceback

import paramiko
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.'

def manual_auth(username, hostname):
default_auth = 'p'
auth = raw_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 = raw_input('RSA key [%s]: ' % default_path)
if len(path) == 0:
path = default_path
try:
key = paramiko.RSAKey.from_private_key_file(path)
except pa
4000
ramiko.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 = raw_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)

# 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 = raw_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, 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 not keys.has_key(hostname):
print '*** WARNING: Unknown host key!'
elif not keys[hostname].has_key(key.get_name()):
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
if username == '':
default_username = getpass.getuser()
username = raw_input('Username [%s]: ' % default_username)
if len(username) == 0:
username = default_username

agent_auth(t, username)
if not t.is_authenticated():
manual_auth(username, hostname)
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!'
print
#下面这条语句做了修改
interactive.interactive_shell(chan, username, hostname)
#上面这条语句做了修改
chan.close()
t.close()

except Exception, e:
print '*** Caught exception: ' + str(e.__class__) + ': ' + str(e)
traceback.print_exc()
try:
t.close()
except:
pass
sys.exit(1)


interative.py

import socket
import sys
import time

# windows does not have termios...
try:
import termios
import tty
has_termios = True
except ImportError:
has_termios = False

def interactive_shell(chan, username, hostname):
if has_termios:
posix_shell(chan, username, hostname)
else:
windows_shell(chan)

#这个函数改动较大
#在/tmp/audit/logs目录下创建日志记录文件,记录时间,指令操作等
def posix_shell(chan, username, hostname):
import select
day_time = time.strftime('%y-%m-%d')
f=open('/tmp/audit/logs/audit_%s.log'%(day_time), 'a')
oldtty = termios.tcgetattr(sys.stdin)
try:
tty.setraw(sys.stdin.fileno())
tty.setcbreak(sys.stdin.fileno())
chan.settimeout(0.0)
record = []
while True:
day = time.strftime('%y-%m-%d %H:%M:%S')
r, w, e = select.select([chan, sys.stdin], [], [])
if chan in r:
try:
x = chan.recv(1024)
if len(x) == 0:
print '\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
chan.send(x)
#print x
record.append(x)

if x == '\r':
cmd = ''.join(record).split('\r')[-2]
f.write(username + ' ' + hostname + ' ' + day + ' ' + cmd + '\n')
f.flush()
record = []

finally:
f.close()
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


测试结果

#日志记录的情况
root 139.159.217.176 17-03-08 12:13:15 df
root 139.159.217.176 17-03-08 12:13:18 ls -a
root 139.159.217.176 17-03-08 12:13:59 exit
root 139.159.217.176 17-03-08 12:14:57 df
root 139.159.217.176 17-03-08 12:14:59 ls -a
root 139.159.217.176 17-03-08 12:15:01 df
root 139.159.217.176 17-03-08 12:15:04 ll
root 139.159.217.176 17-03-08 12:15:09 top -bn 1
root 139.159.217.176 17-03-08 12:15:26 exit


如果你在连接远程主机遇到问题

不妨参考我在开头提到那篇文章,里面提到paramiko安装,用密码或者密钥远程登录主机

为了更高BIGE

安装【shellinabox】,能够在WEB端登录终端。

安装:

#这里只给出指令安装的方式
#想要源码安装的,请自行百度
sudo apt-get install shellinabox

#Then restart the shellinabox daemon:

sudo invoke-rc.d shellinabox restart

#Now you can access your shellinabox server with an easier URL: https://yourcomputername 
#编辑这个文件,可以更改端口,WEB界面背景,字体大小颜色等
sudo gedit /etc/default/shellinabox


安装过程:

derek@ubuntu:~$ sudo apt-get install shellinabox
[sudo] password for derek:
Reading package lists... Done
Building dependency tree
Reading state information..
ed84
. Done
The following NEW packages will be installed:
shellinabox
0 to upgrade, 1 to newly install, 0 to remove and 28 not to upgrade.
Need to get 122 kB of archives.
After this operation, 510 kB of additional disk space will be used.
Get:1 http://us.archive.ubuntu.com/ubuntu xenial/universe amd64 shellinabox amd64 2.19 [122 kB]
Fetched 122 kB in 2s (49.0 kB/s)
Selecting previously unselected package shellinabox.
(Reading database ... 227125 files and directories currently installed.)
Preparing to unpack .../shellinabox_2.19_amd64.deb ...
Unpacking shellinabox (2.19) ...
Processing triggers for man-db (2.7.5-1) ...
Processing triggers for systemd (229-4ubuntu16) ...
Processing triggers for ureadahead (0.100.0-19) ...
Setting up shellinabox (2.19) ...
Processing triggers for systemd (229-4ubuntu16) ...
Processing triggers for ureadahead (0.100.0-19) ...


用指令【ps -ef | grep shellinabox】找到进程ID,登录root然后kill掉。。。有点麻烦



最终的一个效果图



看见没有!通过登录堡垒机账户,提供给【使用者】几个登录选项,【使用者】登陆上。这边就记录了【使用者】的行为信息。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息