Authentication with Emco
EMCO uses Istio and other open source solutions to provide Multi-tenancy solution leveraging Istio Authorization and Authentication frameworks. This is achieved without adding any logic in EMCO microservices. Authentication for the EMCO users are done at the Isito Gateway, where all the traffic enters the cluster. Istio along with autherservice (istio ecosystem project) enables request-level authentication with JSON Web Token (JWT) validation. This can be achieved using a custom authentication provider or any OpenID Connect providers like KeyCloak, Auth0 etc.
In ONAP4K8s no security (Mutual TLS, Authentication and Authorization) and traffic management (Load balancing, Circuit breaking, Traffic control & rate limiting) are not part of the ONAP4K8s micro-services. Also, log collection, metrics collection and distributed tracing for troubleshooting are all not part of the ONAP4K8s micro-services. CNCF architecture is used for these to improve productivity and reduce the errors.
To achieve the above goals ISTIO is used by ONAP4K8s for providing following:
...
Authservice is an entity that works along side with Envoy proxy. It is used to work with external IAM systems (OAUTH2). Many Enterprises have their own OAUTH2 server for authenticating users and provide roles. ONAP4K8s uses Authservice from ISTIO-ingress proxy to talk to along with Istio-ingress and Authservice use single or multiple OAUTH2 servers, one belonging to each project (Enterprise).
Image Removed
Steps for setting up ONAP4K8s with Istio + Authservice
Keycloak
Keycloak is an open source software product to allow single sign-on with Identity Management and Access Management. Keycloak is being used here as an example of IAM service to be used with EMCO.
In a kubernetes cluster where Keycloak is going to be installed follow these steps to create keyclock deployment:
Keyloak deployment file for Kubernetes is available: https://raw.githubusercontent.com/keycloak/keycloak-quickstarts/latest/kubernetes-examples/keycloak.yaml
border | true |
---|
| |
---|
diagramName | v2 API Authentication |
---|
simpleViewer | false |
---|
width | |
---|
links | auto |
---|
tbstyle | top |
---|
lbox | true |
---|
diagramWidth | 719 |
---|
revision | 3 |
---|
|
draw.io Diagram |
---|
border | true |
---|
| |
---|
diagramName | v2 API Authentication with multiple external OAUTH2 servers |
---|
simpleViewer | false |
---|
width | |
---|
links | auto |
---|
tbstyle | top |
---|
lbox | true |
---|
diagramWidth | 731 |
---|
revision | 3 |
---|
|
Authentication Flow with OIDC, Istio Ingress Gateway and Authservice
PlantUML Macro |
---|
|
@startuml
User -> IstioIngress: Access EMCO Rest API's
IstioIngress --> Authservice: Forwared
Authservice -> User: Redirect to User Browser with Keycloak URL
User -> Keycloak: User Browser taken to Keycloack URL
Keycloak -> User: Login Page user prompted to enter credentials
Keycloak -> User: Redirect to User Browser with Authentication code
User -> IstioIngress: Access EMCO Rest API's with authentication code
IstioIngress --> Authservice: Forwarded
Authservice -> Keycloak: Exchange authentication code for access token
Keycloak -> Authservice: Token
Authservice -> User: Redirect to original URL along with token
User -> IstioIngress: Access original URL with token
IstioIngress -> EMCOv2API: Verify token & allow access
@enduml |
Authorization with Emco
Emco uses Istio's AuthorizationPolicy resource to manage authorizations. See at the end of this post for example of Authorization policies.
Steps for setting up ONAP4K8s with Istio + Authservice
Keycloak
Keycloak is an open source software product to allow single sign-on with Identity Management and Access Management. Keycloak is being used here as an example of IAM service to be used with EMCO.
In a kubernetes cluster where Keycloak is going to be installed follow these steps to create keyclock deployment:
Keyloak deployment file for Kubernetes is available: https://raw.githubusercontent.com/keycloak/keycloak-quickstarts/latest/kubernetes-examples/keycloak.yaml
Code Block |
---|
language | yml |
---|
title | Keycloak Installation |
---|
|
kubectl create ns keycloak
kubectl create -n keycloak secret tls ca-keycloak-certs --key keycloak.key --cert keycloak.crt
kubectl apply -f keycloak.yaml -n keycloak |
Code Block |
---|
language | yml |
---|
title | Keycloak Yaml |
---|
|
apiVersion: v1
kind: Service
metadata:
name: keycloak
labels:
app: keycloak
spec:
ports:
- name: http
port: 8080
targetPort: 8080
- name: https
port: 8443
targetPort: 8443
selector:
app: keycloak
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: keycloak
labels:
app: keycloak
spec:
replicas: 1
selector:
matchLabels:
app: keycloak
template:
metadata:
labels:
app: keycloak
spec:
containers:
- name: keycloak
image: quay.io/keycloak/keycloak:9.0.2
volumeMounts:
- name: keycloak-certs
mountPath: /etc/x509/https
readOnly: false
env:
- name: KEYCLOAK_USER
value: "admin"
- name: KEYCLOAK_PASSWORD
value: "admin"
- name: PROXY_ADDRESS_FORWARDING
value: "true"
ports:
- name: http
containerPort: 8080
- name: https
containerPort: 8443
readinessProbe:
httpGet:
path: /auth/realms/master
port: 8080
volumes:
- name: keycloak-certs
secret:
secretName: keycloak-certs
defaultMode: 420
optional: true
|
Code Block |
---|
language | yml |
---|
title | Keycloak Installation |
---|
|
kubectl create ns keycloak
kubectl create -n keycloak secret tls ca-keycloak-certs --key keycloak.key --cert keycloak.crt
kubectl apply -f keycloak.yaml -n keycloak |
Create a realm, add users and roles to Keycloak
- Create a new Realm - ex: enterprise1
- Add Users
- Create a new Client under realm name - ex: emco
- Under Setting for client
- Change assess type for client to confidential
- Under Authentication Flow Overrides - Change Direct grant flow to direct grant
- Update Valid Redirect URIs.
- In Roles tab:
- Add roles (ex. Admin and User)
- Under Users assign roles from emco client to users ( Admin and User). Verify under Emco Client roles for user are in the roleAdmin and User). Verify under Emco Client roles for user are in the role
- Add Mappers
- Under Emco Client under mapper tab create a mapper
- Mapper type - User Client role
- Client-ID: emco
- Token claim name: role
- Claim JSON Type: string
For complete documentation of Keycloak refer to these links:
...
Code Block |
---|
language | yml |
---|
title | EMCO Installation |
---|
|
stioctl kube-inject -f ovn4k8sdb.yaml | kubectl apply -f -
istioctl kube-inject -f ovn4k8s.yaml | kubectl apply -f -
kubectl create -n istio-system secret tls emco-credential --key=v2.key --cert=v2.crt
|
...
Gateway
Code Block |
---|
|
$ kubectl create -n istio-system secret tls emco-credential --key=v2.key --cert=v2.crt
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: emco-gateway
namespace: istio-system
spec:
selector:
istio: ingressgateway # use Istio default gateway implementation
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: emco-credential
hosts:
- "*"
|
...
Virtual service
Code Block |
---|
language | yml |
---|
title | Virtual Service |
---|
|
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: orchestrator
namespace: emco
spec:
hosts:
- "*"
gateways:
- emco-gateway.istio-system.svc.cluster.local
http:
- match:
- uri:
prefix: /v2/oauth
- uri:
prefix: /v2
route:
- destination:
port:
number: 9015
host: orchestrator
|
Make sure the EMCO service is accessible through istio ingress gateway at this point. [https://<Istio Ingress service IP Address:port>/v2/projects]
...
Istio Policy
Code Block |
---|
language | yml |
---|
title | Authentication Policy |
---|
|
apiVersion: "authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
name: "emco-authn-policy"
namespace: istio-system
spec:
origins:
- jwt:
issuer: "https://<Keycloak IP Address:port>/auth/realms/enterprise1"
jwksUri: "http://<Keycloak IP Address:port>/auth/realms/enterprise1/protocol/openid-connect/certs"
principalBinding: USE_ORIGIN
|
...
Authservice Setup in Istio Ingress-gateway
...
Authservice
...
Configmap
The following example shows how to setup authservice with keycloak.
...
Code Block |
---|
language | yml |
---|
title | Authservice Container |
---|
|
$ kubectl edit deployments istio-ingressgateway -n istio-system
Under containers section add:
- name: authservice
image: adrianlzt/authservice:0.3.1-d3cd2d498169
imagePullPolicy: Always
ports:
- containerPort: 10003
volumeMounts:
- name: emco-authservice-configmap-volume
mountPath: /etc/authservice
In the volumes section add:
- name: emco-authservice-configmap-volume
configMap:
name: emco-authservice-configmap
|
...
EnvoyFilter Resource for authservice
Code Block |
---|
language | yml |
---|
title | Envoy Filter |
---|
|
#
# Add the ext_authz filter to the istio-ingressgateway Envoy filter chain.
# Configure the ext_authz filter to ask the authservice about every incoming request
# via GRPC. For every incoming request, the authservice will decide to either allow
# the request and add tokens as headers, or will cause the response to redirect for
# authentication.
#
---
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: sidecar-token-service-filter-for-ingress
namespace: istio-system
spec:
workloadSelector:
labels:
istio: ingressgateway
app: istio-ingressgateway
configPatches:
- applyTo: HTTP_FILTER
match:
context: GATEWAY
listener:
filterChain:
filter:
name: "envoy.http_connection_manager"
subFilter:
name: "envoy.filters.http.jwt_authn"
patch:
operation: INSERT_BEFORE
value:
name: envoy.ext_authz
config:
stat_prefix: ext_authz
grpc_service:
envoy_grpc:
cluster_name: ext_authz
timeout: 10s # Timeout for the entire request (including authcode for token exchange with the IDP)
- applyTo: CLUSTER
match:
context: ANY
cluster: {} # this line is required starting in istio 1.4.0
patch:
operation: ADD
value:
name: ext_authz
connect_timeout: 5s # This timeout controls the initial TCP handshake timeout - not the timeout for the entire request
type: LOGICAL_DNS
lb_policy: ROUND_ROBIN
http2_protocol_options: {}
load_assignment:
cluster_name: ext_authz
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 127.0.0.1
port_value: 10003
|
...
The following changes are required if different OAuth2 servers are needed for different projects. All other configurations remain the same.
...
Virtual service to support multiple servers
Code Block |
---|
language | yml |
---|
title | Virtual Service |
---|
|
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: orchestrator
namespace: test
spec:
hosts:
- "*"
gateways:
- orchestrator-gateway
http:
- match:
- uri:
prefix: /v2/oauth
- uri:
prefix: /v2
- uri:
prefix: /v2/projects/enterprise1/oauth
-uri:
prefix: /v2/projects/enterprise2/oauth
route:
- destination:
port:
number: 9015
host: orchestrator
|
...
Authentication Policy with multiple servers
Code Block |
---|
language | yml |
---|
title | Authentication Policy |
---|
|
---
apiVersion: "authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
name: "orchestrator-authn-policy"
namespace: istio-system
spec:
origins:
- jwt:
issuer: "https://x.x.x.x:31567<url>/auth/realms/enterprise1"
jwksUri: "http://x.x.x.x:32431<url>/auth/realms/enterprise1/protocol/openid-connect/certs"
- jwt:
issuer: "https://x.x.x.x:31567<url>/auth/realms/enterprise2"
jwksUri: "http://x.x.x.x:32431<url>/auth/realms/enterprise2/protocol/openid-connect/certs"
principalBinding: USE_ORIGIN
|
...
The following example shows how to setup authservice with multiple OAUTH2 keycloak servers.
Code Block |
---|
language | yml |
---|
title | Authservice configmap |
---|
|
---
kind: ConfigMap
apiVersion: v1
metadata:
name: emco-authservice-configmap
namespace: istio-system
data:
config.json: |
{
"listen_address": "127.0.0.1",
"listen_port": "10003",
"log_level": "trace",
"threads": 8,
"chains": [
{
"name": "idp_filter_chain_1",
"match": {
"header": ":path",
"prefix": "/v2/projects/enterprise1"
},
"filters": [
{
"oidc":
{
"authorization_uri": "https://x.x.x.x:<port>/auth/realms/enterprise1/protocol/openid-connect/auth",
"token_uri": "https://x.x.x.x:<port>/auth/realms/enterprise1/protocol/openid-connect/token",
"callback_uri": "https://x.x.x.x:<port>/v2/projects/enterprise1/oauth/callback",
"jwks": "{\"keys\":[{\"kid/enterprise1/oauth/callback",
"jwks": "{\"keys\":[{\"kid\":\"xxxxx\",\"kty\":\"RSA\",\"alg\":\"RS256\",\"use\":\"sig\",\"n\":\"zzzzzzz\",\"e\":\"xxxxxAQAB\",\"ktyx5c\":[\"RSAxxxxxx\"],\"algx5t\":\"RS256z7Qrc2nAlK8EVmkiKtz0bOWxugE\",\"x5t#S256\":\"usexxxxxxxxx\":\"sig\",\"n\":\"zzzzzzz\",\"e\":\"AQAB\",\"x5c\":[\"xxxxxx\"],\"x5t\":\"z7Qrc2nAlK8EVmkiKtz0bOWxugE\",\"x5t#S256\":\"xxxxxxxxx\"}]}""}]}",
"client_id": "emco",
"client_secret": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"trusted_certificate_authority": "-----BEGIN CERTIFICATE-----\r\nxxxxxxxx\r\n-----END CERTIFICATE-----\r\n",
"scopes": [],
"clientid_idtoken": "emco",{
"client_secretpreamble": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxBearer",
"trusted_certificate_authority "header": "-----BEGIN CERTIFICATE-----\r\nxxxxxxxx\r\n-----END CERTIFICATE-----\r\n",Authorization"
"scopes": []},
"idaccess_token": {
"preamble": "Bearer",
"header": "Authorization"
}
},
}
"access_token": {
]
},
{
"preamblename": "Beareridp_filter_chain_2",
"match": {
"header": "Authorization:path",
}
"prefix": "/v2/projects/enterprise2"
},
}"filters": [
]{
},
"oidc":
{
"name": "idp_filter_chain_2", {
"match": {
"authorization_uri": "https://x.x.x.x:<port>/auth/realms/enterprise2/protocol/openid-connect/auth",
"header": ":path",
"prefixtoken_uri": "/v2/projects/enterprise2"
https://x.x.x.x:<port>/auth/realms/enterprise2/protocol/openid-connect/token",
},
"filterscallback_uri": [
"https://x.x.x.x:<port>/v2/projects/enterprise2/oauth/callback",
{
"oidcjwks":
{
"authorization_uri": "https://x.x.x.x:<port>/auth/realms/enterprise2/protocol/openid-connect/auth",
"token_uri": "https://x.x.x.x:<port>/auth/realms/enterprise2/protocol/openid-connect/token "{\"keys\":[{\"kid\":\"xxxx\",\"kty\":\"RSA\",\"alg\":\"RS256\",\"use\":\"sig\",\"n\":\"xxxx\",\"e\":\"AQAB\",\"x5c\":[\"xxxxxx\"],\"x5t\":\"xxxxxxx\",\"x5t#S256\":\"xxxxxxx\"}]}",
"callbackclient_uriid": "https://x.x.x.x:<port>/v2/projects/enterprise2/oauth/callbackemco",
"jwksclient_secret": "{\"keys\":[{\"kid\":\"xxxx\",\"kty\":\"RSA\",\"alg\":\"RS256\",\"use\":\"sig\",\"n\":\"xxxx\",\"e\":\"AQAB\",\"x5c\":[\"xxxxxx\"],\"x5t\":\"xxxxxxx\",\"x5t#S256\":\"xxxxxxx\"}]}"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"trusted_certificate_authority": "-----BEGIN CERTIFICATE-----\r\nxxxxxxxx\r\n-----END CERTIFICATE-----\r\n",
"scopes": [],
"id_token": {
"preamble": "Bearer",
"header": "Authorization"
},
"clientaccess_idtoken": "emco",{
"client_secretpreamble": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxBearer",
"trusted_certificate_authorityheader": "-----BEGIN CERTIFICATE-----\r\nxxxxxxxx\r\n-----END CERTIFICATE-----\r\n",
Authorization"
}
"scopes": [],
}
}
"id_token": { ]
}
"preamble": "Bearer",
"header": "Authorization"]
} |
Authorization Policies with Istio
As specified in Keycloak section Role Mappers are created using Keycloak. These can be used apply authorizations for users. Some examples the can used:
Code Block |
---|
language | yml |
---|
title | Authorization Policies |
---|
|
apiVersion: "security.istio.io/v1beta1"
kind: AuthorizationPolicy
metadata:
name: allow-admin
namespace: istio-system
spec:
selector:
matchLabels:
},app: istio-ingressgateway
action: ALLOW
rules:
- "access_token"when:
{
- key: request.auth.claims[role]
values: ["ADMIN"]
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
"preamble"name: "Bearer",allow-user
namespace: istio-system
spec:
selector:
matchLabels:
"header"app: "Authorization"istio-ingressgateway
action: ALLOW
rules:
- to:
- }operation:
}paths: ["/v2/projects/enterprise1/*"]
when:
- }key: request.auth.claims[role]
]
values: ["USER"] }
]
} |