Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 23.0 #1961

Merged
merged 11 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from 10 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
1 change: 1 addition & 0 deletions codeanalysis.ruleset
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<Rule Id="SA1005" Action="None" />
<Rule Id="SA1008" Action="None" />
<Rule Id="SA1009" Action="None" />
<Rule Id="SA1010" Action="None" />
<Rule Id="SA1011" Action="None" />
<Rule Id="SA1012" Action="None" />
<Rule Id="SA1013" Action="None" />
Expand Down
126 changes: 98 additions & 28 deletions docs/features/authentication.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,91 @@ Authentication
==============

In order to authenticate Routes and subsequently use any of Ocelot's claims based features such as authorization or modifying the request with values from the token,
users must register authentication services in their **Startup.cs** as usual but they provide a scheme (authentication provider key) with each registration e.g.
users must register authentication services in their **Startup.cs** as usual but they provide `a scheme <https://learn.microsoft.com/en-us/aspnet/core/security/authentication/#authentication-scheme>`_
(authentication provider key) with each registration e.g.

.. code-block:: csharp

public void ConfigureServices(IServiceCollection services)
{
var authenticationProviderKey = "TestKey";
const string AuthenticationProviderKey = "MyKey";
services
.AddAuthentication()
.AddJwtBearer(authenticationProviderKey,
options => { /* custom auth-setup */ });
.AddJwtBearer(AuthenticationProviderKey, options =>
{
// Custom Authentication setup via options initialization
});
}

In this example "**TestKey**" is the scheme that this provider has been registered with. We then map this to a Route in the configuration e.g.
In this example ``MyKey`` is `the scheme <https://learn.microsoft.com/en-us/aspnet/core/security/authentication/#authentication-scheme>`_ that this provider has been registered with.
We then map this to a Route in the configuration using the following `AuthenticationOptions <https://github.com/search?q=repo%3AThreeMammals%2FOcelot%20AuthenticationOptions&type=code>`_ properties:

