Skip to content

NMica/NMica.Security

Repository files navigation

Kerberos Authentication Proxy

This project allows placing a bi-directional transparent proxy for the app that transforms Kerberos tickets into JWT. This allows authenticating Kerberos clients without having to run on domain joined Windows machine or configuring MIT Kerberos on Linux, and works on any platform supported by .NET 6 (Windows, Linux, OSX). This solution works very well for supporting legacy apps when adopting cloud and container based environments. When configured for ingress it will validate incoming Kerberos tickets in HTTP SPNEGO. This project allows solving for Kerberos authentication scenarios when ticket is transmitted via HTTP Negotiate header (aka Integrated Windows Authentication) header and send user's principal to downstream app as a standard JWT Bearer token. When configured for egress it will authenticate the app using authentication method of choice (MTLs is recommended), acquire Kerberos tickets on application behalf and append it to outgoing request. This approach works for applications that are using HTTP Header to transmit to Kerberos ticket, such as:

  • Front end web applications
  • WCF services with basicHttpBinding, and transport level Windows security (requires using JWT WCF middleware)

It does not work for the following scenarios:

  • SQL Server Integrated Authentication
  • WCF services using wsHttpBinding (ticket is part of SOAP payload rather then header)
  • Services that require Kerberos delegation (maybe possible via future enhancement)

Getting Started

Configure proxy settings by editing application.yaml

  1. Set correct AD settings under SPNEGO section
  2. Set JWT signing key. You can generate new one via included utility by running dotnet run generate-key in src\KerberosUtil folder.
  3. Configure routing. Full documentation can be found in official YARP documentation.

Ingress

When configured as reverse proxy, the incoming requests are matched to a specific route defined in configuration. If the route is associated with an authorization policy, it will be applied. If the user fails to supply a valid Kerberos ticket in the request, the request will end with a 401 Unauthorized and the request will not be proxied to the destination.

The Kerberos ticket verification implementation relies on fully managed Kerberos ticket parser provided via Kerberos.NET library and does not require any communication with the domain controller when validating incoming tickets.

Principal propagation

When the proxy successfully establishes the security principal (identity of the caller + any additional claims) from the Kerberos ticket, it serializes established security principal into JWT and appends it as a standard Bearer token to proxied request. Kerberos tickets contain AD groups the user belongs to, but they are in SID format, and are not sent downstream by default. If you want to make assertions on roles, you need to configure LDAP for the route service which will be used to map SIDs to their common names. When configured it will send AD groups as part of the principal propagation mechanism. Groups are loaded on startup and cached in memory. The proxy will do a periodic light weight incremental query to check if AD groups configuration has changed. The associated middleware will expand full hierarchy of AD groups based on their associations (ex. if user is only assigned group called Foo, and Foo is memberOf group Bar, then principal will show that the user belong to both). In order to enable additional claims loading, Spnego:Ldap section of configuration must be filled out. Additional user claims can be loaded from LDAP and mapped to specific claim type like this:

Spnego:
  Ldap:
    Claims: # see System.Security.Claims.ClaimTypes for standard claim types constants or use your own
      - LdapAttribute: givenName
        ClaimType: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname
      - LdapAttribute: sn
        ClaimType: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname
      - LdapAttribute: email
        ClaimType: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
      - LdapAttribute: memberof
        ClaimType: http://schemas.microsoft.com/ws/2008/06/identity/claims/role

The proxy forwards the principal as a JWT token to the target application. The JWT token is signed with an RSA signing key. The calling application can validate the token by retrieving the signing public key that the proxy publishes via standard OpenID Connect discovery mechanism.

Ingress

The sample payload section of the JWT sent downstream would look as following:

{
  "nbf": 1623776653,
  "exp": 1623776713,
  "iss": "http://localhost:8081",
  "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/sid": "S-1-5-21-3483396884-3677748265-799010679-1105",
  "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname": "iwaclient",
  "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier": "ALMIREX\\iwaclient",
  "http://schemas.microsoft.com/ws/2008/06/identity/claims/role": [
    "Users",
    "MyGroup2",
    "MyGroup"
  ]
}

JWT signing key can be easily generated by running the following command inside util projects folder:

dotnet run generate-key

Configuring routes

Incoming requests are matched to a destination routes by associating them with a named cluster. A cluster defines one or more destination URLs to which request should be forwarded. A basic ingress configuration may look as following:

