ssh via python subprocess.Popen: terminate if pass

2019-07-26 23:05发布

问题:

I'm using a python script to manage ssh fingerprint problems after a workstation(s) is reimaged.

I attempt to connect with ssh, and if I get a any warnings I deal with them.

However, if there are no errors, then I am asked for a password to connect. At this point I want to terminate the process. However, the script hangs on the password request.

Here's the method:

def ssh_fingerprint_changed(node):
    """
    Checks if a node's ssh fingerprint has changed or an old key is found, which can occur when a node is reimaged.
    It does this by attempting to connect via ssh and inspecting stdout for an error message.
    :param node: the ip or hostname of the node
    :return: True if the node's fingerprint doesn't match the client's records. Else False.
    """
    changed = False
    cmd = ["ssh", "-q", ADMIN_USER + "@" + node, "exit"]
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
    print("Checking for fingerprint changes")

    for line in proc.stdout:  # loop on lines
        print("in for loop") # NEVER REACHES HERE IF NO ERRORS, WAITING FOR PASSWORD
        if b"Offending key" in line:
            print("Offending key found.")
            proc.stdin.write(b"no\n")   # don't connect
            changed = True
        elif b"REMOTE HOST IDENTIFICATION HAS CHANGED!" in line:
            print("REMOTE HOST IDENTIFICATION HAS CHANGED!")
            changed = True

    print(changed) # NEVER REACHES HERE IF NO ERRORS, WAITING FOR PASSWORD
    if not changed:  # then everything's good, but it will be waiting for a password to connect
        print("Good to go, terminating ssh test.")
        rc = proc.terminate()
    else:
        rc = proc.wait()

    return changed

If I run this from the terminal ./my_python_script.py, I have the problems. Oddly, if I run in PyCharm, it doesn't hang on the password request and terminates shh, continuing with the script as expected.

回答1:

The easy answer is simply to tell ssh that you don't want to support password authentication at all; you'll still get the messages you want if the host key is changed, but you won't ever have the process hanging waiting for a password to be entered.

cmd = ['ssh',
       '-o', 'PasswordAuthentication no',  ### <-- THIS LINE HERE
       '-o', 'StrictHostKeyChecking yes',  ### also, never modify known_hosts
       '-q',
       '%s@%s' % (ADMIN_USER, + node),
       'exit']

If you did not want to process other prompts, I would suggest setting stdin=subprocess.DEVNULL (in Python 3) or passing the -n argument to ssh to prevent stdin from being passed to the process at all.