akarneliuk / pygnmi

The pure Python implementation of the gNMI client.
https://training.karneliuk.com
BSD 3-Clause "New" or "Revised" License
129 stars 44 forks source link

Problems with SSL certificates with missing common name and alternative name #71

Closed gunhanoral closed 2 years ago

gunhanoral commented 2 years ago

If the common name is empty for the certificate, pygnmi fails due to this line.

>>> ssl_cert_deserialized
<Certificate(subject=<Name(<<redacted>>)>, ...)>
>>> ssl_cert_deserialized.subject.get_attributes_for_oid(x509.oid.NameOID.COMMON_NAME)
[]

I bypassed this using an if-else block, manually setting ssl_target_name_override. But this time pygnmi failed due to alternative name.

>>> ssl_cert_subject_alt_names = ssl_cert_deserialized.extensions.get_extension_for_oid(x509.oid.ExtensionOID.SUBJECT_ALTERNATIVE_NAME)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/redacted/redacted/.virtualenvs/redacted/lib/python3.8/site-packages/cryptography/x509/extensions.py", line 125, in get_extension_for_oid
    raise ExtensionNotFound("No {} extension was found".format(oid), oid)
cryptography.x509.extensions.ExtensionNotFound: No <ObjectIdentifier(oid=2.5.29.17, name=subjectAltName)> extension was found

I tried this with a second certificate we use on some other devices and it failed with the same error.

I'm not sure what is missing here but I am using the same certificates with other gnmi tools in production so I don't think there's something wrong with them.

akarneliuk commented 2 years ago

hey @gunhanoral ,

cam you please post content of your certificate here (CN and Subject Alternative Names)?

Best, Anton

gunhanoral commented 2 years ago

Hello @akarneliuk There's no SAN in the certificates we use. And commonName is empty "commonName": ""

I don't think I can share any more information, sorry.

akarneliuk commented 2 years ago

Hey @gunhanoral ,

I tried to create certificate without CN, but I'm getting errors:

#security pki certificate generate self-signed empty.pem key gnmi_api.key 
Common Name for use in subject: 
% Error generating certificate (Common Name is needed)

So I tried to create such:

dev-pygnmi-eos-001#show management security ssl certificate empty.pem 
Certificate empty.pem:
   Version:                   3
   Serial Number:             96fede194f849f23
   Issuer:                    
      Common name:            ""
   Validity:                  
      Not before:             Jul 14 17:46:19 2022 GMT
      Not After:              Jul 14 17:46:19 2023 GMT
   Subject:                   
      Common name:            ""
   Subject public key info:   
      Encryption Algorithm:   RSA
      Size:                   4096 bits
      Public exponent:        65537
      Modulus:                8fb49a08c950216e0e38b31b5199683e367a5c81586caf1c1b14040
                              ebb0b931f93a77fc42547dd044f056699af344c2db40639728c1902
                              7cfe2cdde36a8ae04c1a7863ae92ed7cba710d2b27a20e0bee697d9
                              b2672df2046877e0f5c8c8e8b0adac54008547ce44d00d750deae2d
                              c3005858ecf6b0cca2d10d88cffa63937964bf6da0701cdccf99b9f
                              decec67757265ced02cc19931327c764d6f9d85598a645162cebaba
                              51bb3a309b902e3fc4ed097355636f2124c0744f5c76f51021aa4f6
                              67c8edce039f24a6432e94c9f2eb81a4b09dd119a3de7d279007f61
                              8b03d6d70a1fbc9332fa5bb4e96d1d7efe283db01687c221408e5b7
                              0969894769f142c7e3a10ea3dab9800bd04f761c70495dddc36922f
                              dc8ca588594e9b826050f31e95e13f3e334fa976a9efe74abd732fb
                              b44360580a35ad3c661377ce7a06694a36238a156ad8dd094342e35
                              832fe558d1cec4bfddb253facda639ec24a3a1f6edcb36c4dcde9bd
                              f874720ffddbe074c9679234e27bed77af1d223f2098bc29086b423
                              32bf7f1e720b5c563c7acf06fff3e74453518fed092ff80dacd8ccc
                              ca9d465621b57aa4469cc3cf8e62fefe0ec5237c95151aaa6d05ea7
                              83a82b4ed321b923f82ed7e9564759a24cf7a14f41ebe04fd948137
                              6302831b576b952454f167850f77037a23880306d139d63a6f76c34
                              69980054f00a8bd8db28c4465e4a58a1dd