Proxy:
  Routes: # Routes tell the proxy which requests to forward
    IngressStaticDestination:
      ClusterId: httpbin
      AuthorizationPolicy: default # defaults to required authenticated user
      Match:
        Hosts:
          - httpbin.org
      Transforms:
        - AuthorizationScheme: Bearer # issue JWT tokens with caller's identity
  Clusters: # Clusters tell the proxy where and how to forward requests
    httpbin:
      Destinations:
        httpbin/1:
          Address: https://httpbin.org

Automatic SPN management

The included NMica.SpnManager API service allows callers to view and create SPNs for their own identity. The callers to SpnManager must authenticate with Kerberos. If they are part of SpnCreators AD group, they can add and delete SPNs for their own identity by calling POST / DELETE to /spn/<SPN> endpoint (ex: /spn/http/myhost.com). LDAP credentials assigned to SpnManager must have sufficient permissions to be able to manage SPNs in the AD.

The proxy can be configured to talk to SpnManager service in order to create the necessary SPNs automatically. You can enable this by configuring the following values in config

SpnManagement:
  Enabled: true
  ServiceUrl: https://localhost:7167
  Routes:
    - https://localhost:7167

Proxy determines own public routes for which SPNs need to be created. The default implementation reads them from SpnManagement:Routes section. When running on Cloud Foundry, the routes are read from environmental variable injected by the platform. IRouteProvider can be used to provide custom implementation for SPN registrations.

Egress

The proxy can work in forwarding mode by acquiring Kerberos tickets and appending them to any outgoing requests. This is useful when application running on modern cloud platform needs to communicate to a legacy service secured by Kerberos. The Kerberos ticket will be obtained from Active Directory KDC using Kerberos.NET library and appended to the outgoing request as Authorize: Negotiate base64(kerb-ticket.

Egress

Authenticating to the proxy

The calling application must authenticate to the proxy in a way that ensures only legitimate calls can be sent to target app. MTLs or API key are preferred modes. You can use API-Key authentication by setting ApiKey property in configuration and applying authorization policy on any egress routes. See sample below

Egress route example

ApiKey: mykey
Proxy:
  Routes: # Routes tell the proxy which requests to forward
    EgressStaticDestination:
      ClusterId: httpbin
      AuthorizationPolicy: ValidApiKey
      Match:
        Hosts:
          - httpbin.local
      Transforms:
        - AuthorizationScheme: Negotiate # acquires and appends Kerberos tickets
  Clusters: # Clusters tell the proxy where and how to forward requests
    httpbin:
      Destinations:
        httpbin/1:
          Address: https://httpbin.org

Configuring for Cloud Foundry

The proxy can be used to secure applications running on Cloud Foundry by configuring it as a route service. This will allow it to intercept any application requests before forwarding them to the destination. When running as a route service, the destination where to forward the request should be sent is specified by the GoRouter via X-CF-Forwarded-Url http header. As such, a special cluster is used that doesn't have any destination, but is tagged with special metadata as following:

Proxy:
  Clusters:
    cloud-foundry-route-service:
      Destinations:
        any-url:
          Metadata:
            Type: route-service

For egress scenarios, when proxy and calling apps are both running on the platform, authenticating to the proxy is best done with MTLs using platform provided container identity certificates. The proxy is already configured to use MTLs authentication. There are two authorization policies that can be applied to routes via AuthorizationPolicy configuration key:

  • sameorg - accepts any request originating from the app that is deployed in same org as the proxy
  • samespace - accepts any request originating from the app that is deployed in same space as the proxy

How to build

Troubleshooting

  • Ensure that Service principal you're using has the appropriate SPN associated with it. For apps that use user's browser as the client, set spn to http/fqdn, for WCF apps set to host/fqdn

  • If testing via browser, ensure that the site is allowed to perform negotiate authentication. For IE & Chrome this is done by adding site to Trusted list in security settings. For Firefox, open up about:config and set fqdn in network.negotiate-auth.trusted-uris

  • Ensure you're testing from domain joined machine

Testing via Postman

Normally when using browser on domain joined machine, a Kerberos ticket will be automatically obtained by the browser from the OS and attached to the header. You can however craft a manual request with Postman. A collection of sample requests is provided as a Postman collection you can import, which can be found in samples directory.

As some sample requests require a valid Kerberos ticket, a Postman pre-request script is used to obtain one and attach it to outgoing requests automatically. It acquires if from a utility webserver that can be launched from KerberosUtil project. It requires that you run dotnet run get-ticket --start-server with rest of the arguments specific to your AD environment.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages