Skip to content

Commit

Permalink
Add OAuth documentation and bump version to 1.2.0 (#349)
Browse files Browse the repository at this point in the history
* Add OAuth documentation and bump version to 1.2.0

* Apply review suggestions
  • Loading branch information
tnoczyns-volue authored May 15, 2023
1 parent 5023d01 commit 059349f
Show file tree
Hide file tree
Showing 6 changed files with 285 additions and 182 deletions.
194 changes: 15 additions & 179 deletions docs/source/authentication.rst
Original file line number Diff line number Diff line change
@@ -1,183 +1,19 @@
==============
Authentication
==============
=====================
Client authentication
=====================

When connecting to Mesh using the Python SDK you might need to authenticate
using Kerberos. This is required by the Mesh server if Kerberos is enabled
for the gRPC interface in the Mesh configuration file.
Mesh server could be configured to protect its gRPC API so that only
authenticated users that are assigned to specific roles can access it. There
are two options:

For security reasons authentication also requires TLS to be enabled, and
you might therefore need Mesh's TLS certificate in the below examples.
- **Kerberos**, where Mesh authenticates users and grants access to specific
APIs based on what AD groups the authenticated users belong to.
- **External access token**, where users authenticate and request access to
Mesh from external authorization servers, and Mesh validates the access
token that is obtained from the authorization server.

.. toctree::
:maxdepth: 2

Windows Kerberos
****************

If you are on Windows as an Active Directory domain user Kerberos
authentication is relatively simple. You only need to find the service
principal name the Mesh service is running under. If Mesh is running as a
machine user the service principal name will usually be
``HOST/full.qualified.domain.name`` or ``HOST/f.q.d.n@DOMAIN.COM`` but it might
be different in your environment. Determining the service principal name for
the Mesh service is out of scope for this guide.

.. code-block:: python
from volue import mesh
with open("certificate.pem", "rb") as f:
certificate = f.read()
connection = mesh.Connection.with_kerberos("mesh.local:50051", certificate,
"HOST/mesh.local@DOMAIN.COM", "user@DOMAIN.COM")
print(connection.get_user_identity())
MIT Kerberos (Linux/MacOS)
**************************

When running on Linux or MacOS our world quickly becomes more complicated.
In most configurations the system will not be aware of the Active Directory
configuration, and you will not be logged in as a domain user. We therefore
have to complete a number of steps to make Kerberos credentials available
to the Python SDK.

This is a quickstart guide designed to help you get started, but MIT Kerberos
and Active Directory are both complex topics with numerous possible
configurations, and it is not unlikely that you will need to do some debugging
at one or more of the steps below.

Before we get started you'll need to find the network address(es) of the Active
Directory (AD) Key Distribution Center (KDC), the AD domain name, credentials
for your AD user, the service principal name for the Mesh service, and the TLS
certificate of the Mesh server. Your IT/Operations department might be able to
assist with this, or you can try to investigate yourself.

In this guide we're going to assume that the domain name also resolves to the
domain controller and the KDC.

To get the domain name from a domain joined Windows computer::

REM Get the AD domain name.

> set USERDNSDOMAIN
USERDDNSOMAIN=DOMAIN.COM

REM Get a bit more information about the domain controller.
REM You should note down the IP address here.

> nltest /dsgetdc:DOMAIN.COM
...

At this stage it's a good idea to test if the domain controller is reachable
from your Linux machine. All the following Linux examples run on Ubuntu 20.04
LTS::

# Ideally your DNS setup includes the domain controller. This will
# make the following steps significantly easier. If this command
# fails it might be a good idea to add the IP address of the
# domain controller(s) to your list of DNS servers.

$ ping domain.com # Or the address of a domain controller.

# You can also use the IP directly, but we would recommend against
# it.

$ ping 172.20.101.20

If the above commands both failed, your network will not allow Kerberos
authentication, and you will have to resolve your network issues.

Then we should see if we're able to connect to the KDC on the Kerberos port. By
default the Kerberos protocol will communicate on port 88. If this fails you
will have to work with your IT/Operations department to resolve the issue::

$ netcat -vz domain.com 88
Connection to domain.com 88 port [tcp/kerberos] succeeded!

If everything has gone well up until this point it's time to install MIT
Kerberos. On your distribution the MIT Kerberos package names might be different::

$ sudo apt install krb5-user krb-config libkrb5-dev

Then we'll need to configure MIT Kerberos::

$ cat /etc/krb5.conf
[libdefaults]
default_realm = DOMAIN.COM

[realms]
DOMAIN.COM = {
kdc = domain.com
admin_server = domain.com
}

[domain_realm]
.domain.com = DOMAIN.COM
domain.com = DOMAIN.COM

And finally we can get a ticket granting ticket from the KDC. If this
works you've successfully performed your first Kerberos authentication::

$ kinit -V user@DOMAIN.COM
Using default cache: /tmp/krb5cc_1000
Using principal: user@DOMAIN.COM
Password for user@DOMAIN.COM: ****
Authenticated to Kerberos v5

The newly generated ticket granting ticket will be used when the Mesh
Python SDK authenticates to the Mesh server. As long as that ticket is
available on the client you will not have to retype your password.
To destroy the ticket run ``kdestroy``.

Finally, we can connect to the Mesh server from Python.

.. code-block:: python
from volue import mesh
# Most certificates won't work with IP addresses, therefore
# you'll need to be able to resolve the Mesh server by name,
# either through your DNS configuration, or through /etc/hosts.
with open("certificate.pem", "rb") as f:
certificate = f.read()
# mesh.domain.com is the address of the Mesh server.
# HOST/... is the service principal name, and user@DOMAIN.COM
# is the user principal name.
connection = mesh.Connection.with_kerberos(
"mesh.domain.com:50051", certificate,
"HOST/mesh.domain.com@DOMAIN.COM", "user@DOMAIN.COM"
)
print(connection.get_user_identity())
Example
*******

Please refer to example *authorization.py*:

.. literalinclude:: /../../src/volue/mesh/examples/authorization.py


Internals
*********

Mesh authentication is based on **authorization tokens**. When authentication
is enabled most network calls to Mesh will require one of these tokens to
succeed. To get an authorization token the client is required to make a call
to an authentication endpoint, such as the ``AuthenticateKerberos`` gRPC
method. Authentication endpoints perform the necessary authentication steps,
and if successful return an authorization token that can be used for future
calls.

Authorization tokens have an expiration time, after which they're no longer
valid, and a new authentication call and a new token is required. At the
time of writing most authorization tokens are valid for one hour, but this
is subject to change.

When creating a Mesh connection with authentication enabled in the Python SDK
authentication calls and authorization tokens will be handled transparently
through the ``_authentication`` module. This module will perform authentication
calls when a new token is required, and add authorization tokens to calls
that require them.
kerberos
external_auth
5 changes: 5 additions & 0 deletions docs/source/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ Connect, asynchronously
.. literalinclude:: /../../src/volue/mesh/examples/connect_asynchronously.py
:language: python

Connect using external access token
***********************************
.. literalinclude:: /../../src/volue/mesh/examples/connect_using_external_access_token.py
:language: python

Get version
************
.. literalinclude:: /../../src/volue/mesh/examples/get_version.py
Expand Down
74 changes: 74 additions & 0 deletions docs/source/external_auth.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
======================
External authorization
======================

A Mesh server may be protected by an external authorization server, where the
clients like Python SDK authenticate and request access tokens. Tokens obtained
from the external authorization server are then sent in the `Authorization`
header when making a call to the Mesh API.

This is required by the Mesh server if *OAuth* is enabled for the gRPC
interface in the Mesh configuration file.

For security reasons authentication also requires TLS to be enabled, and
you might therefore need Mesh's TLS certificate in the below examples.


Token format
****************

Mesh supports OAuth 2.0 JSON Web Token (JWT) access tokens. They need to meet
the following requirements:

- The access token signed using RSA algorithm.
- Authorization is done using `roles` claim that must be part of the access
token.
- User's claims: `name` and `oid` must be part of the access token.

Azure Active Directory (Azure AD) supports all OAuth 2.0 flows.


Example
*******

.. code-block:: python
from volue import mesh
# Most certificates won't work with IP addresses, therefore
# you'll need to be able to resolve the Mesh server by name,
# either through your DNS configuration, or through /etc/hosts.
with open("certificate.pem", "rb") as f:
certificate = f.read()
token = "my_access_token" # obtain it from authorization server
# mesh.domain.com is the address of the Mesh server.
connection = mesh.Connection.with_external_access_token(
"mesh.domain.com:50051", certificate, token
)
print(connection.get_user_identity())
For more complex example please refer to *connect_using_external_access_token.py*:

.. literalinclude:: /../../src/volue/mesh/examples/connect_using_external_access_token.py


Internals
*********

Mesh validates **access tokens** that are sent with every call to the server.
Validation means checking token's:

- RSA signature
- integrity - to make sure no one tampered with it
- expiration time
- claims like audience, scope, etc.

Additionally it checks if user is permitted to access given API by checking the
`roles` claim in the access token.

Access tokens have an expiration time, after which they're no longer valid, and
a new access token is required. To provide new access token create either a new
connection or use :py:meth:`volue.mesh.Connection.update_external_access_token`.
Loading

0 comments on commit 059349f

Please sign in to comment.