First, the concept of Fortress machine
Bastion machines, also known as springboard machines, are used in systems operation and maintenance environments. Refers to in a specific network environment, in order to protect the network and data from external and internal users of intrusion and destruction, and the use of various technical means in real time to collect and monitor the network environment of each component of the system status, security incidents, network activities, in order to centralize alarm, timely processing and audit accountability.
From the functional analysis, it synthesizes the core system operation and security audit control of the two main functions; from the analysis of technology realization, it cut off the terminal computer direct access to the network and server resources, and adopt the Protocol proxy method to take over the network and server access of the terminal computer. Visually speaking, the terminal computer's access to the target needs to be translated by operation and maintenance security audit. To make an analogy, operational security audits act as gatekeepers, and all requests for network equipment and servers go through the door. Therefore, operation and maintenance security audit can intercept illegal access and malicious attacks, and command block the non-compliance command, filter out all the illegal access to the target device, and internal personnel misoperation and illegal operation audit monitoring, so that the post-mortem responsibility tracking.
The original reason of the Fortress machine is that with the continuous development of the system, the network scale and the number of devices are rapidly expanding, the increasingly complex business system and the behavior of the operators of different backgrounds bring greater risks to the system security:
1. Multiple users use the same account. This situation occurs mainly in the same workgroup, because of the need for work, while the system manages accounts only, so only multiple users can share the same account. If a security incident occurs, it is not only difficult to locate the actual user and the responsible person of the account, but also can not effectively control the use of the account, there are large security risks and hidden dangers. 2. One user uses multiple accounts. At present, a maintenance personnel to use multiple accounts is a more common situation, users need to remember multiple sets of passwords at the same time in multiple host systems, network equipment to switch between, reduce work efficiency, increase the complexity of the job. 3. Lack of a unified rights management platform, the Authority management is increasingly heavy and disorderly, and the authority of the maintenance personnel are mostly extensive management, can not be based on the minimum rights allocation principle of user rights management, it is difficult to achieve more granular command-level permissions control, system security can not be fully guaranteed. 4. Unable to develop a unified access audit strategy, audit granularity coarse. Each network equipment, host system, database is separate audit record access behavior, because there is no unified audit strategy, and each system itself audit log content is different, it is difficult to timely through the system itself audit found illegal operation behavior and trace evidence. 5. The traditional network security audit system is unable to audit the content of SSH, RDP and other encryption and graphics operation protocols frequently used by maintenance personnel.
In order to solve the above problem, the fortress machine was developed as a "firewall" between the user and the host.
Simple to say is: Before to a hotel, you want to go to which room can go directly, but now not, must pass the front desk such "Fortress machine" permission and monitoring can, want to "do bad" have to be careful ...
II. structure of the Fortress machine
The core architecture of a bastion machine is usually as follows:
The device is connected as shown in the following way:
General execution process for bastion machines:
- Administrator creates an account for the user on the server (place the public key on the server, or use the username password)
- The user logs on to the fortress machine, enters the bastion machine user name password, displays the server list which the current user can manage
- The user chooses the server and automatically logs in
- Perform actions and record user actions at the same time
Note: In Linux, by configuring the user's. brashrc file, the implementation of SSH login automatically after the script, such as:/usr/bin/env python/home/jack/jump.py, after the completion of business operations logout automatically quit.
In addition, in order to properly and reliably play the role of the fortress machine, only rely on the fortress machine itself is not enough, but also need to user security restrictions, Fort Machine deployment, to ensure that your system to achieve the following conditions:
- All people, including operations, development, and any people who need access to the business system, can only access the business system through a bastion machine
- Reclaim all access to the business system, so that no one knows the login password of any machine in the business system except the Bastion Machine Manager
- Restrictions on the network all personnel can only access the business system by jumping from the fortress machine
- Ensure that all others except the Bastion machine administrator have no operational privileges on the bastion machine itself, only one login jump function
- To ensure that the user's operation record can not be obtained and tampered with by the user in any way, to achieve the role of security audit.
Third, Python implementation fortress machine
With the third-party module Paramiko, the basic functions of the bastion machine can be easily implemented in Python, and web management and maintenance can be realized through the Django framework.
The basic architecture is as follows:
1. A simple implementation version
This version is simpler, can only enter one line of command, enter, and then receive the information returned by the host and print.
#!/usr/bin/env python#-*-coding:utf-8-*-import paramikoimport sysimport osimport socketimport selectimport This line is not required in Getpassfrom paramiko.py3compat import u # python2.x default_username = Getpass.getuser () Username = input (' Username [%s]: '% default_username) if Len (Username) = = 0:username = Default_usernamehostname = Input (' Hostname: ') if Len (hostname) = = 0:print (' * * * hostname required. ') Sys.exit (1) tran = Paramiko. Transport ((hostname, A,)) tran.start_client () Default_auth = "P" auth = input (' Auth by (p) Assword or (R) SA key[%s] '% Defau Lt_auth) If Len (auth) = = 0:auth = Default_authif 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) Tran.auth_publickey (username, key) ELSE:PW = Getpass.getpass (' Password for%[email protected] %s: '% (username, hostname)) Tran.auth_password (username, PW) # Open a channel Chan = Tran.open_session () # Get a Terminal chan.get_pty () # Activator Chan.invoke_shell () while True: # Monitor user input and server return data # Sys.stdin process user Input # Chan is the channel that was created before, used to receive server return information # Select module is IO multiplexing module, powerful, ubiquitous, you are worth learning and memory! Readable, writeable, error = Select.select ([Chan, Sys.stdin,],[],[],1) if chan in readable:try:x = U (CHAN.RECV (1024x768)) # python2.x can be received directly, do not u If len (x) = = 0:print (' \r\n*** eof\r\n ') Break Sys.stdout.write (x) Sys.stdout.flush () except Socket.timeout:pass I F Sys.stdin in READABLE:INP = Sys.stdin.readline () # reads a line of USER commands Chan.sendall (INP) chan.close () Tran . Close ()
2. Version sent by character
Above is a line of command execution, the following is a key to read and send a button.
# from Chan.invoke_shell () This sentence is omitted, and the above example is the same import Termios # import Python terminal module. Note that the Windows system does not have this module. # get the original TTY attribute, used to restore the original state on Exit Oldtty = Termios.tcgetattr (Sys.stdin) Try: # set new properties for TTY # Default current TTY device properties: # Enter a line to return, execute # CTRL + C process exits, encounters special characters, special handling. # This is for the original mode, do not recognize all special symbols # Place special characters applied at the current terminal, so set, all user input is sent to the remote server Tty.setraw (Sys.stdin.fileno ()) chan.settimeout (0.0) While True: # Monitors user input and remote server return data (socket) # blocks until the handle is readable by R, W, E = Select.select ([Chan, Sys.stdin], [], [], 1) if chan in r:try:x = U (CHAN.RECV (1024x768)) 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) # The user taps a key on the keyboard, reads it and sends it to the host if Len (x) = = 0:break Chan.send (x) finally: # Reset Terminal properties As the original termios.tcsetattr (sys.stdin, Termios. Tcsadrain, Oldtty) chan.close () Tran.close ()
3. More advanced version
In Linux, there is the TAB key to complete the function, to implement it, you need to press the key to read, the receiving host returns the tab completion information, and finally combined into a complete command, and execute. In addition, because the Windows operating system does not have a Termios module, it uses multithreading.
Import paramikoimport sysimport osimport socketimport getpassfrom paramiko.py3compat import u# Windows does not has Termi Os...try:import Termios Import TTY Has_termios = trueexcept Importerror:has_termios = falsedef interactive_s Hell (chan): If Has_termios:posix_shell (chan) Else:windows_shell (chan) def Posix_shell (Chan): Impor T Select oldtty = termios.tcgetattr (Sys.stdin) Try:tty.setraw (Sys.stdin.fileno ()) Tty.setcbreak (SYS.S Tdin.fileno ()) chan.settimeout (0.0) log = open (' Handle.log ', ' A + ', encoding= ' utf-8 ') flag = False Temp_list = [] While True:r, W, E = Select.select ([Chan, Sys.stdin], [], []) if Chan in r:try:x = U (CHAN.RECV (1024x768)) If Len (x) = = 0: Sys.stdout.write (' \r\n*** eof\r\n ') break if Flag:if x. StartsWith (' \ r \ n '): Pass Else:temp_list.append (x) Flag = False sys.stdout.write (x) Sys.stdout.flush () except Socke T.timeout:pass if sys.stdin in r:x = Sys.stdin.read (1) impor T json if Len (x) = = 0:break if x = = ' \ t ': flag = True Else:temp_list.append (x) if x = = ' \ R ': Log.write ('. Join (Temp_list)) Log.flush () temp_list.clear () chan.send (x) finall Y:termios.tcsetattr (Sys.stdin, Termios. Tcsadrain, Oldtty) 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 if not data:sys.stdout.write (' \r\n*** EOF ***\r\n\r\n ') sys.stdout.flu SH () 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 hits ^z or F6 passdef Run (): Default_username = Getpass.getuser () Username = input (' username [%s]: '% default_username ') If Len (Usernam e) = = 0:username = default_username hostname = input (' hostname: ') If LEN (hostname) = = 0:print (' * * * Hostname required. ') Sys.exit (1) tran = Paramiko. Transport ((hostname,,)) tran.start_client () Default_auth = "P" auth = input (' Auth by (p) Assword or (r) SA 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) Tran.auth_publickey (username, key) ELSE:PW = Getpass.getpas S (' Password for%[email protected]%s: '% (username, hostname)) Tran.auth_password (username, PW) # Open a channel Chan = Tran.open_session () # Gets a terminal chan.get_pty () # activator Chan.invoke_shell () Interactive_shell (Chan) c Han.close () tran.close () if __name__ = = ' __main__ ': Run ()
Iv. realization of SCP through bastion machine
The following example has a somewhat different bastion mode, which implements the SCP functionality of this architecture:
#!/usr/bin/env pythonimport paramikoimport os,sys,timehostname= "192.168.1.111" # Remote host username= "root" # The user name of the remote host password= "skjh935yft#" blip= "192.168.1.1" # Fortress machine bluser= "root" #堡垒机用户名blpasswd = "skjh935yft#" tmpdir= "/tmp" # Client source file path, bastion machine temporary path and remote host destination file path remotedir= "/data" localpath= "/Home /nginx_access.tar.gz "tmppath=tmpdir+"/nginx_access.tar.gz "remotepath=remotedir+"/nginx_access_hd.tar.gz "port= 22passinfo= ' s password: ' # This is the password information paramiko.util.log_to_file (' Syslogin.log ') # Paramiko comes with the log function t = P Aramiko. Transport ((blip, Port)) # Create a Connection object T.connect (Username=bluser, password=blpasswd) #建立连接sftp =paramiko. Sftpclient.from_transport (t) # Create SFTP connection Sftp.put (LocalPath, Tmppath) # Upload file with put method Sftp.close () # Close SFTP connection ssh=p Aramiko. Sshclient () Ssh.set_missing_host_key_policy (Paramiko. Autoaddpolicy ()) Ssh.connect (hostname=blip,username=bluser,password=blpasswd) #创建一个新的会话channel =ssh.invoke_shell () channel.settimeout () buff = ' resp = ' # Use the Linux native SCP feature to transfer temporary files on the bastion machine to the remote host channel.send (' SCP ' +tmppath+ ' + username+ ' @ ' +hostname+ ': ' +remotepath+ ' \ n ') and not Buff.endswith (passinfo): Try:resp = CHANNEL.RECV (9999) E Xcept exception,e:print ' Error info:%s connection time. '% (str (e)) Channel.close () Ssh.close () Sys.exit () Buff + = Resp if not buff.find (' yes/no ') ==-1:channel.send (' yes\n ') buff= ' Channel.send (password+ ' \ n ') buff= ' and not Buff.endswith (' # '): RESP = CHANNEL.RECV (9999) if not Resp.find (passinfo) ==-1:print ' E Rror info:authentication failed. ' Channel.close () Ssh.close () sys.exit () Buff + respprint buffchannel.close () ssh.close ()
Python-based Fortress machine