I want to reply to server acknowledgement using asycnssh #672
Replies: 15 comments 1 reply
-
This is very likely to be possible, but the details of how to do it will depend on how the server implemented this extra prompt. My guess at this point is that they are using custom keyboard-interactive authentication to actually send the prompt and get back a response. The banner could be done as part of this, or it could be done using the separate "auth banner" feature which can be sent prior to auth completing, and it could then be followed up with the keyboard-interactive auth to print the question and collect the Y/N result. The first thing you'll need here is your own custom subclass of SSHClient, so you can implement auth-related callbacks. You'll also need to use If I'm right about the server using keyboard-interactive auth, you'll need your SSHClient subclass to implement the callbacks More specifically, if there's only a single prompt here, it's likely that the "Do you acknowledge? (Y/N)?" would be the first (and only) prompt string, and the banner text ahead of that could be in either an auth banner message or be in the "instructions" argument here. In your case, you could simply have Assuming this auth succeeds, the server would then move on to some other form of auth like publickey or password. However, it's also possible it code use additional prompts in Very roughly speaking, you should end up with something like: class MySSHClient(asyncssh.SSHClient):
def kbdint_auth_requested(self):
return ''
def kbdint_challenge_received(name, instructions, lang, prompts)
return ['Y']
def auth_completed(self):
print('Authentication successful.')
async def run_client():
conn, client = await asyncssh.create_connection(MySSHClient, 'host')
async with conn:
result = await conn.run('ls abc', check=True)
print(result.stdout, end='')
try:
asyncio.get_event_loop().run_until_complete(run_client())
except (OSError, asyncssh.Error) as exc:
sys.exit('SSH connection failed: ' + str(exc)) A real version of this would want to validate that there was only one prompt in kbdint_challenge_received and that the prompt text was what you were expecting before sending back the |
Beta Was this translation helpful? Give feedback.
-
@ronf Actually my server has this kind of flow when I do normal ssh. after giving username After this I get this banner I tried with this code ` Specify SSH client connection optionsoptions = SSHClientConnectionOptions( class MySSHClient(asyncssh.SSHClient):
async def run_client():
try: These are the logs 2024-07-17 09:29:21,523 - DEBUG - [conn=0] Sending version SSH-2.0-AsyncSSH_2.15.0 |
Beta Was this translation helpful? Give feedback.
-
It looks like it is getting into keyboard-interactive auth here. Since you aren't returning anything from kbdint_challenge_received though, it isn't authenticating you. If you print out name, instructions, lang, and prompts, what do you see? My guess is that you'll see 3 prompts, for "Username", "Password", and "Do you acknowledge (Y/N)?". If that's the case, try returning a list of 3 strings, providing values for each of those prompts. You shouldn't need the password callbacks in this case. You may or may not need the auth banner callback depending on whether the server is using that or using something like the instructions argument in the kbdint callback to deliver the banner. |
Beta Was this translation helpful? Give feedback.
-
Actually it not going in kbdint_challenge_received callback it running kbdint_auth_requested call 4 times and printing 4 times"auth requested". I tried adding print statements in kbdint_challenge_received but as it is not going in the callback no pompts are getting printed. |
Beta Was this translation helpful? Give feedback.
-
It looks like you are returning a single space in the kbdint_auth_requested() rather than an empty string. If you change it to empty string, does that help? |
Beta Was this translation helpful? Give feedback.
-
No I tried both earlier but none works. |
Beta Was this translation helpful? Give feedback.
-
Returning Actually one thing I noticed is that the username is set to "admin" here, but in your OpenSSH client you were passing in a username of "cli". Try setting username (rather than client_username, which is only used for host-based authentication) to "cli" and see if that helps. When you log in with an OpenSSH client with "-vvv", what do you see at the tail end of the log output there (after "debug3: receive packet: type 51")? With a regular SSH server, I see something like:
When you log into this target server, what do you see? |
Beta Was this translation helpful? Give feedback.
-
I tried putting cli in username then it directly goes to auth_completed callback and prints authentication successful. |
Beta Was this translation helpful? Give feedback.
-
Based on these Openssh client logs I think I will have to use some kind of shell or pty functionality receive data and send username and password back. ebug1: update_known_hosts: known hosts file /.ssh/known_hosts2 does not exist Username: admin |
Beta Was this translation helpful? Give feedback.
-
Ok - now that you have the SSH username right ("cli"), it looks like this is using the second approach to prompting for things at logon. It lets the SSH-level user authentication succeed immediately, but after that it starts up a new SSH session which the server can use to write output to and read input from the client. As you said, it appears to be also asking for a pseudo-TTY to be created for this session, and since you didn't specify a command to run in your OpenSSH client, the client just asked for an interactive shell to be started. Given that this is the case, you probably won't need the custom SSHClient subclass, and can go back to use See what output you get if you do something like: async with asyncssh.connect(host, username='cli') as conn:
async with conn.create_process(term_type='xterm-color') as proc:
print(await proc.stdout.read(8192)) I'm expecting this to output a banner and the "Username" prompt. If the Username prompt doesn't show up, you may need to read multiple times in a loop until it does. You could then follow this with something like: proc.stdin.write('admin\n') This would write the username, and then you'll want to go back into a read loop until you get a "Password" prompt. You'd then do a new proc.stdin.write() with the password, and repeat the process again for the Y/N acknowledgement. After that, I'd expect some additional output and then maybe something like a CLI prompt, where you can continue to alternate between reading & writing as above. |
Beta Was this translation helpful? Give feedback.
-
@ronf That worked for me. Thanks. I have this working code right now.
Run the main functionif name == 'main': After this I want to send a large packet as well to test it drops it or not I tried creating session and sending large packet with send_packet method but the current code just logs into server and not run the large packet sending method. This is my latest code . async def send_large_packet(chan): async def main():
Run the main functionif name == 'main': I am still trying to check what can be done. |
Beta Was this translation helpful? Give feedback.
-
You really shouldn't be calling send_packet() directly, unless your goal is to violate the protocol. This large "packet" would do so for multiple reasons, as it exceeds the max allowed packet size by most servers and it also exceeds what most servers advertise as a receive window. As such, most servers will probably end up closing the connection on you when you send such a packet. Also, it's dangerous to count on the data you read always containing the entirety of the prompt you're looking for. It would be possible that something like 'Username' could appear as 'User' at the end of one read() from stdout and then a second read() might start with 'name'. Your current code doesn't attempt to handle this. Creating a separate channel for the large packet may be a problem as well. Many embedded SSH implementations don't support multiple channels being opened on a single connection, so the call to |
Beta Was this translation helpful? Give feedback.
-
Actually I am testing this scenario FCS_SSHC_EXT.1.3The TSF shall ensure that, as described in RFC 4253, I am able to login with current code in to server. I don't get any issues with prompts coming from server. It will always ask for 'Username' first then "Password" then banner for Y/N selection. The current code works fine and I logged in to server but the send_packet functionality doesn't work. Can you help me with this how can I send the large packet without creating separate channel? |
Beta Was this translation helpful? Give feedback.
-
Ah, right - we talked a bit about this a few months ago in #650. If you want to send the large packet on the existing session rather than creating a new one, you should be able to skip the Regarding the other prompts, I expect it will work much of the time as you have it now, but consider the case where there's something like a banner which is sent separately from any of the prompts, such that they end up in different read() results. This may cause the code to fall into the "else: break" case, before it is done actually answering all of the prompts. As I mentioned above, you could also run into a case where the prompt is split across read results, such that just testing "in output" won't find a match. You'd have to collect up the result of multiple read calls until you see the next prompt you're looking for. After that you write the response to the prompt, clear the output buffer, and begin again looking for the next prompt, repeating this until all prompts are answered. Then, you could do your send of the large packet on |
Beta Was this translation helpful? Give feedback.
-
Thankyou so much @ronf for helping me out. |
Beta Was this translation helpful? Give feedback.
-
Hi,
I want to connect to server via ssh and after connecting with password I get this banner in return After I put Y I can log into server
Do you acknowledge? (Y/N)?y
Alarm Status: Critical-1 Major-0 Minor-8 Warning-0
Can this be done in asyncssh?
Beta Was this translation helpful? Give feedback.
All reactions