aaronkollasch/jupyter-o2

Fix interactive 2FA

aaronkollasch opened this issue · 2 comments

Running jupyter-o2 with --2fa produces the following error:

Connecting to <user>@o2.hms.harvard.edu
RUN: ssh <user>@o2.hms.harvard.edu
Traceback (most recent call last):
  File "anaconda/lib/python3.7/site-packages/pexpect/expect.py", line 111, in expect_loop
    incoming = spawn.read_nonblocking(spawn.maxread, timeout)
  File "anaconda/lib/python3.7/site-packages/pexpect/pty_spawn.py", line 485, in read_nonblocking
    return super(spawn, self).read_nonblocking(size)
  File "anaconda/lib/python3.7/site-packages/pexpect/spawnbase.py", line 176, in read_nonblocking
    raise EOF('End Of File (EOF). Empty string style platform.')
pexpect.exceptions.EOF: End Of File (EOF). Empty string style platform.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "anaconda/bin/jupyter-o2", line 10, in <module>
    sys.exit(main())
  File "anaconda/lib/python3.7/site-packages/jupyter_o2/jupyter_o2.py", line 649, in main
    jupyter_o2_runner.run()
  File "anaconda/lib/python3.7/site-packages/jupyter_o2/jupyter_o2.py", line 348, in run
    if self.connect() or self.keep_alive:
  File "anaconda/lib/python3.7/site-packages/jupyter_o2/jupyter_o2.py", line 374, in connect
    if not self._login_ssh.login_2fa(self.host, self.user, self.__pass, self.codes_2fa):
  File "anaconda/lib/python3.7/site-packages/jupyter_o2/jupyter_o2.py", line 84, in login_2fa
    self.set_unique_prompt()
  File "anaconda/lib/python3.7/site-packages/pexpect/pxssh.py", line 491, in set_unique_prompt
    i = self.expect ([TIMEOUT, self.PROMPT], timeout=10)
  File "anaconda/lib/python3.7/site-packages/pexpect/spawnbase.py", line 341, in expect
    timeout, searchwindowsize, async_)
  File "anaconda/lib/python3.7/site-packages/pexpect/spawnbase.py", line 369, in expect_list
    return exp.expect_loop(timeout)
  File "anaconda/lib/python3.7/site-packages/pexpect/expect.py", line 117, in expect_loop
    return self.eof(e)
  File "anaconda/lib/python3.7/site-packages/pexpect/expect.py", line 63, in eof
    raise EOF(msg)
pexpect.exceptions.EOF: End Of File (EOF). Empty string style platform.
<jupyter_o2.jupyter_o2.CustomSSH object at 0x10749acf8>
command: /usr/bin/ssh
args: ['/usr/bin/ssh', '-o', 'ForwardX11=yes', '-o', 'LocalForward=8887 127.0.0.1:8887', '-o', 'PubkeyAuthentication=no', '-q', '-oRSAAuthentication=no', '-o', 'PubkeyAuthentication=no', '-l', '<user>', 'o2.hms.harvard.edu']
buffer (last 100 chars): b''
before (last 100 chars): b'S passcodes to XXX-XXX-XXXX\r\n\r\nPasscode or option (1-3): \r\nIncorrect passcode. Please try again.\r\n\r\n'
after: <class 'pexpect.exceptions.EOF'>
match: None
match_index: None
exitstatus: None
flag_eof: True
pid: 66259
child_fd: 8
closed: False
timeout: 60
delimiter: <class 'pexpect.exceptions.EOF'>
logfile: None
logfile_read: None
logfile_send: None
maxread: 2000
ignorecase: False
searchwindowsize: None
delaybeforesend: 0.05
delayafterclose: 0.1
delayafterterminate: 0.1
searcher: searcher_re:
    0: TIMEOUT
    1: re.compile(b'\\[PEXPECT\\][\\$\\#] ')
Cleaning up
Closing login_ssh

Non-interactive 2FA still works, so the current workaround is to use the arguments --2fa --2fa-code 1 to request Duo pushes automatically.

While an interactive 2FA prompt may be more versatile for different Duo authentication methods, the current approach (automatically send code 1) is more convenient as the 2FA dialog does not need to be navigated twice.
It is also more stable as it doesn't require a workaround for pexpect to allow programmatic exit from the interact() mode.
Thus the interactive mode fix is not a priority.