python连接linux堡垒机_利用Python Paramiko开发linux堡垒机
1.Paramiko模塊下的demo.py程序
前面利用Python中的Paramiko模塊可以進行SSH的連接,以及用來傳送文件(SFTP),但是無論是哪一種方式,連接都是短暫的,并非是長連的,即一次執行一個命令或上傳與下載一個文件,顯然效率是不如直接使用Linux shell下的ssh連接命令來進行連接。其實在將Paramiko的源碼解壓出來后,里面有一個demo的文件夾,里面有一個demo.py的程序,利用它,我們就可以進行長連接,即像ssh一樣連接遠程主機:xpleaf@xpleaf-machine:/mnt/hgfs/Python/day6/sorftwares/demp_test/paramiko-1.7.7.1$?ls
demos??LICENSE??????paramiko???????????PKG-INFO??setup.cfg????????setup.py??tests
docs???MANIFEST.in??paramiko.egg-info??README????setup_helper.py??test.py
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day6/sorftwares/demp_test/paramiko-1.7.7.1$?cd?demos/
xpleaf@xpleaf-machine:/mnt/hgfs/Python/day6/sorftwares/demp_test/paramiko-1.7.7.1/demos$?ls?-l?demo.py
-rwxrwxrwx?1?root?root?5340??6月?16??2010?demo.py
利用demo.py程序,我們可以進行ssh的長連接,比如這里有一臺IP地址為192.168.1.124的遠程主機需要進行連接,使用遠程主機的賬戶名為xpleaf,如下:xpleaf@xpleaf-machine:/mnt/hgfs/Python/day6/sorftwares/demp_test/paramiko-1.7.7.1/demos$?python?demo.py
Hostname:?192.168.1.124
***?Host?key?OK.
Username?[xpleaf]:?xpleaf
Auth?by?(p)assword,?(r)sa?key,?or?(d)ss?key??[p]
Password?for?xpleaf@192.168.1.124:
***?Here?we?go!
Last?login:?Fri?Oct??9?17:19:42?2015?from?192.168.1.13
[xpleaf@moban?~]$?pwd
/home/xpleaf
這樣我們就可以像ssh連接一樣在遠程主機上執行相關的命令了,下面,我們就是通過觀察demo.py程序的源代碼來對相關的程序模塊作修改,然后實現簡單的堡壘主機監控程序的開發。
2.通過修改與demo.py相關的模塊來達到堡壘主機監控的功能
堡壘主機可以監控運維人員在服務器上做的命令操作,這里要做的,只是可以監控運維人員在Linux服務器上執行命令的操作,下面先給出這個監控程序的示意圖:
運維人員登陸認證示意圖:
運維人員命令監控記錄示意圖:
基于上面兩個圖示的說明,以及對Paramiko模塊中demo.py程序代碼的理解,可以對demo.py模塊以及相關的模塊程序源代碼作相應的修改,至于這個引導的過程,因為是Alex老師引導過來的,根據Alex老師修改源代碼的一些思想,然后自己再進一步修改其它部分的源代碼,所以這整一個探索的過程如果要講出來,篇幅比較大,這里就不提及了,下面直接給代碼:
修改后的demo.py源代碼:#!/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,pw):
'''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?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?=?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)'''
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'''
#changed?by?xpleaf?at?2015.10.9
username?=?sys.argv[2]
password?=?sys.argv[3]
sa_username?=?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!'
interactive.interactive_shell(chan,hostname,username,sa_username)
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)
修改后的interactive.py源代碼:import?socket
import?sys,time
#?windows?does?not?have?termios...
try:
import?termios
import?tty
has_termios?=?True
except?ImportError:
has_termios?=?False
def?interactive_shell(chan,hostname,username,sa_username):
if?has_termios:
posix_shell(chan,hostname,username,sa_username)
else:
windows_shell(chan)
def?posix_shell(chan,hostname,username,sa_username):
import?select
date?=?time.strftime('%Y_%m_%d')?#Here?is?changed!
f?=?file('/tmp/%s_%s_record.log'?%?(sa_username,date),'a+')?#Here?is?changed!
record?=?[]?#Here?is?changed!
oldtty?=?termios.tcgetattr(sys.stdin)
try:
tty.setraw(sys.stdin.fileno())
tty.setcbreak(sys.stdin.fileno())
chan.settimeout(0.0)
while?True:
date?=?time.strftime('%Y_%m_%d?%H:%M:%S')?#Here?is?changed!
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
#print?x
record.append(x)
chan.send(x)
if?x?==?'\r':????#Here?is?changed!Follow:
#print?record
cmd?=?''.join(record).split('\r')[-2]
log?=?"%s?|?%s?|?%s?|?%s\n"?%?(hostname,date,sa_username,cmd)
f.write(log)
f.flush()
f.close()????#Here?is?changed!Above:
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
存放在堡壘主機下的Menus程序,這里命名為run_demo.py:#!/usr/bin/env?python
import?os,MySQLdb
os.system('clear')
print?'='*35
print?'''\033[32;1mWelcome?to?the?Connecting?System!\033[0m
Choose?the?Server?to?connect:
1.DNS?Server:??192.168.1.124
2.DHCP?Server:?192.168.1.134'''
print?'='*35
choice?=?raw_input('Your?choice:')
if?choice?==?'1':
address?=?'192.168.1.124'
elif?choice?==?'2':
address?=?'192.168.1.134'
sa_user?=?'yonghaoye'
try:
conn?=?MySQLdb.connect(host?=?'localhost',?user?=?'root',?\
passwd?=?'123456',?db?=?'Server_list',?port?=?3306)
cur?=?conn.cursor()
cur.execute("select?*?from?users?where?sa?=?'%s'"?%?sa_user)
qur_result?=?cur.fetchall()
for?record?in?qur_result:
if?record[3]?==?address:
hostname?=?record[3]
username?=?record[4]
password?=?record[5]
cur.close()
conn.close()
except?MySQLdb.Error,e:
print?'Mysql?Error?Msg:',e
cmd?=?'python?/mnt/hgfs/Python/day6/sorftwares/paramiko-1.7.7.1/demos/demo.py?%s?%s?%s?%s'?%?(hostname,username,password,sa_user)
os.system(cmd)
在堡壘主機上添加數據庫:添加了下面這樣的數據庫
mysql>?show?databases;
+--------------------+
|?Database???????????|
+--------------------+
|?information_schema?|
|?Server_list????????|
|?ftp_user???????????|
|?linkman????????????|
|?mysql??????????????|
|?performance_schema?|
|?s6py???????????????|
+--------------------+
7?rows?in?set?(0.01?sec)
mysql>?use?Server_list
Reading?table?information?for?completion?of?table?and?column?names
You?can?turn?off?this?feature?to?get?a?quicker?startup?with?-A
Database?changed
mysql>?show?tables;
+-----------------------+
|?Tables_in_Server_list?|
+-----------------------+
|?users?????????????????|
+-----------------------+
1?row?in?set?(0.00?sec)
mysql>?describe?users;
+-----------------+------------------+------+-----+---------+----------------+
|?Field???????????|?Type?????????????|?Null?|?Key?|?Default?|?Extra??????????|
+-----------------+------------------+------+-----+---------+----------------+
|?id??????????????|?int(10)?unsigned?|?NO???|?PRI?|?NULL????|?auto_increment?|
|?sa??????????????|?char(20)?????????|?NO???|?????|?NULL????|????????????????|
|?server_name?????|?char(20)?????????|?NO???|?????|?NULL????|????????????????|
|?server_address??|?char(20)?????????|?NO???|?????|?NULL????|????????????????|
|?server_username?|?char(20)?????????|?NO???|?????|?NULL????|????????????????|
|?server_password?|?char(20)?????????|?NO???|?????|?NULL????|????????????????|
+-----------------+------------------+------+-----+---------+----------------+
6?rows?in?set?(0.00?sec)
mysql>?selec?*?from?users;
ERROR?1064?(42000):?You?have?an?error?in?your?SQL?syntax;?check?the?manual?that?corresponds?to?your?MySQL?server?version?for?the?right?syntax?to?use?near?'selec?*?from?users'?at?line?1
mysql>?select?*?from?users;
+----+-----------+-------------+----------------+-----------------+-----------------+
|?id?|?sa????????|?server_name?|?server_address?|?server_username?|?server_password?|
+----+-----------+-------------+----------------+-----------------+-----------------+
|??1?|?yonghaoye?|?DNS?Server??|?192.168.1.124??|?xpleaf??????????|?123456??????????|
|??2?|?yonghaoye?|?DHCP?Server?|?192.168.1.134??|?public??????????|?123456??????????|
+----+-----------+-------------+----------------+-----------------+-----------------+
2?rows?in?set?(0.00?sec)
就不對數據庫中的內容做解釋說明了,其實看了前面的示意圖,再看這里的代碼就可以理解了。
3.監控程序演示
演示的網絡環境如下:
由于我在堡壘主機上安裝了shellinabox程序,所以在運維人員主機上,可以直接在web界面輸入堡壘主機的IP地址進行遠程連接,來看下面操作:
(1)運維人員主機登陸堡壘主機
(2)輸入堡壘主機賬號密碼
(3)登陸成功并進入服務器連接列表選擇界面
(4)選擇連接相應服務器
(5)運維人員執行相關命令
(6)在堡壘主機上查看運維人員的命令操作xpleaf@xpleaf-machine:/tmp$?tail?-f?yonghaoye_2015_10_10_record.log
192.168.1.124?|?2015_10_10?00:36:44?|?yonghaoye?|?pwd
192.168.1.124?|?2015_10_10?00:36:48?|?yonghaoye?|?whoami
192.168.1.124?|?2015_10_10?00:37:13?|?yonghaoye?|?echo?$PATH
可以看到,在堡壘主機上生成了一個相對應用戶的命令記錄日志文件,這里可以查看用戶執行的每一個命令,需要注意的是,這里記錄了用戶名“yonghaoye”,是堡壘主機上的用戶,并不是Linux服務器上面的,該用戶是分配給運維人員的,因此,也再一次看到,運維人員并不知道Linux服務器的賬戶和密碼,這樣就比較安全了。
3.不足與優化思路
通過上面的操作,這樣的一個程序確實是可以記錄運維人員在Linux服務器上做的操作,但是不足的是:
(1)程序還存在非常多的細節問題和Bug
(2)界面操作不夠人性化
但不管怎么說,這個小程序只是作為學習過程中的一個練習程序而已,但思路基本上是沒有問題的,根據上面的兩個缺點,往后可以進一步修改源代碼以保證程序運行的穩定性,同時對于界面問題,往后應該是要做成Web界面的,而不是借助shellinabox程序,這就需要調用Python中的Django模塊來做Web方面的開發,當然還有其它技術。
剛過國慶放假期間,看到Alex老師開發了一個開源的堡壘機監控程序,大家可以去看看,而我這里所的這個小程序,作為入門來學習,其實也是非常不錯的。
真的,那就非常了不得了,目前自己也在努力學習過程中,堅持下來就一定可以學到很多!Python不會讓我們失望的!
4.對于堡壘主機監控程序的進一步開發計劃
由于現在知道的真的是太少,往后會不斷學習,希望以后也能以這里這個小程序的思路自己開發一個開源的堡壘主機監控系統,雖然目前已經有開源的了,但作為自己來練手我想也是非常不錯的。
文章的思路寫得有點唐突,因為實在是很難把這其中學習的一個完整的過程寫下來,因為所花費時間非常多。所以我選擇了在演示操作里進行了更多的說明,至于源代碼的修改,有興趣的朋友可以對比修改前的代碼進行比對的。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的python连接linux堡垒机_利用Python Paramiko开发linux堡垒机的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: whitelabel error pag
- 下一篇: opencv python安装linux