Kerberos

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.

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

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.

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.

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:

import asyncio

import helpers

import volue.mesh.aio
from volue import mesh


def sync_auth(address, tls_root_pem_cert, service_principal, user_principal):
    print("Synchronous authentication example: ")

    connection = mesh.Connection.with_kerberos(
        address, tls_root_pem_cert, service_principal, user_principal
    )

    user_identity = connection.get_user_identity()
    print(user_identity)

    # revoke no longer used token
    connection.revoke_access_token()


async def async_auth(address, tls_root_pem_cert, service_principal, user_principal):
    print("Asynchronous authentication example:")

    connection = mesh.aio.Connection.with_kerberos(
        address, tls_root_pem_cert, service_principal, user_principal
    )

    user_identity = await connection.get_user_identity()
    print(user_identity)

    # revoke no longer used token
    await connection.revoke_access_token()


def main(address, tls_root_pem_cert):
    """Showing how to authorize to gRPC Mesh server."""

    # If Mesh gRPC server is running as a service user,
    # for example LocalSystem, NetworkService or a user account
    # with a registered service principal name then it is enough
    # to provide hostname as service principal, e.g.:
    #   'HOST/hostname.companyad.company.com'
    # If Mesh gRPC server is running as a user account without
    # registered service principal name then it is enough to provide
    # user account name running Mesh server as service principal, e.g.:
    #   'ad\\user.name' or r'ad\user.name'
    # Note: winkerberos converts service principal name if provided in
    #       RFC-2078 format. '@' is converted to '/' if there is no '/'
    #       character in the service principal name. E.g.:
    #           service@hostname
    #       Would be converted to:
    #           service/hostname
    service_principal = "HOST/hostname.companyad.company.com"
    user_principal = None

    sync_auth(address, tls_root_pem_cert, service_principal, user_principal)
    asyncio.run(
        async_auth(address, tls_root_pem_cert, service_principal, user_principal)
    )


if __name__ == "__main__":
    # This will authenticate Python client (receive authorization token from Mesh),
    # then send gRPC request that requires authorization (e.g.: GetUserIdentity)
    # and print the result. If your user name info is printed, you have successfully
    # communicated with the server.
    #
    # This requires Mesh server to be running with enabled TLS and Kerberos options.

    address, tls_root_pem_cert = helpers.get_connection_info()
#    main(address, tls_root_pem_cert)

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.