py-junos-eznc icon indicating copy to clipboard operation
py-junos-eznc copied to clipboard

ConnectAuthError when trying to connect to SRX with SSH private key with passphrase and SSH-agent

Open Raimond56 opened this issue 1 year ago • 11 comments

Hello,

I need some help to troubleshoot why connecting with SSH key with passphrase is not working with SSH agent. If I understand correctly if I have setup access to SRX with SSH keys and using SSH agent then junos-eznc should be able to read that information and connect to device.

Connection to SRX 4200 with version Junos: 21.4R3-S4.9 From ubuntu 22.04 with python 3.10 and junos-eznc 2.7.1

This is what I have in my .ssh/config file:

host 10.35.255.1
        IdentityFile ~/.ssh/privatekey
host *
        KexAlgorithms=+"diffie-hellman-group1-sha1,diffie-hellman-group14-sha1"
        PubkeyAcceptedAlgorithms=+"ssh-rsa"
        HostKeyAlgorithms=+"ssh-rsa"
        Ciphers=+"aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc"
        ServerAliveInterval=300
        ServerAliveCountMax=2
AddKeysToAgent yes

If I connect from command line it doesn't ask password and connects

rilves@raimondadmin:~$ ssh 10.35.255.1 -p 830 -s netconf
<!-- No zombies were killed during the creation of this user interface -->
<!-- user rilves, class j-super-user-local -->
<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">

If I try with the following python code

from pprint import pprint
from jnpr.junos import Device

with Device(host='10.35.255.1', user='rilves') as dev:
    pprint( dev.facts )

I get following error:

