2017-05-10 73 views
0

我試圖使用Paramiko(在Python 2.7上)連接到使用多因素身份驗證(用戶名+密碼+一次性密碼)的主機。 transport.auth_interactive函數似乎是這樣做的方式(基於我從文檔中理解的內容),但執行永遠無法達到這一點 - 身份驗證在client.connect行上失敗。Python帕拉米科(客戶端)多因素身份驗證

我似乎錯過了一些東西。

這是代碼:

#!/usr/bin/env python 

import paramiko 
import getpass 
import os 
import logging 

user = "" 
pw = "" 
mfa = "" 

def inter_handler(title, instructions, prompt_list): 
    resp = [] 

    for pr in prompt_list: 
     if pr[0].strip() == "Username:": 
      resp.append(user) 
     elif pr[0].strip() == "Password:": 
      resp.append(pw) 
     elif pr[0].strip() == "OTP Code:": 
      resp.append(mfa) 

    return tuple(resp) 


#Main Entry Point 
if __name__ == "__main__": 

    paramiko.util.log_to_file(os.path.expanduser('~/paramiko.log'), logging.DEBUG) 

    user = raw_input("Username: ") 
    pw = getpass.getpass("Password: ") 
    mfa = raw_input("OTP Code:") 

    client = paramiko.SSHClient() 
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) #Don't care about host keys 
    client.connect("mfahost.example.com", port=22, username=user, password=pw, look_for_keys=False) 
    client.get_transport().auth_interactive(user, inter_handler) 

    client.exec_command("touch ~/paramikotestfile") 

回答

0

回答我的問題,因爲我設法弄清楚,以爲我也有同感。

簡短的回答是,你必須創建一個套接字,創建一個Paramiko客戶端傳輸,調用auth_interactive,然後打開一個會話。會話提供了一個Paramiko Channel對象,它可以像SSHClient對象一樣執行exec_command調用。

下面的代碼是全參考實現:

 
#!/usr/bin/env python

import paramiko #Provides SSH functionality import getpass #Allows for secure prompting and collection of the user password import os #Used to setup the Paramiko log file import logging #Used to setup the Paramiko log file import socket #This method requires that we create our own socket #Global variables are used to store these data because they're sent to the server by a callback user = "" pw = "" mfa = "" def inter_handler(title, instructions, prompt_list): """ inter_handler: the callback for paramiko.transport.auth_interactive The prototype for this function is defined by Paramiko, so all of the arguments need to be there, even though we don't use 'title' or 'instructions'. The function is expected to return a tuple of data containing the responses to the provided prompts. Experimental results suggests that there will be one call of this function per prompt, but the mechanism allows for multiple prompts to be sent at once, so it's best to assume that that can happen. Since tuples can't really be built on the fly, the responses are collected in a list which is then converted to a tuple when it's time to return a value. Experiments suggest that the username prompt never happens. This makes sense, but the Username prompt is included here just in case. """ resp = [] #Initialize the response container #Walk the list of prompts that the server sent that we need to answer for pr in prompt_list: #str() used to to make sure that we're dealing with a string rather than a unicode string #strip() used to get rid of any padding spaces sent by the server if str(pr[0]).strip() == "Username:": resp.append(user) elif str(pr[0]).strip() == "Password:": resp.append(pw) elif str(pr[0]).strip() == "OTP Code:": resp.append(mfa) return tuple(resp) #Convert the response list to a tuple and return it #Main Entry Point if __name__ == "__main__": #Setup Paramiko logging; this is useful for troubleshooting paramiko.util.log_to_file(os.path.expanduser('~/paramiko.log'), logging.DEBUG) #Get the username, password, and MFA token code from the user user = raw_input("Username: ") pw = getpass.getpass("Password: ") mfa = raw_input("OTP Code: ") #Create a socket and connect it to port 22 on the host sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(("mfahost.example.com", 22)) #Make a Paramiko Transport object using the socket ts = paramiko.Transport(sock) #Tell Paramiko that the Transport is going to be used as a client ts.start_client(timeout=10) #Begin authentication; note that the username and callback are passed ts.auth_interactive(user, inter_handler) #Opening a session creates a channel along the socket to the server chan = ts.open_session(timeout=10) #Now the channel can be used to execute commands chan.exec_command("touch ~/paramikotestfile")