Skip to content
Open
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
16 changes: 14 additions & 2 deletions .docs/reference/libs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -849,15 +849,27 @@ interfaces:
tags:
- identity-auth
- name: charms.oauth2_proxy_k8s.auth_proxy
status: ''
status: legacy
url: https://charmhub.io/oauth2-proxy-k8s/libraries/auth_proxy
docs: ''
src: https://github.com/canonical/oauth2-proxy-k8s-operator
kind: Charmhub
rel_name: auth_proxy
rel_url_charmhub: https://charmhub.io/integrations/auth_proxy
rel_url_schema: https://github.com/canonical/charm-relation-interfaces/tree/main/interfaces/auth_proxy/v0
description: ''
description: 'Interface library for providing OAuth2 Proxy with downstream charms auth-proxy information. Superseded by charmlibs-interfaces-auth-proxy.'
tags:
- identity-auth
- name: charmlibs.interfaces.auth_proxy
status: recommended
url: https://pypi.org/project/charmlibs-interfaces-auth-proxy
docs: https://canonical.com/juju/docs/charmlibs/reference/charmlibs/interfaces/auth-proxy
src: https://github.com/canonical/oauth2-proxy-k8s-operator
kind: PyPI
rel_name: auth_proxy
rel_url_charmhub: https://charmhub.io/integrations/auth_proxy
rel_url_schema: ''
description: 'Interface library for providing OAuth2 Proxy with downstream charms auth-proxy information.'
tags:
- identity-auth
- name: charms.blackbox_exporter_k8s.blackbox_probes
Expand Down
3 changes: 1 addition & 2 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@
# charmlibs.interfaces packages (alphabetical)

# /interfaces/auth_proxy/
/interfaces/auth_proxy/interface/ @canonical/identity
/interfaces/auth_proxy/ruff.toml @canonical/identity
/interfaces/auth_proxy @canonical/identity

# /interfaces/azure_service_principal/
/interfaces/azure_service_principal/interface/ @canonical/data
Expand Down
5 changes: 5 additions & 0 deletions interfaces/auth_proxy/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Changelog

## 1.0.0

Initial release. Migrated from `charms.oauth2_proxy_k8s.v0.auth_proxy` (v0.4).
11 changes: 11 additions & 0 deletions interfaces/auth_proxy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# charmlibs.interfaces.auth_proxy

The `auth_proxy` interface library.

To install, add `charmlibs-interfaces-auth-proxy` to your Python dependencies. Then in your Python code, import as:

```py
from charmlibs.interfaces import auth_proxy
```

See the [reference documentation](https://canonical.com/juju/docs/charmlibs/reference/charmlibs/interfaces/auth-proxy) for more.
108 changes: 108 additions & 0 deletions interfaces/auth_proxy/docs/how-to/integrate-with-oauth2-proxy.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
.. meta::
:description: Integrate your charm and ingress provider with Identity and Access Proxy using the `auth-proxy` interface.

.. _integrate-auth-proxy-with-oauth2-proxy:

Integrate your charm with Identity and Access Proxy
===================================================

Applications that do not conform to OAuth/OIDC standards or don't offer built-in access control
can be secured using the Identity and Access Proxy (IAP) solution. This approach allows you to protect application endpoints
by intercepting incoming requests and delegating the authentication and authorization (authn/authz) process to the relevant components of the `Identity Platform <https://charmhub.io/identity-platform>`_.

OAuth2 Proxy is the main entrypoint to plug the Identity and Access Proxy to your charmed operator. This is achieved through the power of Juju relations.

This guide explains how to extend the Identity Platform with the Identity and Access Proxy solution
and integrate it with your charm, allowing you to restrict access to your application to authenticated users only.

Prerequisites
-------------

We are going to assume that:

1. Your charmed application does not support the OAuth 2.0/OIDC protocols (otherwise, refer to `this guide <https://charmhub.io/topics/canonical-identity-platform/how-to/integrate-oidc-compatible-charms>`_ instead).
2. Your charmed application supports integration with Charmed Traefik via the ``ingress-per-app`` or ``ingress-per-unit`` interface and provides Charmed OAuth2 Proxy with necessary data by supporting the ``auth_proxy`` interface.
3. You have deployed the Identity Platform, see `tutorial <https://charmhub.io/topics/canonical-identity-platform/tutorials/e2e-tutorial>`_.
4. You have deployed your charmed application on Kubernetes.

Initial Deployment State
~~~~~~~~~~~~~~~~~~~~~~~~

This deployment should be your starting point:

.. code:: text

Model Controller Cloud/Region Version SLA Timestamp
iam my-controller microk8s/localhost 3.6.13 unsupported 16:37:26+01:00

SAAS Status Store URL
ingress active local admin/core.ingress
postgresql active local admin/core.postgresql
traefik-route active local admin/core.traefik-route

App Version Status Scale Charm Channel Rev Address Exposed Message
hydra v2.3.0 active 1 hydra latest/stable 395 10.152.183.127 no
kratos v1.3.1 active 1 kratos latest/stable 565 10.152.183.75 no
login-ui 0.24.2 active 1 identity-platform-login-ui-operator latest/stable 197 10.152.183.135 no

Unit Workload Agent Address Ports Message
hydra/0* active idle 10.1.57.184
kratos/0* active idle 10.1.57.183
login-ui/0* active idle 10.1.57.185

Offer Application Charm Rev Connected Endpoint Interface Role
kratos-info-offer kratos kratos 565 0/0 kratos-info kratos_info provider
oauth-offer hydra hydra 395 0/0 oauth oauth provider

In this guide, we assume you also deployed your charmed application in the ``iam`` model.

Step 1: Configure Ingress and Forward Auth
------------------------------------------

In order to set up the proxy, you first need to expose the ``forward-auth`` offer, enable the feature in Charmed Traefik, and integrate it with your charm via the ingress relation.

.. code-block:: shell

juju config traefik-public enable_experimental_forward_auth=True -m core
juju offer traefik-public:experimental-forward-auth forward-auth -m core
juju integrate your-charm admin/core.ingress -m iam

Step 2: Deploy and Integrate OAuth2 Proxy
-----------------------------------------

The next step is to deploy Charmed OAuth2 Proxy and integrate it with Charmed Traefik using the exposed offer:

.. code-block:: shell

juju deploy oauth2-proxy-k8s --channel latest/stable --trust -m iam
juju integrate oauth2-proxy:forward-auth admin/core.forward-auth -m iam

You can follow the deployment status with ``watch -c juju status --color``.

Step 3: Integrate Your Application with the Proxy
-------------------------------------------------

Then, integrate your charm with the proxy by running:

.. code-block:: shell

juju integrate oauth2-proxy-k8s your-charm:auth-proxy -m iam

Step 4: Connect to the Identity Provider
----------------------------------------

Finally, integrate the proxy with the Identity Platform's OIDC provider—Charmed Hydra:

.. code-block:: shell

juju integrate oauth2-proxy-k8s:oauth hydra -m iam

Authentication Flow
-------------------

When you access your application, Charmed Traefik will ask OAuth2 Proxy whether access to the endpoint is protected.
If it is, the proxy will check for a valid session. If no valid session is found, it will redirect you to the Identity Platform login page.
Upon successful authentication, you will be redirected back to your application.

.. note::
See more: `Charmhub | OAuth2 Proxy > Integrations <https://charmhub.io/oauth2-proxy-k8s/integrations>`_
6 changes: 3 additions & 3 deletions interfaces/auth_proxy/interface/v0/interface.yaml
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
name: auth_proxy
version: 0
status: draft
lib: charms.oathkeeper.auth_proxy
lib: charmlibs.interfaces.auth_proxy
summary: Configure an Identity and Access Proxy.
description: |
The `auth_proxy` interface allows charms to configure an Identity and Access Proxy.
The requirer supplies the configuration, such as protected URLs, allowed endpoints, and headers.
The provider is responsible for transforming the configuration into access rules and forwarding relevant configuration to the proxy.

providers:
- name: oathkeeper-operator
url: https://www.github.com/canonical/oathkeeper-operator
- name: oauth2-proxy-k8s-operator
url: https://www.github.com/canonical/oauth2-proxy-k8s-operator

requirers:
[]
Expand Down
82 changes: 82 additions & 0 deletions interfaces/auth_proxy/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
[project]
name = "charmlibs-interfaces-auth-proxy"
description = "The charmlibs.interfaces.auth_proxy package."
readme = "README.md"
requires-python = ">=3.10"
authors = [
{name="Identity Team at Canonical"},
]
license = "Apache-2.0"
classifiers = [
"Programming Language :: Python :: 3",
"Intended Audience :: Developers",
"Operating System :: POSIX :: Linux",
"Development Status :: 5 - Production/Stable",
]
dynamic = ["version"]
dependencies = [
# Libraries should use clear but wide dependency bounds to avoid overly constraining charms.
# Ops 3 is backwards compatible with Ops 2, but drops Python 3.8 support.
# Ops 3 has new features not present in Ops 2.23 (the last Ops 2 feature release).
# See the Ops 2.23 maintenance branch changelog and the Ops 3 changelog for current information:
# - https://github.com/canonical/operator/blob/2.23-maintenance/CHANGES.md
# - https://github.com/canonical/operator/blob/main/CHANGES.md
# Set the dependency bounds according to the features and fixes your library needs.
# "ops>=2.23.1,<4",
"jsonschema>=4.26.0",
"ops>=3.8.0",
]

[dependency-groups]
lint = [ # installed for `just lint interfaces/auth_proxy` (unit, functional, and integration are also installed)
# "typing_extensions",
]
unit = [ # installed for `just unit interfaces/auth_proxy`
"ops[testing]",
]
functional = [ # installed for `just functional interfaces/auth_proxy`
]
integration = [ # installed for `just integration interfaces/auth_proxy`
"jubilant",
]

[project.urls]
"Documentation" = "https://canonical.com/juju/docs/charmlibs/reference/charmlibs/interfaces/auth-proxy"
"Repository" = "https://github.com/canonical/charmlibs/tree/main/interfaces/auth_proxy"
"Issues" = "https://github.com/canonical/charmlibs/issues"
"Changelog" = "https://github.com/canonical/charmlibs/blob/main/interfaces/auth_proxy/CHANGELOG.md"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.build.targets.wheel]
packages = ["src/charmlibs"]

