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

[Feature Request] Expose Authorization: Bearer token when integrated with proxies. #2922

Open
bkupidura opened this issue Feb 28, 2022 · 5 comments
Labels
area/openid-connect OpenID Connect 1.0 / OAuth 2.0 related features/bugs type/feature Request for adding a new feature

Comments

@bkupidura
Copy link

bkupidura commented Feb 28, 2022

Description

Currently Authelia send couple of HTTP headers (Remote-User, Remote-Name, Remote-Email, Remote-Groups) to backend when used with proxy.
Right now we have some OIDC support it Authelia, it would be nice if we would be able to provide Authorization: Bearer <token> header also. should be just JWT token, same as returned by Authelia when OIDC is used.

This way, our backend apps could authorize users based on data provided by Authelia.

Use Case

Currently im using Authelia as SSO, Traefik as ingress and OAuth2-Proxy.

OAuth2-Proxy is only used to fetch token from Authelia (OIDC) and add Authorization Header. After that my backend apps (in this example kubernetes-dashboard), will use this token to authorize requests.

Im using k3s as kubernetes platform, some examples here are my environment specific - but they should present general vision. Im using ansible to render all k8s yaml files, so inside there are variables used.

Authorization flow:

  1. Users hits Traefik ingress for k8s dashboard (k8s.{{ global.domain }})
  2. No auth cookie is present in request, Traefik forwards request (ForwardAuth) to Oauth2-Proxy
  3. Oauth2-Proxy authorize user with Authelia over OIDC
  4. Oauth2-Proxy sets Authorization header in request
  5. Traefik forwards authorized request do k8s dashboard
  6. k8s dashboards uses Authorization header and pass it to k8s api
  7. k8s API validates Authorization header with Authelia (validate JWT token)
  8. K8s API maps token properties (email and groups) to k8s User resource
  9. K8s with ClusterRoleBinding provide access to API based on data from token

Traefik middlewares:

---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: auth-oauth-headers
  namespace: traefik-system
spec:
  headers:
    sslRedirect: true
    stsSeconds: 315360000
    browserXssFilter: true
    contentTypeNosniff: true
    forceSTSHeader: true
    stsIncludeSubdomains: true
    stsPreload: true
    frameDeny: true

---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: auth-oauth-proxy
  namespace: traefik-system
spec:
  forwardAuth:
    address: https://oauth-proxy.{{ global.domain }}/
    trustForwardHeader: true
    authResponseHeaders:
      - Authorization
      - X-Auth-Request-User
      - X-Auth-Request-Email
      - X-Auth-Request-Preferred-Username
      - X-Auth-Request-Groups

Traefik IngressRoutes:

---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: oauth-proxy
  namespace: home-infra
spec:
  entryPoints:
  - websecure
  routes:
  - match: Host(`oauth-proxy.{{ global.domain }}`)
    kind: Rule
    services:
      - name: oauth2-proxy
        port: 80
        namespace: home-infra
    middlewares:
      - name: auth-oauth-headers
        namespace: traefik-system
  tls:
    secretName: "{{ global.domain | replace('.', '-') }}-tls"

---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: k8s-dashboard
  namespace: kube-system
spec:
  entryPoints:
  - websecure
  routes:
  - match: Host(`k8s.{{ global.domain }}`)
    kind: Rule
    services:
      - name: k8s-dashboard-kubernetes-dashboard
        port: 443
        namespace: kube-system
    middlewares:
      - name: auth-oauth-proxy
        namespace: traefik-system
  - match: Host(`k8s.{{ global.domain }}`) && PathPrefix(`/oauth2/`)
    kind: Rule
    services:
      - name: oauth2-proxy
        port: 80
        namespace: home-infra
    middlewares: 
      - name: auth-oauth-headers
        namespace: traefik-system
  tls:
    secretName: "{{ global.domain | replace('.', '-') }}-tls"

Oauth2-Proxy:

---
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
  name: oauth2-proxy
  namespace: kube-system
spec:
  chart: oauth2-proxy
  repo: https://oauth2-proxy.github.io/manifests
  version: {{ manifest.versions.oauth2_proxy.chart }}
  targetNamespace: home-infra
  valuesContent: |-
    extraArgs:
      provider: oidc
      provider-display-name: "Authelia OIDC Provider"
      redirect-url: https://oauth-proxy.{{ global.domain }}/oauth2/callback
      oidc-issuer-url: https://auth.{{ global.domain }}
      reverse-proxy: true
      whitelist-domain: .{{ global.domain }}
      cookie-domain: .{{ global.domain }}
      set-xauthrequest: true
      upstream: static://202
      silence-ping-logging: true
      set-authorization-header: true
      scope: "openid profile email groups"
    config:
      clientID: {{ manifest.oauth2_proxy.client_id }}
      clientSecret: {{ manifest.oauth2_proxy.client_secret }}
      cookieSecret: {{ manifest.oauth2_proxy.cookie_secret }}

K8s dashboard:

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kube-system

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kube-system
- kind: Group
  name: oidc:admin
  apiGroup: rbac.authorization.k8s.io

---
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
  name: k8s-dashboard
  namespace: kube-system
spec:
  chart: kubernetes-dashboard
  repo: https://kubernetes.github.io/dashboard/
  version: {{ manifest.versions.k8s_dashboard.chart }}
  targetNamespace: kube-system

K8s api:
We need to point k8s-api to Authelia, so K8s will validate Bearer token:

Add those parameter to k3s startup script

	'--kube-apiserver-arg' \
	'oidc-issuer-url=https://auth.{{ global.domain }}' \
	'--kube-apiserver-arg' \
	'oidc-client-id=oauth2-proxy' \
	'--kube-apiserver-arg' \
	'oidc-username-claim=email' \
	'--kube-apiserver-arg' \
	'oidc-username-prefix=oidc:' \
	'--kube-apiserver-arg' \
	'oidc-groups-claim=groups' \
	'--kube-apiserver-arg' \
	'oidc-groups-prefix=oidc:' \

Authelia config:

---
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: home-infra
  name: authelia-conf
data:
  users.yml: |-
    users:
      {{ manifest.authelia.users | to_nice_yaml | indent(6, false) }}
  configuration.yml: |-
    identity_providers:
       oidc:
          access_token_lifespan: 1h
          authorize_code_lifespan: 1m
          clients:
          -   audience: []
              authorization_policy: two_factor
              description: OAuth2 Proxy
              grant_types:
              - refresh_token
              - authorization_code
              id: oauth2-proxy
              public: false
              redirect_uris:
              - https://oauth-proxy.{{ global.domain }}/oauth2/callback
              response_modes:
              - form_post
              - query
              - fragment
              response_types:
              - code
              scopes:
              - openid
              - groups
              - email
              - profile
              secret: {{ secret }}
              userinfo_signing_algorithm: none
          enable_client_debug_messages: true
          hmac_secret: {{ secret }}
          id_token_lifespan: 1h
          issuer_private_key: {{ secret }}
          refresh_token_lifespan: 90m
    authentication_backend:
      disable_reset_password: true
      file:
        path: /config/users.yml
        password:
          algorithm: argon2id
          iterations: 1
          salt_length: 16
          parallelism: 8
          memory: 64
    session:
      domain: {{ global.domain }}
      expiration: 3600
      remember_me_duration: 2592000
    notifier:
      disable_startup_check: true
      {{ manifest.authelia.notifier | to_nice_yaml | indent(6, false) }}
    storage:
      encryption_key: {{ manifest.authelia.storage.encryption_key }}
      local:
        path: /data/db.sqlite
    access_control:
      default_policy: deny
      rules:
        - domain: '*.{{ global.domain }}'
          subject: 'group:admin'
          policy: two_factor
    regulation:
      max_retries: 3
      find_time: 300
      ban_time: 900
    default_redirection_url: https://auth.{{ global.domain }}
    jwt_secret: {{ manifest.authelia.jwt_secret }}

Whats wrong with this setup?

  1. Im unable to use Authelia as ForwardAuth middleware in Traefik. Because of that i lose e.g Access Control.
  2. Authelia in this scenario is used just as to store users credentials and for token validation by backend.
  3. All things currently handled by Oauth2-Proxy can be easly and better handled by Authelia - we just need to return Authorization header to proxy and remove Oauth2-Proxy, and Oauth2 middlewares from the picture.
@bkupidura bkupidura added the type/feature Request for adding a new feature label Feb 28, 2022
@obikay200
Copy link

I am also facing a similar requirement, being able to send the jwt down stream for validation and rbac purposes is very useful feature to have!
Do we have any ideas when this could be looked at?

@james-d-elliott
Copy link
Member

This is something we've been thinking about however it's more important to get it right than to get the feature implemented quickly in this instance.

I think the best solution will involve us creating an internal OIDC forward auth handler that allows Authelia performing the OpenID Connect Authorization flow itself. The client config would have to include an additional redirect URL, and the forward auth endpoint would include OIDC parameters. The Authorization Bearer header would just be a response header to the forward auth endpoint for the proxy to staple with the request similar to Remote-User etc.

There are other ways to do it but I think enforcing the Authorization flow is ideal as it will prevent issues down the line and probably invoke less bugs.. it's just going to be quite a bit of work.

@james-d-elliott james-d-elliott added the area/openid-connect OpenID Connect 1.0 / OAuth 2.0 related features/bugs label Aug 7, 2022
@eloo
Copy link

eloo commented Sep 21, 2023

Hi there,
i was just trying to secure my kubernetes dashboard with authelia then i have found this pretty old issue :(

is there any progress to track if this going to happen?
Pretty odd that i can not use such a great tool like authelia to authenticate myself against a kubernetes dashboard.

@deefdragon
Copy link

@james-d-elliott This should now function with the changes made in #6774 right? Ive been messing with it and while I now have the ability to get to the dashboard page following the docs here, but have been unable to get the Authorization header token to included as Im retuned the kubernetes-dashboard sign in modal.

Im currently stuck wondering if Im misinterpreting what "Access Tokens can be granted which can be leveraged as bearer tokens for the purpose of authorization" means and its not actually supported yet? or am I doing it right on the authelia side, but am messing up my traefik proxy or is my authelia config just wrong...

Im basically stuck not knowing how to debug from here.

@james-d-elliott
Copy link
Member

Those changes are not intended for this specific purpose, we would have closed it while merging it (code rabbit mistakenly identified it as related but it isn't).

They are for authenticating with Authelia via token for the proxy integration. It'll make sense when you look at how you can use the client credentials grant type with it. It is something I'd like to do but I'm not 100% sure of the way I want to achieve it.

I'm relatively sure the way k8s works and oauth2 proxy works is it uses the ID Token in bearer auth.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/openid-connect OpenID Connect 1.0 / OAuth 2.0 related features/bugs type/feature Request for adding a new feature
Projects
None yet
Development

No branches or pull requests

5 participants