- Created by Michael Dürre, last modified by Martin Skorupski on Apr 06, 2021
You are viewing an old version of this page. View the current version.
Compare with Current View Page History
« Previous Version 14 Next »
Table of Contents
Motivation
- get rid of the insecure basic authentication of opendaylight for Restconf
- instead implement JsonWebToken(JWT) authorization
- JWT is stateless because its signed
- so roles can be put inside of the token and the token only has to be verified (checked signature) to get the roles of the user)
Problems
- Opendaylight AAA project for aluminium-SR1 is only supporting authorization header starting with "Basic" and JWT is a Bearer token
- So we had to patch the org.opendaylight.aaa:aaa-shiro:0.12.1 bundle with
- some backported classes from org.apache.shiro:shiro-core:1.7 package
- two modifications on the Authenticator to Accept also Bearer tokens
- we realized that an entry in aaa-app-config.xml like
<urls> <pair-key>/**</pair-key> <pair-value>authcBasic, roles["admin,provision"]</pair-value> </urls>
means that the user which wants to access this url pattern needs to have both roles, which does not really make sense. Therefor we also implemented a so called AnyRolesAuthenticationFilter which accepts the connection if one of the given roles matches.
OAuth Provider bundle
API
request | params | response | description |
---|---|---|---|
GET /oauth/providers | OAuthProvider array | list of configured identity providers | |
GET /oauth/redirect | code={}&state={} or session_state={} or token={} | TokenResponse | called by the 301 Response from the identity provider |
POST /oauth/login | username={}&password={} | TokenResponse |
Environment Vars
env | default value | description |
---|---|---|
OAUTH_TOKEN_SECRET | secret | key to sign the token |
OAUTH_TOKEN_ISSUER | ONAP SDNC | |
OAUTH_HOST_URL | null => autodetected | important for reverse proxy use case |
OAUTH_ODLUX_REDIRECT_URI | /odlux/index.html#/oauth?token= | OAuth redirect will be responded |
OAUTH_SUPPORT_ODLUSERS | true | login interface enabled for internal odl configured users |
Dataflow example
for Login with external Identity Provider (KeyCloak)
2:
[{ "id":"keycloak", "title":"OSNL Keycloak Provider", "loginUrl":"http://10.20.11.160:8080/auth/realms/onap/protocol/openid-connect/auth?client_id=odlux.app&response_type=code&scope=openid&redirect_uri=http%3A%2F%10.20.11.159%3A8181%2Foauth%2Fredirect%2Fkeycloak" }]
8:
301 Location: http://10.20.11.159:8181/oauth/redirect/keycloak?state=odlux.app&code=4e4b717f-4a23-4f75-8bf1-76514f4b65dc.b0270d58-d281-4533-910f-19cb938ea189.dbd662ad-e959-44c9-bd18-859ca0142927
10:
POST /auth/realms/onap/protocol/openid-connect/token grant_type: "authorization_code" code: "4e4b717f-4a23-4f75-8bf1-76514f4b65dc.b0270d58-d281-4533-910f-19cb938ea189.dbd662ad-e959-44c9-bd18-859ca0142927" client_id: "odlux.app" client_secret: "5da4ea3d-8cc9-4669-bd7e-3ecb91d120cd" redirect_uri: "http://10.20.11.159:8181/oauth/redirect"
11:
{ "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJkbWFSWXRkaHFkVXFDV2lmRWdNRHFBcWVBcU8tMnFoTDBjdnByelRGdWRRIn0.eyJleHAiOjE2MTExMzU5MjEsImlhdCI6MTYxMTEzNDEyMSwiYXV0aF90aW1lIjoxNjExMTM0MDkxLCJqdGkiOiIzYzFlZmMzZi1lMjFiLTQ3MzktYTY1YS1jNjY1M2ZhOGRjNTQiLCJpc3MiOiJodHRwOi8vMTAuMjAuMTEuMTYwOjgwODAvYXV0aC9yZWFsbXMvb25hcCIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiI0NDZhMjRiYy1kOGEwLTQzZGQtYWZhNS1lNTZlZWQ3NWRlYjgiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJvZGx1eC5hcHAiLCJzZXNzaW9uX3N0YXRlIjoiMTI5YjRhNjMtNzBhMS00MjFmLWEzM2YtOWFjZDkyZTIzM2ZmIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJwcm92aXNpb24iLCJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJvcGVuaWQgcHJvZmlsZSBlbWFpbCIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwibmFtZSI6Ikx1a2UgU2t5d2Fsa2VyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoibHVrZS5za3l3YWxrZXIiLCJnaXZlbl9uYW1lIjoiTHVrZSIsImZhbWlseV9uYW1lIjoiU2t5d2Fsa2VyIiwiZW1haWwiOiJsdWtlLnNreXdhbGtlckBzZG5yLm9uYXAub3JnIn0.tn2NrEGYLRq1u0DkqxD2iDM72hFrDBPGA_q23S-htiRH113yt14a0CzJxU9El0YDobbzog9xm0ELbx6W4jYsGguMABqIi4W5wtTqfbaCh7gmF208CqNpwzA7nG2palMLbBPpmGXiagUm4qLWQxrBP_VOaeW_kK0VHLaiTRJ-4vHuOXSNPYEDQZNCI2QCJQS_dn83K_JI4ecBHl8UeHFLB65BqmocpDHUvf2h835xuNFFQpXJWMcPM_j_FmFQeOSUDM4HmqgdVU9_b4APnDEVFiUezQdoEOfEYNsNlhCoXlaEEn2tCZfEkZ7k72DlhqJMQzomdaGKPk2g8XhKJNwMJg", "expires_in": 1800, "refresh_expires_in": 1800, "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhOGUzMDUwZS0wZmQxLTRjYjQtYjRiZS1jMDVlOGY4OGJhZGUifQ.eyJleHAiOjE2MTExMzU5MjEsImlhdCI6MTYxMTEzNDEyMSwianRpIjoiZmZiYWE3NDktZGVkNi00ZWMzLWI4MjYtYTI4NWY0ODY1ZGI0IiwiaXNzIjoiaHR0cDovLzEwLjIwLjExLjE2MDo4MDgwL2F1dGgvcmVhbG1zL29uYXAiLCJhdWQiOiJodHRwOi8vMTAuMjAuMTEuMTYwOjgwODAvYXV0aC9yZWFsbXMvb25hcCIsInN1YiI6IjQ0NmEyNGJjLWQ4YTAtNDNkZC1hZmE1LWU1NmVlZDc1ZGViOCIsInR5cCI6IlJlZnJlc2giLCJhenAiOiJvZGx1eC5hcHAiLCJzZXNzaW9uX3N0YXRlIjoiMTI5YjRhNjMtNzBhMS00MjFmLWEzM2YtOWFjZDkyZTIzM2ZmIiwic2NvcGUiOiJvcGVuaWQgcHJvZmlsZSBlbWFpbCJ9.mt9VHtiBZycHcEuVCOZVjjtyoOGYNaDVvtcA1NPScIQ", "token_type": "bearer", "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJkbWFSWXRkaHFkVXFDV2lmRWdNRHFBcWVBcU8tMnFoTDBjdnByelRGdWRRIn0.eyJleHAiOjE2MTExMzU5MjEsImlhdCI6MTYxMTEzNDEyMSwiYXV0aF90aW1lIjoxNjExMTM0MDkxLCJqdGkiOiJjZjUzZTc0ZC1kYjZiLTQ4YTUtODkyOS1jYzU3YjY3YjAxN2QiLCJpc3MiOiJodHRwOi8vMTAuMjAuMTEuMTYwOjgwODAvYXV0aC9yZWFsbXMvb25hcCIsImF1ZCI6Im9kbHV4LmFwcCIsInN1YiI6IjQ0NmEyNGJjLWQ4YTAtNDNkZC1hZmE1LWU1NmVlZDc1ZGViOCIsInR5cCI6IklEIiwiYXpwIjoib2RsdXguYXBwIiwic2Vzc2lvbl9zdGF0ZSI6IjEyOWI0YTYzLTcwYTEtNDIxZi1hMzNmLTlhY2Q5MmUyMzNmZiIsImF0X2hhc2giOiJSUXdDclpkQmFKV0VFdmxsRVNxRjV3IiwiYWNyIjoiMSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwibmFtZSI6Ikx1a2UgU2t5d2Fsa2VyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoibHVrZS5za3l3YWxrZXIiLCJnaXZlbl9uYW1lIjoiTHVrZSIsImZhbWlseV9uYW1lIjoiU2t5d2Fsa2VyIiwiZW1haWwiOiJsdWtlLnNreXdhbGtlckBzZG5yLm9uYXAub3JnIn0.rueTNrnvRa4PMo7NS8l4xxRhhNiGzXLmtcUeyWnj3AjFaUoNKuS9l85K3KjRT3zjq494YsepIGuK33I20rvFwDLclcJNHuumAgBnR5dRBi5fLhm7x8YkebhdTHPiYL4hfygpZ7APN1PtcDZnb-uEjjT-RAtjnfk3r-oP6CtqWzI5MjOPnf5HaEwWpkuTjmJf3kyyf_pdhhVkgTwuC-kD8iMjyRIzuZJxVwWVA3S43eL0R7MaIDlpJrOp9EBRfMlObAypc1bLtKwopT0sBla1CM9GmUU2ZYbQb79-hey0rd7CWx1uBkZUxt5myiExBm3pI46boXLP7dzjzxHUKg0m-A", "not-before-policy": 1611134054, "session_state": "129b4a63-70a1-421f-a33f-9acd92e233ff", "scope": "openid profile email" }
which can be decoded to:
{ "exp": 1611135921, "iat": 1611134121, "auth_time": 1611134091, "jti": "3c1efc3f-e21b-4739-a65a-c6653fa8dc54", "iss": "http://10.20.11.160:8080/auth/realms/onap", "aud": "account", "sub": "446a24bc-d8a0-43dd-afa5-e56eed75deb8", "typ": "Bearer", "azp": "odlux.app", "session_state": "129b4a63-70a1-421f-a33f-9acd92e233ff", "acr": "1", "realm_access": { "roles": [ "provision", "offline_access", "uma_authorization" ] }, "resource_access": { "account": { "roles": [ "manage-account", "manage-account-links", "view-profile" ] } }, "scope": "openid profile email", "email_verified": false, "name": "Luke Skywalker", "preferred_username": "luke.skywalker", "given_name": "Luke", "family_name": "Skywalker", "email": "luke.skywalker@sdnr.onap.org" }
where /real_access/roles are the important ones for us which were configured in the keycloak backend.
Hint: offline_access and uma_authorization are built-in keycloak roles. These ones are filtered by oauth-provider bundle. So delivered role in this case is only provision.
The Opendaylight Roles access problem
As described on top we found out that an entry in aaa-app-config.xml like
<urls> <pair-key>/**</pair-key> <pair-value>authcBasic, roles["admin,provision"]</pair-value> </urls>
results in a restriction for the configured url that the user has to be in both rules. That's why we implement a new Filter AnyRoleHttpAuthenticationFilter
. That means if you enable it for a url you just have to be in at least one of this groups to get access.
<main> <pair-key>anyroles</pair-key> <pair-value>org.opendaylight.aaa.shiro.filters.AnyRoleHttpAuthenticationFilter</pair-value> </main>
So usage changes to:
<urls> <pair-key>/**</pair-key> <pair-value>authcBasic, anyroles["admin,provision"]</pair-value> </urls>
Configuration
ConfigFile $ODL_HOME/etc/oauth-provider.config.json
{ "tokenSecret": "${OAUTH_TOKEN_SECRET}", "tokenIssuer": "${OAUTH_TOKEN_ISSUER}", "publicUrl": "", "redirectUri": "${OAUTH_ODLUX_REDIRECT_URI}", "supportOdlUsers": "${OAUTH_SUPPORT_ODLUSERS}", "providers": [] }
key | default | description |
---|---|---|
tokenSecret | randomgeneratedString() | secret to create JWT |
tokenIssuer | "Opendaylight" | issuer for JWT |
publicUrl | autodetect() | url on which odlux webserver is reachable for you. Attention!!!! Be aware behind reverse proxy!! pls set to your reverse proxy url |
redirectUri | "/odlux/index.html#/oauth?token=" | redirect after successful oauth login |
supportOdlUsers | "true" | enable login of internal odl configured users |
Gitlab as a OAuth provider
{ "tokenSecret": "${OAUTH_TOKEN_SECRET}", "tokenIssuer": "${OAUTH_TOKEN_ISSUER}", "publicUrl": "", "redirectUri": "${OAUTH_ODLUX_REDIRECT_URI}", "supportOdlUsers": "true", "providers": [ { "id": "mygit", "type": "GITLAB", "url": "https://my-gitlab-server.com", "clientId": "db312fb791ebc97fd199df1569ebbd45916f52444bb75", "secret": "d376abb4524bc7fbd80833ad34f649584624e0c2b791da", "scope": "api+openid+read_user+profile", "title": "my Gitlab", "roleMapping":{ "mygitlabgroup":"admin" } } ] }
key | description |
---|---|
id | identifier for provider-entry ( regex: [ a-zA-Z0-9]+ ) |
type | implementation-type GITLAB | KEYCLOAK | NEXTCLOUD |
url | url of server |
clientId | shared client-id between OAuth provider and Oauth client |
secret | shared secret between OAuth provider and Oauth client |
scope | enabled scopes on oauth-provider side |
title | title shown in odlux GUI |
roleMapping | HashMap for roles from oauth-provider to odl { "oauth-provider-role":"odl-role" } |
- No labels