However, here we still have CN, which value is "", so it is NOT empty. I will try to work it out it and let you know my results, but so far I'm a bit puzzled how your solution works.

Best, Anton

akarneliuk commented 2 years ago

Hey @gunhanoral ,

Please, try the newest version 0.7.4, that shall be working for you, if my assumption above was accurate.

Best, Anton

gunhanoral commented 2 years ago

Hello @akarneliuk sorry I can't confirm if this solves the issue. I don't get any more errors due to common name or san but I can't connect to the device using pygnmi. At this point I'm not sure if this is related to this issue. Maybe I'm mixing up certificates?

This is the script that I'm running:

# Modules
from pygnmi.client import gNMIclient
from getpass import getpass
import os

# Variables
host = ('redacted', 'redacted')
ssl = {
    'path_root': os.path.join(os.getenv('SSLDIR'), 'certs', 'ca.crt'),
    'path_key': os.path.join(os.getenv('SSLDIR'), 'keys', 'client.key'),
    'path_cert': os.path.join(os.getenv('SSLDIR'), 'certs', 'client.crt')
}

# Body
if __name__ == '__main__':
    with gNMIclient(target=host, username=os.getenv('USER'), password=getpass(), skip_verify=True, **ssl) as gc:
         result = gc.get(path=['/interfaces/interface[name=Ethernet1]/state'])

    print(result)

Output:

❯ python3 /tmp/test.py
Password:
Cannot get Common Name: list index out of range
Cannot get Subject Alternative Names: No <ObjectIdentifier(oid=2.5.29.17, name=subjectAltName)> extension was found
ssl_target_name_override is applied, should be used for testing only!
ERROR:root:Failed to setup gRPC channel, trying change cipher
Traceback (most recent call last):
  File "/Users/redacted/.virtualenvs/redacted/lib/python3.8/site-packages/pygnmi/client.py", line 223, in wait_for_connect
    grpc.channel_ready_future(self.__channel).result(timeout=timeout)
  File "/Users/redacted/.virtualenvs/redacted/lib/python3.8/site-packages/grpc/_utilities.py", line 139, in result
    self._block(timeout)
  File "/Users/redacted/.virtualenvs/redacted/lib/python3.8/site-packages/grpc/_utilities.py", line 85, in _block
    raise grpc.FutureTimeoutError()
grpc.FutureTimeoutError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/tmp/test.py", line 16, in <module>
    with gNMIclient(target=host, username=os.getenv('USER'), password=getpass(), skip_verify=True, **ssl) as gc:
  File "/Users/redacted/.virtualenvs/redacted/lib/python3.8/site-packages/pygnmi/client.py", line 99, in __enter__
    return self.connect()
  File "/Users/redacted/.virtualenvs/redacted/lib/python3.8/site-packages/pygnmi/client.py", line 213, in connect
    self.wait_for_connect(timeout)
  File "/Users/redacted/.virtualenvs/redacted/lib/python3.8/site-packages/pygnmi/client.py", line 230, in wait_for_connect
    grpc.channel_ready_future(self.__channel).result(timeout=timeout)
  File "/Users/redacted/.virtualenvs/redacted/lib/python3.8/site-packages/grpc/_utilities.py", line 139, in result
    self._block(timeout)
  File "/Users/redacted/.virtualenvs/redacted/lib/python3.8/site-packages/grpc/_utilities.py", line 85, in _block
    raise grpc.FutureTimeoutError()
grpc.FutureTimeoutError

An example of gnmic output using the same certificates:

