From 8b7d25a6b78e17af768bad94181b5f1af7afa8e4 Mon Sep 17 00:00:00 2001 From: Le Yao <54387247+leyao-daily@users.noreply.github.com> Date: Thu, 29 Jul 2021 05:56:52 +0000 Subject: [PATCH] Istio integration guide Signed-off-by: Le Yao Change-Id: I86eb9fe690d898996fb3701ec089f57ac24c19d3 --- .../docs/istio/Multiple KeyCloak Configuration.md | 172 ++++++++++ central-controller/docs/istio/README.md | 358 +++++++++++++++++++++ .../docs/istio/istio-example-yaml/README.md | 1 + .../istio/istio-example-yaml/ingressgateway.yaml | 24 ++ .../istio-example-yaml/istio_allow_policy.yaml | 36 +++ .../istio-example-yaml/istio_deny_policy.yaml | 14 + .../docs/istio/istio-example-yaml/mTLS.yaml | 8 + .../istio/istio-example-yaml/request_auth.yaml | 9 + .../istio/istio-example-yaml/virtual_services.yaml | 70 ++++ central-controller/docs/istio/keycloak/README.md | 1 + .../docs/istio/keycloak/keycloak.yaml | 64 ++++ central-controller/docs/istio/mutl-key.png | Bin 0 -> 45540 bytes 12 files changed, 757 insertions(+) create mode 100644 central-controller/docs/istio/Multiple KeyCloak Configuration.md create mode 100644 central-controller/docs/istio/README.md create mode 100644 central-controller/docs/istio/istio-example-yaml/README.md create mode 100644 central-controller/docs/istio/istio-example-yaml/ingressgateway.yaml create mode 100644 central-controller/docs/istio/istio-example-yaml/istio_allow_policy.yaml create mode 100644 central-controller/docs/istio/istio-example-yaml/istio_deny_policy.yaml create mode 100644 central-controller/docs/istio/istio-example-yaml/mTLS.yaml create mode 100644 central-controller/docs/istio/istio-example-yaml/request_auth.yaml create mode 100644 central-controller/docs/istio/istio-example-yaml/virtual_services.yaml create mode 100644 central-controller/docs/istio/keycloak/README.md create mode 100644 central-controller/docs/istio/keycloak/keycloak.yaml create mode 100644 central-controller/docs/istio/mutl-key.png diff --git a/central-controller/docs/istio/Multiple KeyCloak Configuration.md b/central-controller/docs/istio/Multiple KeyCloak Configuration.md new file mode 100644 index 0000000..53a4a04 --- /dev/null +++ b/central-controller/docs/istio/Multiple KeyCloak Configuration.md @@ -0,0 +1,172 @@ +## Multiple KeyCloak Services Configuration + +In many use cases, we may need different KeyCloak service for different enterprise. They themselves maintain their own authentication framework/service. So we simply list how to configure two independent KeyCloak services to do authentication on same overlay controller. + +![arch](https://github.com/akraino-edge-stack/icn-sdwan/tree/master/central-controller/docs/istio/mutl-key.png) + +### Create different namespace + +```shell +# We will create two namespaces +kubectl create ns k1 +kubectl create ns k2 +``` + + + +### Deploy KeyCloak in different namespace + +```shell +# Create secret for each KeyCloak service or use your own secret +openssl genrsa 2048 > k1.key +openssl req -new -x509 -nodes -sha256 -days 365 -key k1.key -out k1.crt + +openssl genrsa 2048 > k2.key +openssl req -new -x509 -nodes -sha256 -days 365 -key k2.key -out k2.crt + +kubectl create -n k1 secret tls ca-keycloak-certs --key k1.key --cert k1.crt +kubectl create -n k2 secret tls ca-keycloak-certs --key k2.key --cert k2.crt + +# We will deploy two keycloak services into these two namespaces using the configuration file inside the `keycloak` dirctory. +kubectl apply -f keycloak/keycloak.yaml -n k1 +kubectl apply -f keycloak/keycloak.yaml -n k2 +``` + + + +### Configure the KeyCloak using Web GUI + +```yaml +# Access each KeyCloak Web interface and configure as the following, and we do not need to distinguish the value in each KeyCloak Web Configuration. +- Create a new Realm - ex: enterprise1 +- Add Users (as per customer requirement) - ex: "users" and configure the confidential password - ex:"test" in the first time +- Create a new Client under realm name - ex: "sdewan" +- 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. # "https:///*". + - In Roles tab: + > Add roles (ex. admin and user) + - Add Mappers # Under sdewan Client under mapper tab create a mapper + > Mapper type - User Client role + > Client-ID: sdewan + > Token claim name: role + > Claim JSON Type: string +- Under Users: assign roles from sdewan client to users ( Admin and User). Verify under sdewan Client roles for user are in the role. +``` + + + +### Enable Istio Authentication and Authorization Policy + +Assume you have already configure the `istio` gateway and virtual services as `README`. Now we can install an Authentication Policy for the Keycloak server being used. + +```yaml +apiVersion: security.istio.io/v1beta1 +kind: RequestAuthentication +metadata: + name: request-keycloak-auth + namespace: istio-system +spec: + jwtRules: + - issuer: "http:///auth/realms/enterprise1" + jwksUri: "http:///auth/realms/enterprise1/protocol/openid-connect/certs" + - issuer: "http:///auth/realms/enterprise1" + jwksUri: "http:///auth/realms/enterprise1/protocol/openid-connect/certs" +``` + + + +### Authorization Policies with Istio + +A deny policy is added to ensure that only authenticated users (with right token) are allowed to access specified resources. + +```yaml +apiVersion: "security.istio.io/v1beta1" +kind: "AuthorizationPolicy" +metadata: + name: "deny-auth-policy" + namespace: istio-system +spec: + selector: + matchLabels: + istio: ingressgateway + action: DENY + rules: + - from: + - source: + notRequestPrincipals: ["*"] +``` + +Curl to the scc service url will get an error "403 : RBAC: access denied" + +Retrieve access token from Keycloak and use it to access resources. Note that please replace the client secret with your keycloak `sdewan` client secret. Different enterprise have its own secret and they can get the token through their keycloak service. + +```shell +export TOKEN=`curl --location --request POST 'http:///auth/realms//protocol/openid-connect/token' --header 'Content-Type: application/x-www-form-urlencoded' --data-urlencode 'grant_type=password' --data-urlencode 'client_id=sdewan' --data-urlencode 'username=users' --data-urlencode 'password=test' --data-urlencode 'client_secret=' | jq .access_token` + +curl --header "Authorization: Bearer $TOKEN" http:///scc/overlays +``` + +#### Authorization Policies with Istio + +As specified in Keycloak section Role Mappers are created using Keycloak. Check Keycloak section on how to create Roles using Keycloak. These can be used apply authorizations based on Role of the user. + +For example to allow Role "Admin" to perform any operations and Role "User" to only create/delete resources under a specified project following policies can be used. + +```yaml +apiVersion: "security.istio.io/v1beta1" +kind: AuthorizationPolicy +metadata: + name: allow-admin + namespace: istio-system +spec: + selector: + matchLabels: + app: istio-ingressgateway + action: ALLOW + rules: + - when: + # The value in `request.auth.claims[]` is specified in your client mapper with tag name `Token claim name`. + - key: request.auth.claims[role] + # The value in `[]` is defined as the roles in your client. + values: ["admin"] + +--- +apiVersion: security.istio.io/v1beta1 +kind: AuthorizationPolicy +metadata: + name: allow-user + namespace: istio-system +spec: + selector: + matchLabels: + app: istio-ingressgateway + action: ALLOW + rules: + - from: + - source: + # This is the issuer you defined in pre-steps as an istio RequestAuthentication with `/*` + requestPrincipals: ["/*"] + to: + - operation: + methods: ["GET"] + paths: ["/scc/v1/overlays/overlay1"] + when: + # The value in `request.auth.claims[]` is specified in your client mapper with tag name `Token claim name`. + - key: request.auth.claims[role] + # The value in `[]` is defined as the roles in your client. + - from: + - source: + requestPrincipals: ["/*"] + to: + - operation: + methods: ["GET"] + paths: ["/scc/v1/overlays/overlay2"] + when: + # The value in `request.auth.claims[]` is specified in your client mapper with tag name `Token claim name`. + - key: request.auth.claims[role] + # The value in `[]` is defined as the roles in your client. +``` + +Then you can find that the user in k1 enterprise1 can not access overlay2 which can be accessed by the user in k2 enterprise1. Meantime, the user in k2 enterprise1 can not access overlay1 which can be accessed by the user in k1 enterprise1. diff --git a/central-controller/docs/istio/README.md b/central-controller/docs/istio/README.md new file mode 100644 index 0000000..8ad4b76 --- /dev/null +++ b/central-controller/docs/istio/README.md @@ -0,0 +1,358 @@ +# istio-sdewan +This is for the integration of istio and sdewan + + +SDEWAN uses Istio and other open source solutions to leverage Istio Authorization and Authentication frameworks. Authentication for the SDEWAN users are done at the Istio Gateway, where all the traffic enters the cluster. Istio along with Authservice (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. + +Authservice (https://github.com/istio-ecosystem/authservice) is an istio-ecosystem project that works alongside with Envoy proxy. It is used along with Istio to work with external IAM systems (OAUTH2). Many Enterprises have their own OAUTH2 server for authenticating users and providing roles to users. + +## Steps for setting up SDEWAN with Istio + +These steps need to be followed in the Kubernetes Cluster where SDEWAN-CNF, SDEWAN-CRD-CONTROLLER and SDEWAN-OVERLAY-CONTROLLER are installed. + +### Pre-Installation + +Note: The `url` in the following configurations are recommended to be set as node port mode. For example, `` can be found from the command `kubectl get service -A`. + +#### Istio + +- Install istioctl and init + + ```shell + # Download the latest istio release + curl -L https://istio.io/downloadIstio | sh - + + # Download the specified istio release for target_arch + # In the guide, we need istio version >= 1.7.4 + # curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.7.4 TARGET_ARCH=x86_64 sh - + + # Add the `istioctl` client binary to your path + export PATH=$PWD/bin:$PATH + + # Deploy istio operator + istioctl operator init + ``` + +- Install the `istio` demo profile using operator + + ```shell + # Create istio namespace + kubectl create ns istio-system + + # Aplly the istio demo profile + kubectl apply -f - < keycloak.key + openssl req -new -x509 -nodes -sha256 -days 365 -key keycloak.key -out keycloak.crt + ``` + +- Configure and setup keycloak in k8s cluster + + ```shell + # Create keycloak namespace + kubectl create ns keycloak + + # Create secret for keycloak (key and cert here can be created or offered in repo) + kubectl create -n keycloak secret tls ca-keycloak-certs --key keycloak.key --cert keycloak.crt + + # Deploy keycloak + kubectl apply -f keycloak/keycloak.yaml -n keycloak + ``` + +- Configure in Keycloak using its web interface + + ``` + - Create a new Realm - ex: enterprise1 + - Add Users (as per customer requirement) + - Create a new Client under realm name - ex: sdewan + - 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. # "https://istio-ingress-url/*". + - In Roles tab: + > Add roles (ex. admin and user) + - Add Mappers # Under sdewan Client under mapper tab create a mapper + > Mapper type - User Client role + > Client-ID: sdewan + > Token claim name: role + > Claim JSON Type: string + - Under Users: assign roles from sdewan client to users ( Admin and User). Verify under sdewan Client roles for user are in the role. + ``` + +### Configure and integrate + +#### Istio Sidecar Injection for SDEWAN namespace + +```Shell +# Label the namespace needed sidecar injection +kubectl label namespace sdewan-system istio-injection=enabled + +# To exclude deployment - no need for sidecar +spec: + template: + metadata: + annotations: + sidecar.istio.io/inject: "false" +``` +And then you can Install SDEWAN overlay controller in the `sdewan-system` namespace + +#### Enable mTLS in target namespace + +```Shell +# We set the namespace as `sdewan-system` and enable mTLS +kubectl apply -f istio-example-yaml/mTLS.yaml +``` + +The following is an example to enable mTLS in specified namespace, `sdewan-system` here. + +```yaml +apiVersion: "security.istio.io/v1beta1" +kind: "PeerAuthentication" +metadata: + name: "default" + namespace: sdewan-system +spec: + mtls: + mode: STRICT +``` + +#### Configure Istio Ingress Gateway + +Create certificate for Ingress Gateway and create secret for Istio Ingress Gateway +```shell +# Use the command to create key and cert for istio +openssl genrsa 2048 > v2.key +openssl req -new -x509 -nodes -sha256 -days 365 -key v2.key -out v2.crt + +kubectl create -n istio-system secret tls sdewan-credential --key=v2.key --cert=v2.crt +``` + +Example Gateway yaml + +```shell +apiVersion: networking.istio.io/v1alpha3 +kind: Gateway +metadata: + name: sdewan-gateway + namespace: sdewan-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: sdewan-credential + hosts: + - "*" +``` + +#### Create Istio VirtualServices Resources for SDEWAN +An Istio VirtualService Resource is required to be created for each of the microservices. + +```yaml +apiVersion: networking.istio.io/v1alpha3 +kind: VirtualService +metadata: + name: vs-sdewan-scc + namespace: sdewan-system +spec: + hosts: + - "*" + gateways: + - sdewan-gateway + http: + - match: + - uri: + prefix: /scc/v1 + - uri: + exact: /scc/v1/overlays + - uri: + regex: /scc/v1/overlays/[^\/]* + - uri: + exact: /scc/v1/provider + - uri: + regex: /scc/v1/provider/[^\/]* + - uri: + regex: /scc/v1/provider/ipranges + - uri: + regex: /scc/v1/provider/ipranges/[^\/]* + - uri: + regex: /scc/v1/overlays/.+/ipranges + - uri: + regex: /scc/v1/overlays/.+/ipranges/[^\/]* + - uri: + regex: /scc/v1/overlays/.+/certificates + - uri: + regex: /scc/v1/overlays/.+/certificates/[^\/]* + - uri: + regex: /scc/v1/overlays/.+/proposals + - uri: + regex: /scc/v1/overlays/.+/proposals/[^\/]* + - uri: + regex: /scc/v1/overlays/.+/hubs + - uri: + regex: /scc/v1/overlays/.+/hubs/[^\/]* + - uri: + regex: /scc/v1/overlays/.+/hubs/.+/cnfs + - uri: + regex: /scc/v1/overlays/.+/hubs/.+/devices + - uri: + regex: /scc/v1/overlays/.+/hubs/.+/devices/[^\/]* + - uri: + regex: /scc/v1/overlays/.+/hubs/.+/connections + - uri: + regex: /scc/v1/overlays/.+/hubs/.+/connections/[^\/]* + - uri: + regex: /scc/v1/overlays/.+/devices + - uri: + regex: /scc/v1/overlays/.+/devices/[^\/]* + - uri: + regex: /scc/v1/overlays/.+/devices/.+/cnfs + - uri: + regex: /scc/v1/overlays/.+/devices/.+/connections + - uri: + regex: /scc/v1/overlays/.+/devices/.+/connections/[^\/]* + + route: + - destination: + port: + number: 9015 + host: scc +``` + +Make sure the SDEWAN overlay controller is accessible through Istio Ingress Gateway at this point. "https://istio-ingress-url/scc/v1" + + +```shell +curl http:///scc/v1 +200: ... +``` + +#### Enable Istio Authentication and Authorization Policy +Install an Authentication Policy for the Keycloak server being used. + +```yaml +apiVersion: security.istio.io/v1beta1 +kind: RequestAuthentication +metadata: + name: request-keycloak-auth + namespace: istio-system +spec: + jwtRules: + - issuer: "http:///auth/realms/enterprise1" + jwksUri: "http:///auth/realms/enterprise1/protocol/openid-connect/certs" +# And you can create another keycloak service if you need +# jwtRules: +# - issuer: "http:///auth/realms/ano-enterprise" +# jwksUri: "http:///auth/realms/ano-enterprise/protocol/openid-connect/certs" +``` + +#### Authorization Policies with Istio + +A deny policy is added to ensure that only authenticated users (with right token) are allowed to access specified resources. + +```yaml +apiVersion: "security.istio.io/v1beta1" +kind: "AuthorizationPolicy" +metadata: + name: "deny-auth-policy" + namespace: istio-system +spec: + selector: + matchLabels: + istio: ingressgateway + action: DENY + rules: + - from: + - source: + notRequestPrincipals: ["*"] +``` + +Curl to the scc url will give an error "403 : RBAC: access denied" + +Retrieve access token from Keycloak and use it to access resources. Note that please replace the client secret with your keycloak `sdewan` client secret. + +```shell +export TOKEN=`curl --location --request POST 'http:///auth/realms//protocol/openid-connect/token' --header 'Content-Type: application/x-www-form-urlencoded' --data-urlencode 'grant_type=password' --data-urlencode 'client_id=sdewan' --data-urlencode 'username=user1' --data-urlencode 'password=test' --data-urlencode 'client_secret=' | jq .access_token` + +curl --header "Authorization: Bearer $TOKEN" http:///scc/overlays +``` +#### Authorization Policies with Istio + +As specified in Keycloak section Role Mappers are created using Keycloak. Check Keycloak section on how to create Roles using Keycloak. These can be used to apply authorizations based on Role of the user. + +For example to allow Role "Admin" to perform any operations and Role "User" to only create/delete resources under a specified project following policies can be used. + +```yaml +apiVersion: "security.istio.io/v1beta1" +kind: AuthorizationPolicy +metadata: + name: allow-admin + namespace: istio-system +spec: + selector: + matchLabels: + app: istio-ingressgateway + action: ALLOW + rules: + - when: + # The value in `request.auth.claims[]` is specified in your client mapper with tag name `Token claim name`. + - key: request.auth.claims[role] + # The value in `[]` is defined as the roles in your client. + values: ["admin"] + +--- +apiVersion: security.istio.io/v1beta1 +kind: AuthorizationPolicy +metadata: + name: allow-user + namespace: istio-system +spec: + selector: + matchLabels: + app: istio-ingressgateway + action: ALLOW + rules: + - to: + - operation: + paths: ["/scc/v1/*"] + - operation: + methods: ["GET"] + paths: ["/scc/v1/overlays"] + when: + # The value in `request.auth.claims[]` is specified in your client mapper with tag name `Token claim name`. + - key: request.auth.claims[role] + # The value in `[]` is defined as the roles in your client. + values: ["user"] + +# If you have another keycloak service, you may create specified configuration for target mapper claim name. +``` + +Then you can only access specified resources with the roles your account have. + +Note: For multi-keycloak services for different companies or tenants, you can refer to this [page](https://github.com/akraino-edge-stack/icn-sdwan/tree/master/central-controller/docs/istio/Multiple%20KeyCloak%20Configuration.md) diff --git a/central-controller/docs/istio/istio-example-yaml/README.md b/central-controller/docs/istio/istio-example-yaml/README.md new file mode 100644 index 0000000..89402b6 --- /dev/null +++ b/central-controller/docs/istio/istio-example-yaml/README.md @@ -0,0 +1 @@ +These are example files of the configuration for istio integrated into SDEWAN diff --git a/central-controller/docs/istio/istio-example-yaml/ingressgateway.yaml b/central-controller/docs/istio/istio-example-yaml/ingressgateway.yaml new file mode 100644 index 0000000..8ccdd6e --- /dev/null +++ b/central-controller/docs/istio/istio-example-yaml/ingressgateway.yaml @@ -0,0 +1,24 @@ +apiVersion: networking.istio.io/v1alpha3 +kind: Gateway +metadata: + name: sdewan-gateway + namespace: sdewan-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: sdewan-credential + hosts: + - "*" diff --git a/central-controller/docs/istio/istio-example-yaml/istio_allow_policy.yaml b/central-controller/docs/istio/istio-example-yaml/istio_allow_policy.yaml new file mode 100644 index 0000000..2b2972c --- /dev/null +++ b/central-controller/docs/istio/istio-example-yaml/istio_allow_policy.yaml @@ -0,0 +1,36 @@ +apiVersion: "security.istio.io/v1beta1" +kind: AuthorizationPolicy +metadata: + name: allow-admin + namespace: istio-system +spec: + selector: + matchLabels: + app: istio-ingressgateway + action: ALLOW + rules: + - when: + - key: request.auth.claims[role] + values: ["admin"] + +--- +apiVersion: security.istio.io/v1beta1 +kind: AuthorizationPolicy +metadata: + name: allow-user + namespace: istio-system +spec: + selector: + matchLabels: + app: istio-ingressgateway + action: ALLOW + rules: + - to: + - operation: + paths: ["/scc/v1/*"] + - operation: + methods: ["GET"] + paths: ["/scc/v1/overlays"] + when: + - key: request.auth.claims[role] + values: ["user"] diff --git a/central-controller/docs/istio/istio-example-yaml/istio_deny_policy.yaml b/central-controller/docs/istio/istio-example-yaml/istio_deny_policy.yaml new file mode 100644 index 0000000..2699135 --- /dev/null +++ b/central-controller/docs/istio/istio-example-yaml/istio_deny_policy.yaml @@ -0,0 +1,14 @@ +apiVersion: "security.istio.io/v1beta1" +kind: "AuthorizationPolicy" +metadata: + name: "deny-auth-policy" + namespace: istio-system +spec: + selector: + matchLabels: + istio: ingressgateway + action: DENY + rules: + - from: + - source: + notRequestPrincipals: ["*"] diff --git a/central-controller/docs/istio/istio-example-yaml/mTLS.yaml b/central-controller/docs/istio/istio-example-yaml/mTLS.yaml new file mode 100644 index 0000000..770037b --- /dev/null +++ b/central-controller/docs/istio/istio-example-yaml/mTLS.yaml @@ -0,0 +1,8 @@ +apiVersion: "security.istio.io/v1beta1" +kind: "PeerAuthentication" +metadata: + name: "default" + namespace: sdewan-system +spec: + mtls: + mode: STRICT diff --git a/central-controller/docs/istio/istio-example-yaml/request_auth.yaml b/central-controller/docs/istio/istio-example-yaml/request_auth.yaml new file mode 100644 index 0000000..9fc7257 --- /dev/null +++ b/central-controller/docs/istio/istio-example-yaml/request_auth.yaml @@ -0,0 +1,9 @@ +apiVersion: security.istio.io/v1beta1 +kind: RequestAuthentication +metadata: + name: request-keycloak-auth + namespace: istio-system +spec: + jwtRules: + - issuer: "http:///auth/realms/enterprise1" + jwksUri: "http:///auth/realms/enterprise1/protocol/openid-connect/certs" diff --git a/central-controller/docs/istio/istio-example-yaml/virtual_services.yaml b/central-controller/docs/istio/istio-example-yaml/virtual_services.yaml new file mode 100644 index 0000000..d46dc75 --- /dev/null +++ b/central-controller/docs/istio/istio-example-yaml/virtual_services.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) 2020 Intel Corporation +apiVersion: networking.istio.io/v1alpha3 +kind: VirtualService +metadata: + name: vs-sdewan-scc + namespace: sdewan-system +spec: + hosts: + - "*" + gateways: + - sdewan-gateway + http: + - match: + - uri: + prefix: /scc/v1 + - uri: + exact: /scc/v1/overlays + - uri: + regex: /scc/v1/overlays/[^\/]* + - uri: + exact: /scc/v1/provider + - uri: + regex: /scc/v1/provider/[^\/]* + - uri: + regex: /scc/v1/provider/ipranges + - uri: + regex: /scc/v1/provider/ipranges/[^\/]* + - uri: + regex: /scc/v1/overlays/.+/ipranges + - uri: + regex: /scc/v1/overlays/.+/ipranges/[^\/]* + - uri: + regex: /scc/v1/overlays/.+/certificates + - uri: + regex: /scc/v1/overlays/.+/certificates/[^\/]* + - uri: + regex: /scc/v1/overlays/.+/proposals + - uri: + regex: /scc/v1/overlays/.+/proposals/[^\/]* + - uri: + regex: /scc/v1/overlays/.+/hubs + - uri: + regex: /scc/v1/overlays/.+/hubs/[^\/]* + - uri: + regex: /scc/v1/overlays/.+/hubs/.+/cnfs + - uri: + regex: /scc/v1/overlays/.+/hubs/.+/devices + - uri: + regex: /scc/v1/overlays/.+/hubs/.+/devices/[^\/]* + - uri: + regex: /scc/v1/overlays/.+/hubs/.+/connections + - uri: + regex: /scc/v1/overlays/.+/hubs/.+/connections/[^\/]* + - uri: + regex: /scc/v1/overlays/.+/devices + - uri: + regex: /scc/v1/overlays/.+/devices/[^\/]* + - uri: + regex: /scc/v1/overlays/.+/devices/.+/cnfs + - uri: + regex: /scc/v1/overlays/.+/devices/.+/connections + - uri: + regex: /scc/v1/overlays/.+/devices/.+/connections/[^\/]* + + route: + - destination: + port: + number: 9015 + host: scc diff --git a/central-controller/docs/istio/keycloak/README.md b/central-controller/docs/istio/keycloak/README.md new file mode 100644 index 0000000..5030da1 --- /dev/null +++ b/central-controller/docs/istio/keycloak/README.md @@ -0,0 +1 @@ +The example files for keycloak configuration diff --git a/central-controller/docs/istio/keycloak/keycloak.yaml b/central-controller/docs/istio/keycloak/keycloak.yaml new file mode 100644 index 0000000..e6c604c --- /dev/null +++ b/central-controller/docs/istio/keycloak/keycloak.yaml @@ -0,0 +1,64 @@ +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 diff --git a/central-controller/docs/istio/mutl-key.png b/central-controller/docs/istio/mutl-key.png new file mode 100644 index 0000000000000000000000000000000000000000..60326f5291e1259c7a1d3a384dcb30550fde70fb GIT binary patch literal 45540 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 BcZC1| literal 0 HcmV?d00001 -- 2.16.6