[tool.hatch.version]
path = "src/charmlibs/interfaces/auth_proxy/_version.py"

[tool.ruff]
extend = "../../pyproject.toml"
src = ["src", "tests/unit", "tests/functional", "tests/integration"] # correctly sort local imports in tests

[tool.ruff.lint.extend-per-file-ignores]
# add additional per-file-ignores here to avoid overriding repo-level config
"tests/**/*" = [
# "E501", # line too long
]

[tool.pyright]
extends = "../../pyproject.toml"
include = ["src", "tests"]
pythonVersion = "3.10" # check no python > 3.10 features are used

[tool.charmlibs.functional]
ubuntu = [] # ubuntu versions to run functional tests with, e.g. "24.04" (defaults to just "latest")
pebble = [] # pebble versions to run functional tests with, e.g. "v1.0.0", "master" (defaults to no pebble versions)
sudo = false # whether to run functional tests with sudo (defaults to false)

[tool.charmlibs.integration]
# tags to run integration tests with (defaults to running once with no tag, i.e. tags = [''])
# Available in CI in tests/integration/pack.sh and integration tests as CHARMLIBS_TAG
tags = [] # Not used by the pack.sh and integration tests generated by the template
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Copyright 2026 Canonical Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Interface library for providing OAuth2 Proxy with downstream charms' auth-proxy information.

It is required to integrate a charm into an Identity and Access Proxy (IAP).

Getting Started
---------------

To install, add `charmlibs-interfaces-auth-proxy` to your Python dependencies.
Then in your Python code, import as:

.. code-block:: python

from charmlibs.interfaces import auth_proxy

**Note that you also need to add ``jsonschema`` to your charm's ``requirements.txt``.**

To use the library from the requirer side, add the following to the ``metadata.yaml`` of the charm:

.. code-block:: yaml

requires:
auth-proxy:
interface: auth_proxy
limit: 1

Then, to initialise the library:

.. code-block:: python

from charmlibs.interfaces.auth_proxy import AuthProxyConfig, AuthProxyRequirer

AUTH_PROXY_ALLOWED_ENDPOINTS = ["welcome", "about/app"]
AUTH_PROXY_HEADERS = ["X-Auth-Request-User", "X-Auth-Request-Email"]
AUTH_PROXY_AUTHENTICATED_EMAILS = ["test@example.com", "test@canonical.com"]
AUTH_PROXY_AUTHENTICATED_EMAIL_DOMAINS = ["canonical.com"]

class SomeCharm(CharmBase):
def __init__(self, *args):
# ...
self.auth_proxy = AuthProxyRequirer(self, self._auth_proxy_config)

@property
def external_urls(self) -> list:
# Get ingress-per-unit or externally-configured web urls
# ...
return ["https://example.com/unit-0", "https://example.com/unit-1"]

@property
def _auth_proxy_config(self) -> AuthProxyConfig:
return AuthProxyConfig(
protected_urls=self.external_urls,
allowed_endpoints=AUTH_PROXY_ALLOWED_ENDPOINTS,
headers=AUTH_PROXY_HEADERS,
authenticated_emails=AUTH_PROXY_AUTHENTICATED_EMAILS,
authenticated_email_domains=AUTH_PROXY_AUTHENTICATED_EMAIL_DOMAINS
)

def _on_ingress_ready(self, event):
self._configure_auth_proxy()

def _configure_auth_proxy(self):
self.auth_proxy.update_auth_proxy_config(auth_proxy_config=self._auth_proxy_config)
"""

from ._auth_proxy import (
AuthProxyConfig,
AuthProxyConfigChangedEvent,
AuthProxyConfigError,
AuthProxyConfigRemovedEvent,
AuthProxyProvider,
AuthProxyRelationRemovedEvent,
AuthProxyRequirer,
InvalidAuthProxyConfigEvent,
)
from ._version import __version__ as __version__

__all__ = [
'AuthProxyConfig',
'AuthProxyConfigChangedEvent',
'AuthProxyConfigError',
'AuthProxyConfigRemovedEvent',
'AuthProxyProvider',
'AuthProxyRelationRemovedEvent',
'AuthProxyRequirer',
'InvalidAuthProxyConfigEvent',
]
Loading
Loading