* ``AuthenticationProviderKey`` is a string object, obsolete. [#f1]_ This is legacy definition when you define :ref:`authentication-single` (scheme).
* ``AuthenticationProviderKeys`` is an array of strings, the recommended definition of :ref:`authentication-multiple` feature.

.. authentication-single:

Single Key [#f1]_
-----------------

| Property: ``AuthenticationOptions.AuthenticationProviderKey``

We map authentication provider to a Route in the configuration e.g.

.. code-block:: json

"Routes": [{
"AuthenticationOptions": {
"AuthenticationProviderKey": "TestKey",
"AllowedScopes": []
}
}]
"AuthenticationOptions": {
"AuthenticationProviderKey": "MyKey",
"AllowedScopes": []
}

When Ocelot runs it will look at this Routes ``AuthenticationOptions.AuthenticationProviderKey`` and check that there is an authentication provider registered with the given key.
When Ocelot runs it will look at this Routes ``AuthenticationProviderKey`` and check that there is an authentication provider registered with the given key.
If there isn't then Ocelot will not start up. If there is then the Route will use that provider when it executes.

If a Route is authenticated, Ocelot will invoke whatever scheme is associated with it while executing the authentication middleware.
If the request fails authentication, Ocelot returns a HTTP status code `401 Unauthorized <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401>`_.

.. authentication-multiple:

Multiple Authentication Schemes [#f2]_
--------------------------------------

| Property: ``AuthenticationOptions.AuthenticationProviderKeys``

In real world of ASP.NET, apps may need to support multiple types of authentication by single Ocelot app instance.
To register `multiple authentication schemes <https://learn.microsoft.com/en-us/aspnet/core/security/authorization/limitingidentitybyscheme#use-multiple-authentication-schemes>`_
(authentication provider keys) for each appropriate authentication provider, use and develop this abstract configuration of two or more schemes:

.. code-block:: csharp

public void ConfigureServices(IServiceCollection services)
{
const string DefaultScheme = JwtBearerDefaults.AuthenticationScheme; // Bearer
services.AddAuthentication()
.AddJwtBearer(DefaultScheme, options => { /* JWT setup */ })
// AddJwtBearer, AddCookie, AddIdentityServerAuthentication etc.
.AddMyProvider("MyKey", options => { /* Custom auth setup */ });
}

In this example, the schemes ``MyKey`` and ``Bearer`` represent the keys which these providers have been registered with.
We then map these schemes to a Route in the configuration, as shown below

.. code-block:: json

"AuthenticationOptions": {
"AuthenticationProviderKeys": [ "Bearer", "MyKey" ] // The order matters!
"AllowedScopes": []
}

Afterward, Ocelot applies all steps that are specified for ``AuthenticationProviderKey`` as :ref:`authentication-single`.

**Note** that the order of the keys in an array definition does matter! We use a "First One Wins" authentication strategy.

Finally, we would say that registering providers, initializing options, forwarding authentication artifacts can be a "real" coding challenge.
If you're stuck or don't know what to do, just find inspiration in our `acceptance tests <https://github.com/search?q=repo%3AThreeMammals%2FOcelot%20MultipleAuthSchemesFeatureTests&type=code>`_
(currently for `Identity Server 4 <https://identityserver4.readthedocs.io/>`_ only).
We would appreciate any new PRs to add extra acceptance tests for your custom scenarios with `multiple authentication schemes <https://learn.microsoft.com/en-us/aspnet/core/security/authorization/limitingidentitybyscheme#use-multiple-authentication-schemes>`__. [#f2]_

JWT Tokens
----------

Expand All @@ -41,7 +96,7 @@ If you want to authenticate using JWT tokens maybe from a provider like `Auth0 <

public void ConfigureServices(IServiceCollection services)
{
var authenticationProviderKey = "TestKey";
var authenticationProviderKey = "MyKey";
services
.AddAuthentication()
.AddJwtBearer(authenticationProviderKey, options =>
Expand All @@ -56,12 +111,16 @@ Then map the authentication provider key to a Route in your configuration e.g.

.. code-block:: json

"Routes": [{
"AuthenticationOptions": {
"AuthenticationProviderKey": "TestKey",
"AllowedScopes": []
}
}]
"AuthenticationOptions": {
"AuthenticationProviderKeys": [ "MyKey" ],
"AllowedScopes": []
}

Docs
^^^^

* Microsoft Learn: `Authentication and authorization in minimal APIs <https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/security>`_
* Andrew Lock | .NET Escapades: `A look behind the JWT bearer authentication middleware in ASP.NET Core <https://andrewlock.net/a-look-behind-the-jwt-bearer-authentication-middleware-in-asp-net-core/>`_

Identity Server Bearer Tokens
-----------------------------
Expand All @@ -73,7 +132,7 @@ If you don't understand how to do this, please consult the IdentityServer `docum

public void ConfigureServices(IServiceCollection services)
{
var authenticationProviderKey = "TestKey";
var authenticationProviderKey = "MyKey";
Action<JwtBearerOptions> options = (opt) =>
{
opt.Authority = "https://whereyouridentityserverlives.com";
Expand All @@ -89,12 +148,10 @@ Then map the authentication provider key to a Route in your configuration e.g.

.. code-block:: json

"Routes": [{
"AuthenticationOptions": {
"AuthenticationProviderKey": "TestKey",
"AllowedScopes": []
}
}]
"AuthenticationOptions": {
"AuthenticationProviderKeys": [ "MyKey" ],
"AllowedScopes": []
}

Auth0 by Okta
-------------
Expand Down Expand Up @@ -137,8 +194,21 @@ If you add scopes to **AllowedScopes**, Ocelot will get all the user claims (fro

This is a way to restrict access to a Route on a per scope basis.

More identity providers
-----------------------
Links
-----

* Microsoft Learn: `Overview of ASP.NET Core authentication <https://learn.microsoft.com/en-us/aspnet/core/security/authentication/>`_
* Microsoft Learn: `Authorize with a specific scheme in ASP.NET Core <https://learn.microsoft.com/en-us/aspnet/core/security/authorization/limitingidentitybyscheme>`_
* Microsoft Learn: `Policy schemes in ASP.NET Core <https://learn.microsoft.com/en-us/aspnet/core/security/authentication/policyschemes>`_
* Microsoft .NET Blog: `ASP.NET Core Authentication with IdentityServer4 <https://devblogs.microsoft.com/dotnet/asp-net-core-authentication-with-identityserver4/>`_

Future
------

We invite you to add more examples, if you have integrated with other identity providers and the integration solution is working.
Please, open `Show and tell <https://github.com/ThreeMammals/Ocelot/discussions/categories/show-and-tell>`_ discussion in the repository.

""""

.. [#f1] Use the ``AuthenticationProviderKeys`` property instead of ``AuthenticationProviderKey`` one. We supports this obsolete property because of backward compatibility and allowing migrations. In future releases the property can be removed as a breaking change.
.. [#f2] `Multiple authentication schemes <https://learn.microsoft.com/en-us/aspnet/core/security/authorization/limitingidentitybyscheme#use-multiple-authentication-schemes>`__ feature was requested in issues `740 <https://github.com/ThreeMammals/Ocelot/issues/740>`_, `1580 <https://github.com/ThreeMammals/Ocelot/issues/1580>`_ and delivered as a part of `23.0 <https://github.com/ThreeMammals/Ocelot/releases/tag/23.0.0>`_ release.
5 changes: 4 additions & 1 deletion docs/features/caching.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,14 @@ Finally, in order to use caching on a route in your Route configuration add this

.. code-block:: json

"FileCacheOptions": { "TtlSeconds": 15, "Region": "europe-central" }
"FileCacheOptions": { "TtlSeconds": 15, "Region": "europe-central", "Header": "Authorization" }

In this example **TtlSeconds** is set to 15 which means the cache will expire after 15 seconds.
The **Region** represents a region of caching.

Additionally, if a header name is defined in the **Header** property, that header value is looked up by the key (header name) in the ``HttpRequest`` headers,
and if the header is found, its value will be included in caching key. This causes the cache to become invalid due to the header value changing.

If you look at the example `here <https://github.com/ThreeMammals/Ocelot/blob/main/test/Ocelot.ManualTest/Program.cs>`_ you can see how the cache manager is setup and then passed into the Ocelot ``AddCacheManager`` configuration method.
You can use any settings supported by the **CacheManager** package and just pass them in.

Expand Down
61 changes: 39 additions & 22 deletions docs/features/kubernetes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,27 @@
:alt: K8s Logo
:width: 40

|K8s Logo| Kubernetes
=====================
|K8s Logo| Kubernetes [#f1]_ aka K8s
====================================

Feature: :doc:`../features/servicediscovery`
A part of feature: :doc:`../features/servicediscovery`

This feature was requested as part of `issue 345 <https://github.com/ThreeMammals/Ocelot/issues/345>`_ to add support for `Kubernetes <https://kubernetes.io/>`_ service discovery provider.
About [#f2]_
------------

Ocelot will call the K8s endpoints API in a given namespace to get all of the endpoints for a pod and then load balance across them.
Ocelot used to use the services API to send requests to the K8s service but this was changed in `PR 1134 <https://github.com/ThreeMammals/Ocelot/pull/1134>`_ because the service did not load balance as expected.
Ocelot will call the `K8s <https://kubernetes.io/>`_ endpoints API in a given namespace to get all of the endpoints for a pod and then load balance across them.
Ocelot used to use the services API to send requests to the `K8s <https://kubernetes.io/>`__ service but this was changed in `PR 1134 <https://github.com/ThreeMammals/Ocelot/pull/1134>`_ because the service did not load balance as expected.

The first thing you need to do is install the `NuGet package <https://www.nuget.org/packages/Ocelot.Provider.Kubernetes>`_ that provides Kubernetes support in Ocelot:
Install
-------

The first thing you need to do is install the `NuGet package <https://www.nuget.org/packages/Ocelot.Provider.Kubernetes>`_ that provides **Kubernetes** [#f1]_ support in Ocelot:

.. code-block:: powershell

Install-Package Ocelot.Provider.Kubernetes

Then add the following to your ConfigureServices method:
Then add the following to your ``ConfigureServices`` method:

.. code-block:: csharp

Expand All @@ -41,47 +45,53 @@ K8s API server and token will read from pod.

kubectl create clusterrolebinding permissive-binding --clusterrole=cluster-admin --user=admin --user=kubelet --group=system:serviceaccounts

The following example shows how to set up a Route that will work in Kubernetes.
Configuration
-------------

The following examples show how to set up a Route that will work in Kubernetes.
The most important thing is the **ServiceName** which is made up of the Kubernetes service name.
We also need to set up the **ServiceDiscoveryProvider** in **GlobalConfiguration**.

Kube default provider
^^^^^^^^^^^^^^^^^^^^^

The example here shows a typical configuration:

.. code-block:: json

{
"Routes": [
{
"DownstreamPathTemplate": "/api/values",
"DownstreamScheme": "http",
"UpstreamPathTemplate": "/values",
"ServiceName": "downstreamservice",
"UpstreamHttpMethod": [ "Get" ]
// ...
}
],
"GlobalConfiguration": {
"ServiceDiscoveryProvider": {
"Host": "192.168.0.13",
"Port": 443,
"Token": "txpc696iUhbVoudg164r93CxDTrKRVWG",
"Namespace": "dev",
"Type": "kube"
"Namespace": "Dev",
"Type": "Kube"
}
}
}

Service deployment in **Namespace** ``dev``, **ServiceDiscoveryProvider** type is ``kube``, you also can set ``pollkube`` **ServiceDiscoveryProvider** type.
Service deployment in **Namespace** ``Dev``, **ServiceDiscoveryProvider** type is ``Kube``, you also can set :ref:`kubernetes-pollkube-provider` type.
Note: **Host**, **Port** and **Token** are no longer in use.

.. kubernetes-pollkube-provider:

PollKube provider
^^^^^^^^^^^^^^^^^

You use Ocelot to poll Kubernetes for latest service information rather than per request.
If you want to poll Kubernetes for the latest services rather than per request (default behaviour) then you need to set the following configuration:

.. code-block:: json

"ServiceDiscoveryProvider": {
// ...
"Namespace": "dev",
"Type": "pollkube",
"PollingInterval": 100
"Type": "PollKube",
"PollingInterval": 100 // ms
}

The polling interval is in milliseconds and tells Ocelot how often to call Kubernetes for changes in service configuration.
Expand All @@ -92,14 +102,21 @@ This really depends on how volatile your services are.
We doubt it will matter for most people and polling may give a tiny performance improvement over calling Kubernetes per request.
There is no way for Ocelot to work these out for you.

Global vs Route levels
^^^^^^^^^^^^^^^^^^^^^^

If your downstream service resides in a different namespace, you can override the global setting at the Route-level by specifying a **ServiceNamespace**:

.. code-block:: json

"Routes": [
{
// ...
"ServiceName": "downstreamservice",
"ServiceNamespace": "downstream-namespace"
}
]

""""

.. [#f1] `Wikipedia <https://en.wikipedia.org/wiki/Kubernetes>`_ | `K8s Website <https://kubernetes.io/>`_ | `K8s Documentation <https://kubernetes.io/docs/>`_ | `K8s GitHub <https://github.com/kubernetes/kubernetes>`_
.. [#f2] This feature was requested as part of `issue 345 <https://github.com/ThreeMammals/Ocelot/issues/345>`_ to add support for `Kubernetes <https://kubernetes.io/>`_ :doc:`../features/servicediscovery` provider.
Loading