❯ gnmic -a redacted:redacted get --path "/interfaces/interface[name=Ethernet1]/state" -u redacted --tls-ca $SSLDIR/certs/ca.crt --tls-cert $SSLDIR/certs/client.crt --tls-key $SSLDIR/keys/client.key --skip-verify
failed loading config file: open : no such file or directory
password:
[
  {
    "time": "1970-01-01T02:00:00+02:00",
    "updates": [
      {
        "Path": "interfaces/interface[name=Ethernet1]/state",
        "values": {
          "interfaces/interface/state": {
            "arista-intf-augments:inactive": false,
            "openconfig-interfaces:admin-status": "UP",
            "openconfig-interfaces:counters": {
              "in-broadcast-pkts": "38901",
              "in-discards": "0",
              "in-errors": "0",
              "in-fcs-errors": "0",
              "in-multicast-pkts": "1228474",
              "in-octets": "66497837150590",
              "in-unicast-pkts": "273129392409",
              "out-broadcast-pkts": "2399338480",
              "out-discards": "1499",
              "out-errors": "0",
              "out-multicast-pkts": "138494649",
              "out-octets": "104862819133032",
              "out-unicast-pkts": "294719493758"
            },
            "openconfig-interfaces:enabled": true,
            "openconfig-interfaces:ifindex": 1,
            "openconfig-interfaces:last-change": "1639769865809263872",
            "openconfig-interfaces:mtu": 9236,
            "openconfig-interfaces:name": "Ethernet1",
            "openconfig-interfaces:oper-status": "UP",
            "openconfig-interfaces:type": "iana-if-type:ethernetCsmacd",
            "openconfig-platform-port:hardware-port": "Port1",
            "openconfig-vlan:tpid": "openconfig-vlan-types:TPID_0X8100"
          }
        }
      }
    ]
  }
]

Thank you for your time Gunhan

akarneliuk commented 2 years ago

Hey @gunhanoral ,

Thanks for sharing this. Could you please advise if override as you did it before still working?

Best, Anton

akarneliuk commented 2 years ago

Hey @gunhanoral ,

I did a few more tests, please check the latest #82 and published pygnmi==0.8.2. I believe it shall be doing what you aimed to have: override target to '' in case your certificate doesn't have CN and SARs. However, I didn't get that working with my test device, as device doesn't like an empty cert. The override though is tested and worked.

Best, Anton

gunhanoral commented 2 years ago

Hello @akarneliuk ,

Sorry, it failed with another kind of error this time. I tried with override option, with same results.

❯ python3 /tmp/test.py
Password:
Cannot get Common Name: list index out of range
Cannot get Subject Alternative Names: No <ObjectIdentifier(oid=2.5.29.17, name=subjectAltName)> extension was found
ssl_target_name_override is applied, should be used for testing only!
E0723 19:51:08.863921000 123145395400704 ssl_transport_security.cc:1702] Invalid server name indication .
E0723 19:51:08.863952000 123145395400704 ssl_security_connector.cc:135] Handshaker creation failed with error TSI_INTERNAL_ERROR.
E0723 19:51:08.902530000 123145395400704 subchannel.cc:981]            subchannel 0x7f9eb5043a30 {address=ipv4:<redacted>:<redacted>, args={grpc.client_channel_factory=0x7f9eb523d160, grpc.default_authority=, grpc.http2_scheme=https, grpc.internal.channel_credentials=0x7f9eb521fb20, grpc.internal.security_connector=0x7f9eb503fd00, grpc.internal.subchannel_pool=0x7f9eb5255590, grpc.primary_user_agent=grpc-python/1.47.0, grpc.resource_quota=0x7f9eb524d310, grpc.server_uri=dns:///<redacted>:<redacted>, grpc.ssl_target_name_override=}}: error initializing subchannel stack: {"created":"@1658595068.902461000","description":"Auth context missing from client auth filter args","file":"src/core/lib/transport/error_utils.cc","file_line":167,"grpc_status":3}
E0723 19:51:08.902722000 123145395400704 combiner.cc:150]              assertion failed: last & STATE_UNORPHANED
[1]    33493 abort      python3 /tmp/test.py
akarneliuk commented 2 years ago

Hey @gunhanoral ,

This message is quite interesting:

"description":"Auth context missing from client auth filter args

So basically it suggests something is missing in authentication. If you cannot discuss something in public, you can reach me in DM here or in LinkedIn. It is at the moment a bit difficult to troubleshoot your scenario, because I cannot reproduce your issue.

Are using mTLS?

Best, Anton

akarneliuk commented 2 years ago

Hey @gunhanoral ,

after further tests, we've figured that this is not a Python problem but rather underlying C library, which handles gRPC connection. It doesn't have API to disable the validation entirely; hence, with the existing certificate I don't believe it will work, I'm afraid.

The reason why gnmic working is that because it doesn't rely on the Google's C gRPC library.

Best, Anton