2017-05-10 10 views
0

Paramiko (Python 2.7)를 사용하여 다중 요소 인증 (username + password + one-time-password)을 사용하는 호스트에 연결하려고합니다. transport.auth_interactive 함수는 (문서에서 이해하고있는 것을 기반으로) 이것을 수행하는 방법 인 것처럼 보이지만 실행은 결코 그 시점에 이르지 못합니다. 인증은 client.connect 행에서 실패합니다.Python Paramiko (클라이언트) Multifactor 인증

뭔가 빠져있는 것 같습니다.

#!/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를 호출 한 다음 세션을 열어야한다는 것입니다. 세션은 SSHClient 객체처럼 exec_command 호출을 취할 수있는 Paramiko Channel 객체를 제공합니다.

아래 코드 전체 참조 구현이다

 
#!/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")