Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion docs/user/authentication.rst
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,41 @@ mTLS - Mutual TLS Authentication (Certificate-Based Authentication)

The most ideal form of authentication for machine to machine communication. Follow `KB0993615 <https://support.servicenow.com/kb?id=kb_article_view&sysparm_article=KB0993615>`_ then:

>>> client = ServiceNowClient(instance, cert=('/path/to/client.cert', '/path/to/client.key'))

>>> client = ServiceNowClient(instance, cert=('/path/to/USER_x509.pem', '/path/to/USERPRIVATEKEY.key'))


A quick example, using self-signed certificates:

1. Setup the CA (root) key

.. code-block:: bash

# generate a root private key, if for some reason you don't have one already
openssl genrsa -aes256 -out ca.key 2048
# generate the CA certificate
openssl req -x509 -new -nodes -key ca.key -out cert.pem -sha512 -days 365 -out cacert.pem

2. Upload `cacert.pem` via `/sys_ca_certificate.do`

3. Setup the user key and CSR (we just generate them here for a POC example)

.. code-block:: bash

openssl req -nodes -newkey rsa:2048 -keyout USERPRIVATEKEY.key -out USERCSR.csr

.. important::

Python requests (the underlying http library) does not directly support keys with passwords! See `requests#2519 <https://github.com/psf/requests/issues/2519>`_ for details.

4. Sign the CSR with the root, creating a X.509 for the user

.. code-block:: bash

openssl x509 -req -days 365 -in USERCSR.csr -CA cacert.pem -CAkey ca.key -extfile <(printf "extendedKeyUsage=clientAuth") -out USER_x509.pem

5. Attach `USER_x509.pem` to a new `/sys_user_certificate.do` record


Requests Authentication
-----------------------
Expand Down
3 changes: 2 additions & 1 deletion pysnc/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class ServiceNowClient(object):
:param bool verify: Verify the SSL/TLS certificate OR the certificate to use. Useful if you're using a self-signed HTTPS proxy.
:param cert: if String, path to ssl client cert file (.pem). If Tuple, (‘cert’, ‘key’) pair.
"""
def __init__(self, instance, auth, proxy=None, verify=None, cert=None, auto_retry=True):
def __init__(self, instance, auth=None, proxy=None, verify=None, cert=None, auto_retry=True):
self._log = logging.getLogger(__name__)
self.__instance = get_instance(instance)

Expand Down Expand Up @@ -62,6 +62,7 @@ def __init__(self, instance, auth, proxy=None, verify=None, cert=None, auto_retr
elif isinstance(auth, ServiceNowFlow):
self.__session = auth.authenticate(self.__instance, proxies=self.__proxies, verify=verify)
elif cert is not None:
self.__session = requests.session()
self.__session.cert = cert
else:
raise AuthenticationException('No valid authentication method provided')
Expand Down
13 changes: 13 additions & 0 deletions test/test_snc_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,19 @@ def nop_test_jwt(self):
assert gr.get('6816f79cc0a8016401c5a33be04be441'), "did not jwt auth"
'''

@skip("Requires keys and conf that makes automation hard")
def test_mtls(self):
# e.g. PYSNC_USER_KEY=x PYSNC_USER_CERT=y poetry run pytest test/test_snc_auth.py::TestAuth::test_mtls
path_key = self.c.get_value('USER_KEY')
assert path_key, 'Require user private key'
path_cert = self.c.get_value('USER_CERT')
assert path_cert, 'Require user x509 certificate'

client = ServiceNowClient(self.c.server, cert=(path_cert, path_key))
gr = client.GlideRecord('sys_user')
gr.fields = 'sys_id'
self.assertTrue(gr.get('6816f79cc0a8016401c5a33be04be441'))




Expand Down