rilves@raimondadmin:~$ python3 test.py
Traceback (most recent call last):
  File "/home/rilves/.local/lib/python3.10/site-packages/jnpr/junos/device.py", line 1371, in open
    self._conn = netconf_ssh.connect(
  File "/home/rilves/.local/lib/python3.10/site-packages/ncclient/manager.py", line 187, in connect
    return connect_ssh(*args, **kwds)
  File "/home/rilves/.local/lib/python3.10/site-packages/ncclient/manager.py", line 139, in connect_ssh
    session.connect(*args, **kwds)
  File "/home/rilves/.local/lib/python3.10/site-packages/ncclient/transport/ssh.py", line 359, in connect
    self._auth(username, password, key_filenames, allow_agent, look_for_keys)
  File "/home/rilves/.local/lib/python3.10/site-packages/ncclient/transport/ssh.py", line 494, in _auth
    raise AuthenticationError(repr(saved_exception))
ncclient.transport.errors.AuthenticationError: SSHException('encountered EC key, expected OPENSSH key')

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/rilves/test.py", line 5, in <module>
    with Device(host='10.35.255.1', user='rilves') as dev:
  File "/home/rilves/.local/lib/python3.10/site-packages/jnpr/junos/device.py", line 1476, in __enter__
    self.open()
  File "/home/rilves/.local/lib/python3.10/site-packages/jnpr/junos/device.py", line 1392, in open
    raise EzErrors.ConnectAuthError(self)
jnpr.junos.exception.ConnectAuthError: ConnectAuthError(10.35.255.1)

Everything works if I provide "passwd: " in the python script aswell. But would like to have key authentication. Do I need to create OPENSSH key or could this be any other error and it just not getting my key for some reason ?

Thanks for help!

Raimond56 avatar Dec 18 '24 09:12 Raimond56

Alright I did some more reading and checked out the first error line code "device.py" And from that file I found this

            # we want to enable the ssh-agent if-and-only-if we are
            # not given a password or an ssh key file.
            # in this condition it means we want to query the agent
            # for available ssh keys

            allow_agent = bool(
                (self._auth_password is None) and (self._ssh_private_key_file is None)
            )

It seems that SSH agent only works when you don't give SSH key file and password defined anywhere. So I tested and removed my .ssh/config file and then the python code works and connects to device.

Is this expected behavior? Is it possible to set this in python to script to still have .ssh/config file the same? Also if the key password is not yet in the SSH agent after new session it would also break. Shouldn't it still check the SSH key from .ssh/config and then ask for password if not in SSH agent already ?

Raimond56 avatar Dec 18 '24 09:12 Raimond56

I am using an SSH key with a passphrase which is cached by my local SSH agent. I was getting something similar, errors like this:

Traceback (most recent call last):
  File "/home/myuser/.local/lib/python3.10/site-packages/jnpr/junos/device.py", line 1390, in open
    self._conn = netconf_ssh.connect(
  File "/home/myuser/.local/lib/python3.10/site-packages/ncclient/manager.py", line 187, in connect
    return connect_ssh(*args, **kwds)
  File "/home/myuser/.local/lib/python3.10/site-packages/ncclient/manager.py", line 139, in connect_ssh
    session.connect(*args, **kwds)
  File "/home/myuser/.local/lib/python3.10/site-packages/ncclient/transport/ssh.py", line 359, in connect
    self._auth(username, password, key_filenames, allow_agent, look_for_keys)
  File "/home/myuser/.local/lib/python3.10/site-packages/ncclient/transport/ssh.py", line 494, in _auth
    raise AuthenticationError(repr(saved_exception))
ncclient.transport.errors.AuthenticationError: PasswordRequiredException('private key file is encrypted')

I was opening the device as follows:

device = Device(dev_name, username=os.getlogin(), ssh_config=args.sshconfig, port=22)

The ssh config file I was passing is very basic:

Host *
    User myusername
    IdentityFile ~/.ssh/id_ed25519
    ProxyCommand /usr/bin/nc -X 5 -x localhost:10801 %h %p

If I manually hack my local device.py file and set allow_agent = True then everything works ok.

If I leave device.py as it is and remove the 'IdentityFile' line from the ssh config file it still fails.

I tried adding ssh_private_key_file=None and passwd=None when creating the device object, so allow_agent would naturally evaluate to true, but that didn't work for some reason.

topranks avatar Feb 20 '25 19:02 topranks

I modified device.py file and removed private key file check from allow_agent and then everything works fine when key is still passed in .ssh/config file So I changed this:

            allow_agent = bool(
                (self._auth_password is None) and (self._ssh_private_key_file is None)
            )

to this:

            allow_agent = bool(
                (self._auth_password is None)
            )

Raimond56 avatar Feb 21 '25 14:02 Raimond56

I modified device.py file and removed private key file check from allow_agent and then everything works fine when key is still passed in .ssh/config file So I changed this:

            allow_agent = bool(
                (self._auth_password is None) and (self._ssh_private_key_file is None)
            )

to this:

            allow_agent = bool(
                (self._auth_password is None)
            )

Hi @Raimond56, Thanks for sharing the information. let me replicate this issue on our lab setup and verify.

Thanks Chidanand

chidanandpujar avatar Jun 27 '25 04:06 chidanandpujar

Hi @Raimond56,

 Thanks for providing the details.
 I am not able to replicate this issue with latest PyEZ code (pip install git+https://github.com/Juniper/py-junos-eznc.git).

Could you please check and let us know, if we are missing any steps.
eval "$(ssh-agent -s)"

ssh-keygen -t rsa -b 4096 

ssh-add ~/.ssh/id_rsa

ssh-copy-id -i ~/.ssh/id_rsa.pub [email protected]

vi ~/.ssh/config

host xx.xx.xx.xx
        IdentityFile ~/.ssh/id_rsa
host *
        KexAlgorithms=+"diffie-hellman-group1-sha1,diffie-hellman-group14-sha1"
        HostKeyAlgorithms=+"ssh-rsa"
        Ciphers=+"aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc"
        ServerAliveInterval=300
        ServerAliveCountMax=2
AddKeysToAgent yes



PyEZ script:

from  pprint import pprint
from jnpr.junos import Device

with Device(host='xx.xx.xx.xx', user='regress') as dev:
        pprint( dev.facts )


(venv) regress@masterhost:~/pyez_issues$ python test.py 
{'2RE': True,
 'HOME': '/var/home/regress',
 'RE0': {'last_reboot_reason': 'Router rebooted after a normal shutdown.',
         'mastership_state': 'master',
         'model': 'RE-VMX',
         'status': 'OK',
         'up_time': '9 days, 16 hours, 41 minutes, 58 seconds'},
 'RE1': {'last_reboot_reason': None,
         'mastership_state': 'Present',
         'model': None,
         'status': None,
         'up_time': None},

Thanks & Regards Chidanand

chidanandpujar avatar Jul 10 '25 08:07 chidanandpujar

Hi @chidanandpujar thank you for looking at this issue!

I have set up a fresh Debian 12 VM to test this. The regular ssh agent is loaded on boot:

debian@debian12:~$ ssh-agent -s 
SSH_AUTH_SOCK=/tmp/ssh-IuNk6wdCTHQ2/agent.3364; export SSH_AUTH_SOCK;
SSH_AGENT_PID=3365; export SSH_AGENT_PID;
echo Agent pid 3365;
debian@debian12:~$ 
debian@debian12:~$ eval "$(ssh-agent -s)"
Agent pid 3381

My ssh config file is set to connect to one of our routers via a socks5 proxy running on another host on my network:

debian@debian12:~$ cat ./.ssh/config 
Host cr1-esams.wikimedia.org
    User cmooney
    IdentityFile ~/.ssh/id_ed25519
    ProxyCommand /usr/bin/nc -X 5 -x 192.168.240.106:10801 %h %p

I can add my ed25519 ssh key to the local agent with 'ssh-add':

debian@debian12:~$ ssh-add ./.ssh/id_ed25519 
Enter passphrase for ./.ssh/id_ed25519: 
Identity added: ./.ssh/id_ed25519 (cathal@wikilap)
debian@debian12:~$ 

After this I can ssh to the router fine, it picks up the key from the ssh agent, and proxies as required based on the config file:

debian@debian12:~$ ssh cr1-esams.wikimedia.org 
Last login: Thu Jul 17 11:02:52 2025 from 2a02:ec80:300:2:185:15:59:36
--- JUNOS 23.4R2-S3.9 Kernel 64-bit  JNPR-12.1-20240604.39c9257_buil
{master}
[email protected]>

I have the latest PyEz installed from the url you posted above, and have this Python file:

debian@debian12:~$ cat test_pyez.py 
#!/usr/bin/python3

from jnpr.junos import Device
from pprintpp import pprint as pp

junos_dev = Device('cr1-esams.wikimedia.org', username='cmooney', ssh_config='/home/debian/.ssh/config', port=22)
junos_dev.open()
pp(junos_dev.facts)

When I run this I get an error saying the private key file is encrypted:

debian@debian12:~$ ./test_pyez.py 
Traceback (most recent call last):
  File "/home/debian/.local/lib/python3.11/site-packages/jnpr/junos/device.py", line 1390, in open
    self._conn = netconf_ssh.connect(
                 ^^^^^^^^^^^^^^^^^^^^
  File "/home/debian/.local/lib/python3.11/site-packages/ncclient/manager.py", line 187, in connect
    return connect_ssh(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/debian/.local/lib/python3.11/site-packages/ncclient/manager.py", line 139, in connect_ssh
    session.connect(*args, **kwds)
  File "/home/debian/.local/lib/python3.11/site-packages/ncclient/transport/ssh.py", line 359, in connect
    self._auth(username, password, key_filenames, allow_agent, look_for_keys)
  File "/home/debian/.local/lib/python3.11/site-packages/ncclient/transport/ssh.py", line 494, in _auth
    raise AuthenticationError(repr(saved_exception))
ncclient.transport.errors.AuthenticationError: PasswordRequiredException('Private key file is encrypted')

If I modify the 'allow_agent' variable on this line so it simply says allow_agent = True then the connection works:

debian@debian12:~$ ./test_pyez.py 
{'2RE': True,
 'HOME': '/var/home/cmooney',
 'RE0': {'last_reboot_reason': '0x2000:hypervisor reboot',
         'mastership_state': 'master',
         'model': 'RE-S-2X00x6',
         'status': 'OK',
         'up_time': '64 days, 2 hours, 2 minutes, 18 seconds'},

topranks avatar Jul 17 '25 11:07 topranks

@chidanandpujar actually looking at the code and the setup I tried something else. I removed this line from my ~/.ssh/config file:

IdentityFile ~/.ssh/id_ed25519

And then with the unmodified device.py file the connection actually worked. So I think the and (self._ssh_private_key_file is None) part of that conditional is the problem.

On my normal system I have many SSH keys for access to different systems though. So I need the IdentityFile line in my ssh config to tell ssh what key to use for the given device, but I need it to be loaded from the agent then as it has a passphrase.

topranks avatar Jul 17 '25 11:07 topranks

Hi @topranks , Thanks for the sharing the details. Let me replicate this issue on test setup and verify the fix mentioned.

Thanks Chidanand

chidanandpujar avatar Jul 17 '25 15:07 chidanandpujar

Thanks! In terms of the correct fix I'm not sure what is best. Perhaps another option when connecting to the device could be added like "force_agent" or something, which if set would always use the agent, even if self._ssh_private_key_file is not null?

topranks avatar Jul 17 '25 23:07 topranks

Hey @chidanandpujar thanks for looking into this

Did you have passphrase configured for your private key aswell?

I tested again in clean python3 venv I run pip install git+https://github.com/Juniper/py-junos-eznc.git Then run the script and it results in same error.

I run the same script as before

from pprint import pprint
from jnpr.junos import Device

with Device(host='10.35.255.1', user='rilves') as dev:
    pprint( dev.facts )

And I have this in .ssh/config file:

host 10.35.255.*
       IdentityFile ~/.ssh/privatekey
host *
        KexAlgorithms=+"diffie-hellman-group1-sha1,diffie-hellman-group14-sha1"
        PubkeyAcceptedAlgorithms=+"ssh-rsa"
        HostKeyAlgorithms=+"ssh-rsa"
        Ciphers=+"aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc"
        ServerAliveInterval=300
        ServerAliveCountMax=2
AddKeysToAgent yes

If I remove IdentifyFile from .ssh/config file the script works. But if it is there it does not.

@topranks Thanks for testing aswell! As topranks said I am also not sure what the best fix could be, but for my deployment currently works if I remove and (self._ssh_private_key_file is None) from device.py.

Raimond56 avatar Jul 18 '25 06:07 Raimond56

Hi @Raimond56, Thanks for sharing the information.

Thanks & Regards Chidanand

chidanandpujar avatar Jul 18 '25 10:07 chidanandpujar