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:
- Mutual TLS among the internal micro-services.
- Terminate connections coming from external entities (Ingress)
- Traffic Management - Load balancing & Circuit breaking.
- Observability along with Kiali.
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 multiple OAUTH2 servers, one belonging to each project (Enterprise).
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
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 role
For complete documentation of Keycloak refer to these links:
https://www.keycloak.org/getting-started/getting-started-kube
https://developers.redhat.com/blog/2020/01/29/api-login-and-jwt-token-generation-using-keycloak/
Emco Setup with Istio
In a kubernetes cluster where EMCO is going to be run install Istio Demo Profile:
https://istio.io/latest/docs/setup/install/standalone-operator/
Istio version to use is 1.5.3
Install Emco with side car injection
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
Create Gateway
$ 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: - "*"
Create 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]
Create 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
Now when you try to assess EMCO you'll get 403 error. [https://<Istio Ingress service IP Address:port>/v2/projects]
Authservice Setup in Istio Ingress-gateway
Setup configmap required by Authservice.
The following example shows how to setup authservice with keycloak.
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", "filters": [ { "oidc": { "authorization_uri": "https://<Keycloak IP Address:port>/auth/realms/enterprise1/protocol/openid-connect/auth", "token_uri": "https://<Keycloak IP Address:port>/auth/realms/enterprise1/protocol/openid-connect/token", "callback_uri": "https://<Istio Ingress service IP Address:port>/v2/oauth/callback", "jwks": "{Escaped Json output of the command --> curl http://<Keycloak IP Address:port>/auth/realms/enterprise1/protocol/openid-connect/certs}", "client_id": "emco", "client_secret": "Copy secret from keycloak", "trusted_certificate_authority": "-----BEGIN CERTIFICATE-----CA Certificate for the keycloak server in escaped format----END CERTIFICATE-----", "scopes": [], "id_token": { "preamble": "Bearer", "header": "Authorization" }, "access_token": { "preamble": "Bearer", "header": "Authorization" } } } ] } ] }
Install Authservice with the Isito-Ingress gateway
In this setup Authservice is getting setup at the Isito-Ingress gateway level. Refer this link for details:
Currently, there is not yet a native way to install Authservice into the Istio Ingress-gateway. We are manually modifying the Deployment
of istio-ingressgateway
to add the Authservice container. Add the contianer below. Note: Change the container section in ingress-gateway deployment to make it possible to add multiple containers.
$ 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
Create EnvoyFilter Resource for authservice
# # 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
Try accessing EMCO URL agian [https://<Istio Ingress service IP Address:port>/v2/projects]. This will take you to the Keycloak login page and from there user can get authenticated before allowed to access EMCO resources.
Setup with multiple OAuth2 Servers.
The following changes are required if different OAuth2 servers are needed for different projects. All other configurations remain the same.
Create virtual service to support multiple servers
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
Create Authentication Policy with multiple servers
--- 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/auth/realms/enterprise1" jwksUri: "http://x.x.x.x:32431/auth/realms/enterprise1/protocol/openid-connect/certs" - jwt: issuer: "https://x.x.x.x:31567/auth/realms/enterprise2" jwksUri: "http://x.x.x.x:32431/auth/realms/enterprise2/protocol/openid-connect/certs" principalBinding: USE_ORIGIN
Setup configmap required by Authservice.
The following example shows how to setup authservice with keycloak.
--- 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:31567/auth/realms/enterprise1/protocol/openid-connect/auth", "token_uri": "https://x.x.x.x:31567/auth/realms/enterprise1/protocol/openid-connect/token", "callback_uri": "https://x.x.x.x:31063/v2/projects/enterprise1/oauth/callback", "jwks": "{\"keys\":[{\"kid\":\"xxxxx\",\"kty\":\"RSA\",\"alg\":\"RS256\",\"use\":\"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": [], "id_token": { "preamble": "Bearer", "header": "Authorization" }, "access_token": { "preamble": "Bearer", "header": "Authorization" } } } ] }, { "name": "idp_filter_chain_2", "match": { "header": ":path", "prefix": "/v2/projects/enterprise2" }, "filters": [ { "oidc": { "authorization_uri": "https://x.x.x.x:31567/auth/realms/enterprise2/protocol/openid-connect/auth", "token_uri": "https://x.x.x.x:31567/auth/realms/enterprise2/protocol/openid-connect/token", "callback_uri": "https://x.x.x.x:31063/v2/projects/enterprise2/oauth/callback", "jwks": "{\"keys\":[{\"kid\":\"xxxx\",\"kty\":\"RSA\",\"alg\":\"RS256\",\"use\":\"sig\",\"n\":\"xxxx\",\"e\":\"AQAB\",\"x5c\":[\"xxxxxx\"],\"x5t\":\"xxxxxxx\",\"x5t#S256\":\"xxxxxxx\"}]}", "client_id": "emco", "client_secret": "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" }, "access_token": { "preamble": "Bearer", "header": "Authorization" } } } ] } ] }