ICS tests with istio and JWTs 84/7984/1
authorBjornMagnussonXA <bjorn.magnusson@est.tech>
Tue, 22 Mar 2022 09:35:10 +0000 (10:35 +0100)
committerBjornMagnussonXA <bjorn.magnusson@est.tech>
Tue, 22 Mar 2022 09:35:17 +0000 (10:35 +0100)
Added support to create JWT in keycloak
Added support to configure ICS with JWT handling
Added support to configure auth with isto
Adapted test case to test ICS with auth

Issue-ID: NONRTRIC-738
Signed-off-by: BjornMagnussonXA <bjorn.magnusson@est.tech>
Change-Id: Ifc540778b84e5f4291876a90be78c838652ef8b3

25 files changed:
test/auto-test/FTC1100.sh
test/common/api_curl.sh
test/common/authsidecar_api_functions.sh [new file with mode: 0644]
test/common/chartmus_api_functions.sh
test/common/clean_docker.sh
test/common/clean_kube.sh
test/common/ics_api_functions.sh
test/common/istio_api_functions.sh [new file with mode: 0644]
test/common/keycloak_api_functions.sh [new file with mode: 0644]
test/common/kubeproxy_api_functions.sh
test/common/prodstub_api_functions.sh
test/common/pvccleaner_api_functions.sh
test/common/ricsim_api_functions.sh
test/common/test_env-onap-jakarta.sh
test/common/test_env-oran-e-release.sh
test/common/test_env-oran-f-release.sh
test/common/testcase_common.sh
test/common/testengine_config.sh
test/simulator-group/ics/app.yaml
test/simulator-group/ics/application.yaml
test/simulator-group/istio/ap-principal-template.yaml [new file with mode: 0644]
test/simulator-group/istio/ra-jwks-template.yaml [new file with mode: 0644]
test/simulator-group/istio/ra-jwksuri-template.yaml [new file with mode: 0644]
test/simulator-group/keycloak/svc_app.yaml [new file with mode: 0644]
test/simulator-group/kubeproxy/jwt-info.txt [new file with mode: 0644]

index ce95a77..0ff083c 100755 (executable)
 #
 
 
-TC_ONELINE_DESCR="ICS full interfaces walkthrough"
+TC_ONELINE_DESCR="ICS full interfaces walkthrough - with or without istio enabled"
+
+USE_ISTIO=1
 
 #App names to include in the test when running docker, space separated list
 DOCKER_INCLUDED_IMAGES="ICS PRODSTUB CR RICSIM CP HTTPPROXY NGW KUBEPROXY"
 
 #App names to include in the test when running kubernetes, space separated list
-KUBE_INCLUDED_IMAGES="PRODSTUB CR ICS RICSIM CP HTTPPROXY KUBEPROXY NGW"
+if [ $USE_ISTIO -eq 0 ]; then
+    KUBE_INCLUDED_IMAGES="PRODSTUB CR ICS RICSIM CP HTTPPROXY KUBEPROXY NGW"
+else
+    KUBE_INCLUDED_IMAGES="PRODSTUB CR ICS RICSIM CP HTTPPROXY KUBEPROXY NGW KEYCLOAK ISTIO AUTHSIDECAR"
+fi
 #Prestarted app (not started by script) to include in the test when running kubernetes, space separated list
 KUBE_PRESTARTED_IMAGES=""
 
@@ -48,18 +54,77 @@ FLAT_A1_EI="1"
 
 clean_environment
 
+if [ $USE_ISTIO -eq 1 ]; then
+    echo -e $RED"#########################################"$ERED
+    echo -e $RED"# Work around istio jwks cache"$ERED
+    echo -e $RED"# Cycle istiod down and up to clear cache"$ERED
+    echo ""
+    __kube_scale deployment istiod istio-system 0
+    __kube_scale deployment istiod istio-system 1
+    echo -e $RED"# Cycle istiod done"
+    echo -e $RED"#########################################"$ERED
+
+    istio_enable_istio_namespace $KUBE_SIM_NAMESPACE
+    istio_enable_istio_namespace $KUBE_NONRTRIC_NAMESPACE
+fi
+
 start_kube_proxy
+set_kubeproxy_debug
 
-use_ics_rest_https
+if [ $USE_ISTIO -eq 1 ]; then
+    use_ics_rest_http
 
-use_prod_stub_https
+    use_prod_stub_http
 
-use_simulator_https
+    use_simulator_http
 
-use_cr_https
+    use_cr_http
+else
+    use_ics_rest_https
+
+    use_prod_stub_https
+
+    use_simulator_https
+
+    use_cr_https
+fi
 
 start_http_proxy
 
+if [ $USE_ISTIO -eq 1 ]; then
+    start_keycloak
+
+    keycloak_api_obtain_admin_token
+
+    keycloak_api_create_realm                   nrtrealm   true   60
+    keycloak_api_create_confidential_client     nrtrealm   icsc
+    keycloak_api_generate_client_secret         nrtrealm   icsc
+    keycloak_api_get_client_secret              nrtrealm   icsc
+
+    keycloak_api_get_client_token               nrtrealm   icsc
+
+    CLIENT_TOKEN=$(keycloak_api_read_client_token nrtrealm   icsc)
+    echo "CLIENT_TOKEN: "$CLIENT_TOKEN
+
+    ICS_SEC=$(keycloak_api_read_client_secret nrtrealm   icsc)
+    echo "ICS_SEC: "$ICS_SEC
+
+    istio_req_auth_by_jwks              $PROD_STUB_APP_NAME $KUBE_SIM_NAMESPACE KUBEPROXY "$KUBE_PROXY_ISTIO_JWKS_KEYS"
+    istio_auth_policy_by_issuer         $PROD_STUB_APP_NAME $KUBE_SIM_NAMESPACE KUBEPROXY
+
+    istio_req_auth_by_jwksuri           $PROD_STUB_APP_NAME $KUBE_SIM_NAMESPACE nrtrealm
+    istio_auth_policy_by_realm          $PROD_STUB_APP_NAME $KUBE_SIM_NAMESPACE nrtrealm
+
+    istio_req_auth_by_jwks              $CR_APP_NAME $KUBE_SIM_NAMESPACE KUBEPROXY "$KUBE_PROXY_ISTIO_JWKS_KEYS"
+    istio_auth_policy_by_issuer         $CR_APP_NAME $KUBE_SIM_NAMESPACE KUBEPROXY
+
+    istio_req_auth_by_jwksuri           $CR_APP_NAME $KUBE_SIM_NAMESPACE nrtrealm
+    istio_auth_policy_by_realm          $CR_APP_NAME $KUBE_SIM_NAMESPACE nrtrealm
+
+    ics_configure_sec nrtrealm icsc $ICS_SEC
+
+fi
+
 start_ics NOPROXY $SIM_GROUP/$ICS_COMPOSE_DIR/$ICS_CONFIG_FILE  #Change NOPROXY to PROXY to run with http proxy
 
 if [ $RUNMODE == "KUBE" ]; then
@@ -85,11 +150,19 @@ start_cr 1
 CB_JOB="$PROD_STUB_SERVICE_PATH$PROD_STUB_JOB_CALLBACK"
 CB_SV="$PROD_STUB_SERVICE_PATH$PROD_STUB_SUPERVISION_CALLBACK"
 #Targets for ei jobs
-TARGET1="$RIC_SIM_HTTPX://ricsim_g3_1:$RIC_SIM_PORT/datadelivery"
-TARGET2="$RIC_SIM_HTTPX://ricsim_g3_2:$RIC_SIM_PORT/datadelivery"
-TARGET3="$RIC_SIM_HTTPX://ricsim_g3_3:$RIC_SIM_PORT/datadelivery"
-TARGET8="$RIC_SIM_HTTPX://ricsim_g3_4:$RIC_SIM_PORT/datadelivery"
-TARGET10="$RIC_SIM_HTTPX://ricsim_g3_4:$RIC_SIM_PORT/datadelivery"
+if [ $RUNMODE == "KUBE" ]; then
+    TARGET1="$RIC_SIM_HTTPX://ricsim-g3-1.ricsim-g3.$KUBE_A1SIM_NAMESPACE:$RIC_SIM_PORT/datadelivery"
+    TARGET2="$RIC_SIM_HTTPX://ricsim-g3-2.ricsim-g3.$KUBE_A1SIM_NAMESPACE:$RIC_SIM_PORT/datadelivery"
+    TARGET3="$RIC_SIM_HTTPX://ricsim-g3-3.ricsim-g3.$KUBE_A1SIM_NAMESPACE:$RIC_SIM_PORT/datadelivery"
+    TARGET8="$RIC_SIM_HTTPX://ricsim-g3-4.ricsim-g3.$KUBE_A1SIM_NAMESPACE:$RIC_SIM_PORT/datadelivery"
+    TARGET10="$RIC_SIM_HTTPX://ricsim-g3-4.ricsim-g3.$KUBE_A1SIM_NAMESPACE:$RIC_SIM_PORT/datadelivery"
+else
+    TARGET1="$RIC_SIM_HTTPX://ricsim_g3_1:$RIC_SIM_PORT/datadelivery"
+    TARGET2="$RIC_SIM_HTTPX://ricsim_g3_2:$RIC_SIM_PORT/datadelivery"
+    TARGET3="$RIC_SIM_HTTPX://ricsim_g3_3:$RIC_SIM_PORT/datadelivery"
+    TARGET8="$RIC_SIM_HTTPX://ricsim_g3_4:$RIC_SIM_PORT/datadelivery"
+    TARGET10="$RIC_SIM_HTTPX://ricsim_g3_4:$RIC_SIM_PORT/datadelivery"
+fi
 
 #Targets for info jobs
 TARGET101="http://localhost:80/target"  # Dummy target, no target for info data in this env...
index e99f579..6058e08 100644 (file)
@@ -159,12 +159,15 @@ __do_curl_to_api() {
         echo "-000"
         return 1
     fi
-
+       jwt=""
+       if [ ! -z "$KUBE_PROXY_CURL_JWT" ]; then
+               jwt=" -H "\""Authorization: Bearer $KUBE_PROXY_CURL_JWT"\"
+       fi
        if [ $__ADAPTER_TYPE == "REST" ]; then
         url=" "${__ADAPTER}${input_url}
         oper=" -X "$oper
         curlString="curl -k $proxyflag "${oper}${timeout}${httpcode}${accept}${content}${url}${file}
-        echo " CMD: "$curlString >> $HTTPLOG
+        echo " CMD: $curlString $jwt" >> $HTTPLOG
                if [ $# -gt 3 ]; then
                        echo " FILE: $(<$fname)" >> $HTTPLOG
                fi
@@ -174,7 +177,11 @@ __do_curl_to_api() {
                while [ $maxretries -ge 0 ]; do
 
                        let maxretries=maxretries-1
-                       res=$($curlString)
+                       if [ ! -z "$KUBE_PROXY_CURL_JWT" ]; then
+                               res=$($curlString -H "Authorization: Bearer $KUBE_PROXY_CURL_JWT")
+                       else
+                               res=$($curlString)
+                       fi
                        retcode=$?
                        if [ $retcode -ne 0 ]; then
                                echo " RETCODE: "$retcode >> $HTTPLOG
diff --git a/test/common/authsidecar_api_functions.sh b/test/common/authsidecar_api_functions.sh
new file mode 100644 (file)
index 0000000..594aa31
--- /dev/null
@@ -0,0 +1,109 @@
+#!/bin/bash
+
+#  ============LICENSE_START===============================================
+#  Copyright (C) 2020 Nordix Foundation. All rights reserved.
+#  ========================================================================
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#  ============LICENSE_END=================================================
+#
+
+# This is a script that contains container/service management functions
+# for AUTHSIDECAR
+
+################ Test engine functions ################
+
+# Create the image var used during the test
+# arg: <image-tag-suffix> (selects staging, snapshot, release etc)
+# <image-tag-suffix> is present only for images with staging, snapshot,release tags
+__AUTHSIDECAR_imagesetup() {
+       __check_and_create_image_var AUTHSIDECAR "AUTHSIDECAR_IMAGE" "AUTHSIDECAR_IMAGE_BASE" "AUTHSIDECAR_IMAGE_TAG" $1 "$AUTHSIDECAR_DISPLAY_NAME"
+}
+
+# Pull image from remote repo or use locally built image
+# arg: <pull-policy-override> <pull-policy-original>
+# <pull-policy-override> Shall be used for images allowing overriding. For example use a local image when test is started to use released images
+# <pull-policy-original> Shall be used for images that does not allow overriding
+# Both var may contain: 'remote', 'remote-remove' or 'local'
+__AUTHSIDECAR_imagepull() {
+       __check_and_pull_image $1 "$AUTHSIDECAR_DISPLAY_NAME" $AUTHSIDECAR_APP_NAME AUTHSIDECAR_IMAGE
+}
+
+# Build image (only for simulator or interfaces stubs owned by the test environment)
+# arg: <image-tag-suffix> (selects staging, snapshot, release etc)
+# <image-tag-suffix> is present only for images with staging, snapshot,release tags
+__AUTHSIDECAR_imagebuild() {
+       echo -e $RED"Image for app AUTHSIDECAR shall never be built"$ERED
+}
+
+# Generate a string for each included image using the app display name and a docker images format string
+# If a custom image repo is used then also the source image from the local repo is listed
+# arg: <docker-images-format-string> <file-to-append>
+__AUTHSIDECAR_image_data() {
+       echo -e "$AUTHSIDECAR_DISPLAY_NAME\t$(docker images --format $1 $AUTHSIDECAR_IMAGE)" >>   $2
+       if [ ! -z "$AUTHSIDECAR_IMAGE_SOURCE" ]; then
+               echo -e "-- source image --\t$(docker images --format $1 $AUTHSIDECAR_IMAGE_SOURCE)" >>   $2
+       fi
+}
+
+# Scale kubernetes resources to zero
+# All resources shall be ordered to be scaled to 0, if relevant. If not relevant to scale, then do no action.
+# This function is called for apps fully managed by the test script
+__AUTHSIDECAR_kube_scale_zero() {
+       :
+}
+
+# Scale kubernetes resources to zero and wait until this has been accomplished, if relevant. If not relevant to scale, then do no action.
+# This function is called for prestarted apps not managed by the test script.
+__AUTHSIDECAR_kube_scale_zero_and_wait() {
+       :
+}
+
+# Delete all kube resouces for the app
+# This function is called for apps managed by the test script.
+__AUTHSIDECAR_kube_delete_all() {
+       :
+}
+
+# Store docker logs
+# This function is called for apps managed by the test script.
+# args: <log-dir> <file-prexix>
+__AUTHSIDECAR_store_docker_logs() {
+       if [ $RUNMODE == "KUBE" ]; then
+               kubectl $KUBECONF  logs -l "autotest=AUTHSIDECAR" -A --tail=-1 > $1$2_authsidecar.log 2>&1
+       fi
+}
+
+# Initial setup of protocol, host and ports
+# This function is called for apps managed by the test script.
+# args: -
+__AUTHSIDECAR_initial_setup() {
+       :
+}
+
+# Set app short-name, app name and namespace for logging runtime statistics of kubernets pods or docker containers
+# For docker, the namespace shall be excluded
+# This function is called for apps managed by the test script as well as for prestarted apps.
+# args: -
+__AUTHSIDECAR_statisics_setup() {
+       echo ""
+}
+
+# Check application requirements, e.g. helm, the the test needs. Exit 1 if req not satisfied
+# args: -
+__AUTHSIDECAR_test_requirements() {
+       :
+}
+
+#######################################################
+
+# This app is intended as a sidecard container - no specific test functions
\ No newline at end of file
index f7e268e..7c7a365 100644 (file)
@@ -201,7 +201,7 @@ start_chart_museum() {
 
                if [ $retcode_p -eq 0 ]; then
                        echo -e " Using existing $CHART_MUS_APP_NAME deployment and service"
-                       echo " Setting RC replicas=1"
+                       echo " Setting CHARTMUS replicas=1"
                        __kube_scale deployment $CHART_MUS_APP_NAME $KUBE_SIM_NAMESPACE 1
                fi
 
index 70e42da..7fe6c39 100755 (executable)
@@ -18,7 +18,7 @@
 
 # Script to clean all docker containers having the label 'nrttest_app', i.e started by autotest
 
-echo "Will stop and remove all docker containers with label 'nrttest_app'"
+echo "Stopping and removing all docker containers with label 'nrttest_app'"
 echo " Stopping containers..."
 docker stop $(docker ps -qa  --filter "label=nrttest_app") 2> /dev/null
 echo " Removing stopped containers..."
index f25d52c..5997abc 100755 (executable)
@@ -86,7 +86,7 @@ __kube_wait_for_zero_count() {
 __kube_delete_all_resources() {
        echo " Delete all in namespace $1 ..."
        namespace=$1
-       resources="deployments replicaset statefulset services pods configmaps pvc serviceaccounts secrets"
+       resources="deployments replicaset statefulset services pods configmaps pvc serviceaccounts secrets authorizationpolicies requestauthentications"
        for restype in $resources; do
                result=$(kubectl $KUBECONF get $restype -n $namespace -o jsonpath='{.items[?(@.metadata.labels.autotest)].metadata.name}')
                if [ $? -eq 0 ] && [ ! -z "$result" ]; then
index 0df2ae4..2152de9 100644 (file)
@@ -89,6 +89,7 @@ __ICS_store_docker_logs() {
 # args: -
 __ICS_initial_setup() {
        use_ics_rest_http
+       export ICS_SIDECAR_JWT_FILE=""
 }
 
 # Set app short-name, app name and namespace for logging runtime statistics of kubernets pods or docker containers
@@ -278,6 +279,10 @@ start_ics() {
                        # Create app
                        input_yaml=$SIM_GROUP"/"$ICS_COMPOSE_DIR"/"app.yaml
                        output_yaml=$PWD/tmp/ics_app.yaml
+                       if [ -z "$ICS_SIDECAR_JWT_FILE" ]; then
+                               cat $input_yaml | sed  '/#ICS_JWT_START/,/#ICS_JWT_STOP/d' > $PWD/tmp/ics_app_tmp.yaml
+                               input_yaml=$PWD/tmp/ics_app_tmp.yaml
+                       fi
                        __kube_create_instance app $ICS_APP_NAME $input_yaml $output_yaml
                fi
 
@@ -448,7 +453,7 @@ set_ics_debug() {
 # (Function for test scripts)
 set_ics_trace() {
        echo -e $BOLD"Setting ics trace logging"$EBOLD
-       curlString="$ICS_SERVICE_PATH/actuator/loggers/org.oransc.information -X POST  -H Content-Type:application/json -d {\"configuredLevel\":\"trace\"}"
+       curlString="$ICS_SERVICE_PATH$ICS_ACTUATOR -X POST  -H Content-Type:application/json -d {\"configuredLevel\":\"trace\"}"
        result=$(__do_curl "$curlString")
        if [ $? -ne 0 ]; then
                __print_err "Could not set trace mode" $@
@@ -2461,4 +2466,17 @@ ics_kube_pvc_reset() {
 
        __log_test_pass
        return 0
+}
+
+# args: <realm> <client-name> <client-secret>
+ics_configure_sec() {
+       export ICS_CREDS_GRANT_TYPE="client_credentials"
+       export ICS_CREDS_CLIENT_SECRET=$3
+       export ICS_CREDS_CLIENT_ID=$2
+       export ICS_AUTH_SERVICE_URL=$KEYCLOAK_SERVICE_PATH$KEYCLOAK_TOKEN_URL_PREFIX/$1/protocol/openid-connect/token
+       export ICS_SIDECAR_MOUNT="/token-cache"
+       export ICS_SIDECAR_JWT_FILE=$ICS_SIDECAR_MOUNT"/jwt.txt"
+
+       export AUTHSIDECAR_APP_NAME
+       export AUTHSIDECAR_DISPLAY_NAME
 }
\ No newline at end of file
diff --git a/test/common/istio_api_functions.sh b/test/common/istio_api_functions.sh
new file mode 100644 (file)
index 0000000..2afc3f9
--- /dev/null
@@ -0,0 +1,249 @@
+#!/bin/bash
+
+#  ============LICENSE_START===============================================
+#  Copyright (C) 2021 Nordix Foundation. All rights reserved.
+#  ========================================================================
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#  ============LICENSE_END=================================================
+#
+
+# This is a script that contains function to handle helm on localhost
+
+
+################ Test engine functions ################
+
+# Create the image var used during the test
+# arg: <image-tag-suffix> (selects staging, snapshot, release etc)
+# <image-tag-suffix> is present only for images with staging, snapshot,release tags
+__ISTIO_imagesetup() {
+       :
+}
+
+# Pull image from remote repo or use locally built image
+# arg: <pull-policy-override> <pull-policy-original>
+# <pull-policy-override> Shall be used for images allowing overriding. For example use a local image when test is started to use released images
+# <pull-policy-original> Shall be used for images that does not allow overriding
+# Both var may contain: 'remote', 'remote-remove' or 'local'
+__ISTIO_imagepull() {
+       :
+}
+
+# Build image (only for simulator or interfaces stubs owned by the test environment)
+# arg: <image-tag-suffix> (selects staging, snapshot, release etc)
+# <image-tag-suffix> is present only for images with staging, snapshot,release tags
+__ISTIO_imagebuild() {
+       :
+}
+
+# Generate a string for each included image using the app display name and a docker images format string
+# If a custom image repo is used then also the source image from the local repo is listed
+# arg: <docker-images-format-string> <file-to-append>
+__ISTIO_image_data() {
+       :
+}
+
+# Scale kubernetes resources to zero
+# All resources shall be ordered to be scaled to 0, if relevant. If not relevant to scale, then do no action.
+# This function is called for apps fully managed by the test script
+__ISTIO_kube_scale_zero() {
+       :
+}
+
+# Scale kubernetes resources to zero and wait until this has been accomplished, if relevant. If not relevant to scale, then do no action.
+# This function is called for prestarted apps not managed by the test script.
+__ISTIO_kube_scale_zero_and_wait() {
+       :
+}
+
+# Delete all kube resouces for the app
+# This function is called for apps managed by the test script.
+__ISTIO_kube_delete_all() {
+       __kube_delete_all_resources $KUBE_NONRTRIC_NAMESPACE autotest ISTIO
+}
+
+# Store docker logs
+# This function is called for apps managed by the test script.
+# args: <log-dir> <file-prexix>
+__ISTIO_store_docker_logs() {
+       :
+}
+
+# Initial setup of protocol, host and ports
+# This function is called for apps managed by the test script.
+# args: -
+__ISTIO_initial_setup() {
+       # See jwt-info.txt in simulator-group/kubeproxy for detailed info
+       KUBE_PROXY_CURL_JWT=$ISTIO_GENERIC_JWT
+       KUBE_PROXY_ISTIO_JWKS_KEYS=$ISTIO_GENERIC_JWKS_KEY
+}
+
+# Set app short-name, app name and namespace for logging runtime statistics of kubernets pods or docker containers
+# For docker, the namespace shall be excluded
+# This function is called for apps managed by the test script as well as for prestarted apps.
+# args: -
+__ISTIO_statisics_setup() {
+       :
+}
+
+# Check application requirements, e.g. helm, the the test needs. Exit 1 if req not satisfied
+# args: -
+__ISTIO_test_requirements() {
+
+       kubectl $KUBECONF get requestauthentications -A &> /dev/null
+       if [ $? -ne 0 ]; then
+               echo $RED" Istio api: kubectl get requestauthentications is not installed"
+               exit 1
+       fi
+       kubectl $KUBECONF get authorizationpolicies -A &> /dev/null
+       if [ $? -ne 0 ]; then
+               echo $RED" Istio api: kubectl get authorizationpolicies is not installed"
+               exit 1
+       fi
+}
+
+#######################################################
+
+
+# Enable istio on namespace
+# arg: <namespace>
+istio_enable_istio_namespace() {
+       __log_conf_start $@
+    if [ $# -ne 1 ]; then
+        __print_err "<namespace>" $@
+        return 1
+    fi
+       __kube_create_namespace $1
+       __kube_label_non_ns_instance ns $1 "istio-injection=enabled"
+       __log_conf_ok
+       return 0
+}
+
+# Request authorization by jwksuri
+# args: <app> <namespace> <realm>
+istio_req_auth_by_jwksuri() {
+       __log_conf_start $@
+    if [ $# -ne 3 ]; then
+        __print_err "<app> <namespace> <realm>" $@
+        return 1
+    fi
+       name="ra-jwksuri-"$3"-"$1"-"$2
+       export  ISTIO_TEMPLATE_REPLACE_RA_NAME=$(echo $name | tr '[:upper:]' '[:lower:]')
+       export  ISTIO_TEMPLATE_REPLACE_RA_NS=$2
+       export  ISTIO_TEMPLATE_REPLACE_RA_APP_NAME=$1
+       export  ISTIO_TEMPLATE_REPLACE_RA_ISSUER=$KEYCLOAK_ISSUER_PATH$KEYCLOAK_TOKEN_URL_PREFIX/$3
+       export  ISTIO_TEMPLATE_REPLACE_RA_JWKSURI=$KEYCLOAK_SERVICE_PATH$KEYCLOAK_TOKEN_URL_PREFIX/$3/protocol/openid-connect/certs
+       inputfile=$SIM_GROUP/$ISTIO_COMPOSE_DIR/ra-jwksuri-template.yaml
+       outputfile=tmp/$ISTIO_TEMPLATE_REPLACE_RA_NAME".yaml"
+       envsubst < $inputfile > $outputfile
+       if [ $? -ne 0 ]; then
+               __log_conf_fail_general "Cannot substitute yaml: $inputfile"
+               return 1
+       fi
+       kubectl $KUBECONF apply -f $outputfile &> tmp/kubeerr
+       if [ $? -ne 0 ]; then
+               __log_conf_fail_general "Cannot apply yaml: $outputfile"
+               return 1
+       fi
+       __log_conf_ok
+       return 0
+}
+
+# Request authorization by jwks (inline keys)
+# args: <app> <namespace> <issuer> <key>
+istio_req_auth_by_jwks() {
+       __log_conf_start $@
+    if [ $# -ne 4 ]; then
+        __print_err "<app> <namespace> <issuer> <key>" $@
+        return 1
+    fi
+       name="ra-jwks-"$3"-"$1"-"$2
+       export  ISTIO_TEMPLATE_REPLACE_RA_NAME=$(echo $name | tr '[:upper:]' '[:lower:]')
+       export  ISTIO_TEMPLATE_REPLACE_RA_NS=$2
+       export  ISTIO_TEMPLATE_REPLACE_RA_APP_NAME=$1
+       export  ISTIO_TEMPLATE_REPLACE_RA_ISSUER=$3
+       export  ISTIO_TEMPLATE_REPLACE_RA_JWKS=$4
+       inputfile=$SIM_GROUP/$ISTIO_COMPOSE_DIR/ra-jwks-template.yaml
+       outputfile=tmp/$ISTIO_TEMPLATE_REPLACE_RA_NAME".yaml"
+       envsubst < $inputfile > $outputfile
+       if [ $? -ne 0 ]; then
+               __log_conf_fail_general "Cannot substitute yaml: $inputfile"
+               return 1
+       fi
+       kubectl $KUBECONF apply -f $outputfile &> tmp/kubeerr
+       if [ $? -ne 0 ]; then
+               __log_conf_fail_general "Cannot apply yaml: $outputfile"
+               return 1
+       fi
+       __log_conf_ok
+       return 0
+}
+
+# Authorization policy - by realm
+# args: <app> <namespace> <realam>
+istio_auth_policy_by_realm() {
+       __log_conf_start $@
+    if [ $# -ne 3 ]; then
+        __print_err "<app> <namespace> <realam>" $@
+        return 1
+    fi
+       name="ap-realm-"$3"-"$1"-"$2
+       export  ISTIO_TEMPLATE_REPLACE_AP_NAME=$(echo $name | tr '[:upper:]' '[:lower:]')
+       export  ISTIO_TEMPLATE_REPLACE_AP_NS=$2
+       export  ISTIO_TEMPLATE_REPLACE_AP_APP_NAME=$1
+       export  ISTIO_TEMPLATE_REPLACE_AP_PRINCIPAL="$KEYCLOAK_ISSUER_PATH$KEYCLOAK_TOKEN_URL_PREFIX/$3/*"
+       inputfile=$SIM_GROUP/$ISTIO_COMPOSE_DIR/ap-principal-template.yaml
+       outputfile=tmp/$ISTIO_TEMPLATE_REPLACE_AP_NAME".yaml"
+       envsubst < $inputfile > $outputfile
+       if [ $? -ne 0 ]; then
+               __log_conf_fail_general "Cannot substitute yaml: $inputfile"
+               return 1
+       fi
+       kubectl $KUBECONF apply -f $outputfile &> tmp/kubeerr
+       if [ $? -ne 0 ]; then
+               __log_conf_fail_general "Cannot apply yaml: $outputfile"
+               return 1
+       fi
+       __log_conf_ok
+       return 0
+}
+
+# Authorization policy - by issuer
+# args: <app> <namespace> <issuer>
+istio_auth_policy_by_issuer() {
+       __log_conf_start $@
+    if [ $# -ne 3 ]; then
+        __print_err "<app> <namespace> <issuer>" $@
+        return 1
+    fi
+       name="ap-iss-"$3"-"$1"-"$2
+       export  ISTIO_TEMPLATE_REPLACE_AP_NAME=$(echo $name | tr '[:upper:]' '[:lower:]')
+       export  ISTIO_TEMPLATE_REPLACE_AP_NS=$2
+       export  ISTIO_TEMPLATE_REPLACE_AP_APP_NAME=$1
+       export  ISTIO_TEMPLATE_REPLACE_AP_PRINCIPAL="$3/*"
+       inputfile=$SIM_GROUP/$ISTIO_COMPOSE_DIR/ap-principal-template.yaml
+       outputfile=tmp/$ISTIO_TEMPLATE_REPLACE_AP_NAME".yaml"
+       envsubst < $inputfile > $outputfile
+       if [ $? -ne 0 ]; then
+               __log_conf_fail_general "Cannot substitute yaml: $inputfile"
+               return 1
+       fi
+       kubectl $KUBECONF apply -f $outputfile &> tmp/kubeerr
+       if [ $? -ne 0 ]; then
+               __log_conf_fail_general "Cannot apply yaml: $outputfile"
+               return 1
+       fi
+       __log_conf_ok
+       return 0
+}
+
+
diff --git a/test/common/keycloak_api_functions.sh b/test/common/keycloak_api_functions.sh
new file mode 100644 (file)
index 0000000..828dcb3
--- /dev/null
@@ -0,0 +1,616 @@
+#!/bin/bash
+
+#  ============LICENSE_START===============================================
+#  Copyright (C) 2021 Nordix Foundation. All rights reserved.
+#  ========================================================================
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#  ============LICENSE_END=================================================
+#
+
+# This is a script that contains container/service management functions and test functions for Keycloak
+
+
+################ Test engine functions ################
+
+# Create the image var used during the test
+# arg: <image-tag-suffix> (selects staging, snapshot, release etc)
+# <image-tag-suffix> is present only for images with staging, snapshot,release tags
+__KEYCLOAK_imagesetup() {
+       __check_and_create_image_var KEYCLOAK "KEYCLOAK_IMAGE" "KEYCLOAK_IMAGE_BASE" "KEYCLOAK_IMAGE_TAG" REMOTE_OTHER "$KEYCLOAK_DISPLAY_NAME"
+}
+
+# Pull image from remote repo or use locally built image
+# arg: <pull-policy-override> <pull-policy-original>
+# <pull-policy-override> Shall be used for images allowing overriding. For example use a local image when test is started to use released images
+# <pull-policy-original> Shall be used for images that does not allow overriding
+# Both var may contain: 'remote', 'remote-remove' or 'local'
+__KEYCLOAK_imagepull() {
+       __check_and_pull_image $2 "$KEYCLOAK_DISPLAY_NAME" $KEYCLOAK_APP_NAME KEYCLOAK_IMAGE
+}
+
+# Build image (only for simulator or interfaces stubs owned by the test environment)
+# arg: <image-tag-suffix> (selects staging, snapshot, release etc)
+# <image-tag-suffix> is present only for images with staging, snapshot,release tags
+__KEYCLOAK_imagebuild() {
+       echo -e $RED" Image for app KEYCLOAK shall never be built"$ERED
+}
+
+# Generate a string for each included image using the app display name and a docker images format string
+# If a custom image repo is used then also the source image from the local repo is listed
+# arg: <docker-images-format-string> <file-to-append>
+__KEYCLOAK_image_data() {
+       echo -e "$KEYCLOAK_DISPLAY_NAME\t$(docker images --format $1 $KEYCLOAK_IMAGE)" >>   $2
+       if [ ! -z "$KEYCLOAK_IMAGE_SOURCE" ]; then
+               echo -e "-- source image --\t$(docker images --format $1 $KEYCLOAK_IMAGE_SOURCE)" >>   $2
+       fi
+}
+
+# Scale kubernetes resources to zero
+# All resources shall be ordered to be scaled to 0, if relevant. If not relevant to scale, then do no action.
+# This function is called for apps fully managed by the test script
+__KEYCLOAK_kube_scale_zero() {
+       __kube_scale_all_resources $KUBE_KEYCLOAK_NAMESPACE autotest KEYCLOAK
+}
+
+# Scale kubernetes resources to zero and wait until this has been accomplished, if relevant. If not relevant to scale, then do no action.
+# This function is called for prestarted apps not managed by the test script.
+__KEYCLOAK_kube_scale_zero_and_wait() {
+       echo -e $RED" KEYCLOAK app is not scaled in this state"$ERED
+}
+
+# Delete all kube resouces for the app
+# This function is called for apps managed by the test script.
+__KEYCLOAK_kube_delete_all() {
+       __kube_delete_all_resources $KUBE_KEYCLOAK_NAMESPACE autotest KEYCLOAK
+}
+
+# Store docker logs
+# This function is called for apps managed by the test script.
+# args: <log-dir> <file-prexix>
+__KEYCLOAK_store_docker_logs() {
+       if [ $RUNMODE == "KUBE" ]; then
+               kubectl $KUBECONF  logs -l "autotest=KEYCLOAK" -n $KUBE_KEYCLOAK_NAMESPACE --tail=-1 > $1$2_keycloak.log 2>&1
+       else
+               docker logs $KEYCLOAK_APP_NAME > $1$2_keycloak.log 2>&1
+       fi
+}
+
+# Initial setup of protocol, host and ports
+# This function is called for apps managed by the test script.
+# args: -
+__KEYCLOAK_initial_setup() {
+       use_keycloak_http
+}
+
+# Set app short-name, app name and namespace for logging runtime statistics of kubernets pods or docker containers
+# For docker, the namespace shall be excluded
+# This function is called for apps managed by the test script as well as for prestarted apps.
+# args: -
+__KEYCLOAK_statisics_setup() {
+       if [ $RUNMODE == "KUBE" ]; then
+               echo "KEYCLOAK $KEYCLOAK_APP_NAME $KUBE_KEYCLOAK_NAMESPACE"
+       else
+               echo "KEYCLOAK $KEYCLOAK_APP_NAME"
+       fi
+}
+
+# Check application requirements, e.g. helm, the the test needs. Exit 1 if req not satisfied
+# args: -
+__KEYCLOAK_test_requirements() {
+       which jq > /dev/null
+       if [ $? -ne 0 ]; then
+               echo $RED" 'jq' is required to run tests. Pls install 'jq'"
+               return 1
+       fi
+}
+
+#######################################################
+
+# Set http as the protocol to use for all communication to the Keycloak
+# args: -
+# (Function for test scripts)
+use_keycloak_http() {
+       __keycloak_set_protocoll "http" $KEYCLOAK_INTERNAL_PORT $KEYCLOAK_EXTERNAL_PORT
+}
+
+# Set https as the protocol to use for all communication to the Keycloak
+# args: -
+# (Function for test scripts)
+use_keycloak_https() {
+       __keycloak_set_protocoll "https" $KEYCLOAK_INTERNAL_SECURE_PORT $KEYCLOAK_EXTERNAL_SECURE_PORT
+}
+
+# Setup paths to svc/container for internal and external access
+# args: <protocol> <internal-port> <external-port>
+__keycloak_set_protocoll() {
+       echo -e $BOLD"$KEYCLOAK_DISPLAY_NAME protocol setting"$EBOLD
+       echo -e " Using $BOLD $1 $EBOLD towards $KEYCLOAK_DISPLAY_NAME"
+
+       ## Access to Keycloak
+
+       KEYCLOAK_SERVICE_PATH=$1"://"$KEYCLOAK_APP_NAME":"$2  # docker access, container->container and script->container via proxy
+       KEYCLOAK_SERVICE_PORT=$2
+       KEYCLOAK_SERVICE_HOST=$KEYCLOAK_APP_NAME
+       KEYCLOAK_ISSUER_PATH=$1"://"$KEYCLOAK_APP_NAME
+       if [ $RUNMODE == "KUBE" ]; then
+               KEYCLOAK_SERVICE_PATH=$1"://"$KEYCLOAK_APP_NAME.$KUBE_KEYCLOAK_NAMESPACE":"$3 # kube access, pod->svc and script->svc via proxy
+               KEYCLOAK_SERVICE_PORT=$3
+               KEYCLOAK_SERVICE_HOST=$KEYCLOAK_APP_NAME.$KUBE_KEYCLOAK_NAMESPACE
+               KEYCLOAK_ISSUER_PATH=$1"://"$KEYCLOAK_APP_NAME.$KUBE_KEYCLOAK_NAMESPACE
+       fi
+       KEYCLOAK_SERVICE_HTTPX=$1
+
+       echo ""
+}
+
+### Admin API functions Keycloak
+
+###########################
+### Keycloak functions
+###########################
+
+# Export env vars for config files, docker compose and kube resources
+# args:
+__keycloak_export_vars() {
+       export KEYCLOAK_APP_NAME
+       export KEYCLOAK_DISPLAY_NAME
+
+       export DOCKER_SIM_NWNAME
+       export KUBE_KEYCLOAK_NAMESPACE
+
+       export KEYCLOAK_IMAGE
+       export KEYCLOAK_INTERNAL_PORT
+       export KEYCLOAK_EXTERNAL_PORT
+
+       export KEYCLOAK_ADMIN_USER
+       export KEYCLOAK_ADMIN_PWD
+       export KEYCLOAK_KC_PROXY
+}
+
+
+# Start the Keycloak in the simulator group
+# args: -
+# (Function for test scripts)
+start_keycloak() {
+
+       echo -e $BOLD"Starting $KEYCLOAK_DISPLAY_NAME"$EBOLD
+
+       if [ $RUNMODE == "KUBE" ]; then
+
+               # Check if app shall be fully managed by the test script
+               __check_included_image "KEYCLOAK"
+               retcode_i=$?
+
+               # Check if app shall only be used by the testscipt
+               __check_prestarted_image "KEYCLOAK"
+               retcode_p=$?
+
+               if [ $retcode_i -ne 0 ] && [ $retcode_p -ne 0 ]; then
+                       echo -e $RED"The $KEYCLOAK_NAME app is not included as managed nor prestarted in this test script"$ERED
+                       echo -e $RED"The $KEYCLOAK_APP_NAME will not be started"$ERED
+                       exit
+               fi
+               if [ $retcode_i -eq 0 ] && [ $retcode_p -eq 0 ]; then
+                       echo -e $RED"The $KEYCLOAK_APP_NAME app is included both as managed and prestarted in this test script"$ERED
+                       echo -e $RED"The $KEYCLOAK_APP_NAME will not be started"$ERED
+                       exit
+               fi
+
+               if [ $retcode_p -eq 0 ]; then
+                       echo -e " Using existing $KEYCLOAK_APP_NAME deployment and service"
+                       echo " Setting keycloak replicas=1"
+                       __kube_scale deployment $KEYCLOAK_APP_NAME $KUBE_KEYCLOAK_NAMESPACE 1
+               fi
+
+               if [ $retcode_i -eq 0 ]; then
+                       echo -e " Creating $KEYCLOAK_APP_NAME deployment and service"
+
+            __kube_create_namespace $KUBE_KEYCLOAK_NAMESPACE
+
+                       __keycloak_export_vars
+
+                       # Create service and app
+                       input_yaml=$SIM_GROUP"/"$KEYCLOAK_COMPOSE_DIR"/"svc_app.yaml
+                       output_yaml=$PWD/tmp/keycloak_svc_app.yaml
+                       __kube_create_instance "service/app" $KEYCLOAK_APP_NAME $input_yaml $output_yaml
+
+               fi
+
+               __check_service_start $KEYCLOAK_APP_NAME $KEYCLOAK_SERVICE_PATH$KEYCLOAK_ALIVE_URL
+       else
+
+               # Check if docker app shall be fully managed by the test script
+               __check_included_image 'KEYCLOAK'
+               if [ $? -eq 1 ]; then
+                       echo -e $RED"The Keycloak app is not included as managed in this test script"$ERED
+                       echo -e $RED"The Keycloak will not be started"$ERED
+                       exit
+               fi
+
+               __keycloak_export_vars
+
+               __start_container $KEYCLOAK_COMPOSE_DIR "" NODOCKERARGS 1 $KEYCLOAK_APP_NAME
+
+        __check_service_start $KEYCLOAK_APP_NAME $KEYCLOAK_SERVICE_PATH$KEYCLOAK_ALIVE_URL
+       fi
+    echo ""
+    return 0
+}
+
+# Excute a curl cmd towards the keycloak and check the response code is 2XX.
+# args: <curl-cmd-string>
+# resp: <returned-payload> if return code is 0 otherwise <error-info>
+__execute_curl_to_keycloak() {
+
+       proxyflag=""
+       if [ ! -z "$KUBE_PROXY_PATH" ]; then
+               if [ $KUBE_PROXY_HTTPX == "http" ]; then
+                       proxyflag=" --proxy $KUBE_PROXY_PATH"
+               else
+                       proxyflag=" --proxy-insecure --proxy $KUBE_PROXY_PATH"
+               fi
+       fi
+       __cmd="curl -skw %{http_code} $proxyflag $1"
+       echo " CMD: $__cmd" >> $HTTPLOG
+       res=$($__cmd)
+       echo " RESP: $res" >> $HTTPLOG
+       retcode=$?
+    if [ $retcode -ne 0 ]; then
+        __log_conf_fail_general " Fatal error when executing curl, response: "$retcode
+        echo "$res"
+               return 1
+    fi
+    status=${res:${#res}-3}
+       if [ $status -lt 200 ] && [ $status -gt 299 ]; then
+               __log_conf_fail_status_code "2XX" $status
+               echo "$res"
+               return 1
+       fi
+       echo ${res:0:${#res}-3}
+       return 0
+}
+
+# # Excute a curl cmd towards the keycloak and check the response code is 2XX.
+# # args: <command-file>
+# # resp: <returned-payload> if return code is 0 otherwise <error-info>
+# __execute_curl_to_keycloak2() {
+
+#     # TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
+#     # echo "(${BASH_LINENO[0]}) - ${TIMESTAMP}: ${FUNCNAME[0]}" $@ >> $HTTPLOG
+#      proxyflag=""
+#      if [ ! -z "$KUBE_PROXY_PATH" ]; then
+#              if [ $KUBE_PROXY_HTTPX == "http" ]; then
+#                      proxyflag=" --proxy $KUBE_PROXY_PATH"
+#              else
+#                      proxyflag=" --proxy-insecure --proxy $KUBE_PROXY_PATH"
+#              fi
+#      fi
+#      __cmd="curl -skw %{http_code} $proxyflag "$(< $1)
+#      echo " CMD: $__cmd" >> $HTTPLOG
+#      res=$(curl -skw %{http_code} $proxyflag $(< $1))
+#      echo " RESP: $res" >> $HTTPLOG
+#      retcode=$?
+#     if [ $retcode -ne 0 ]; then
+#         __log_conf_fail_general " Fatal error when executing curl, response: "$retcode
+#         echo "$res"
+#              return 1
+#     fi
+#     status=${res:${#res}-3}
+#      if [ $status -lt 200 ] && [ $status -gt 299 ]; then
+#              __log_conf_fail_status_code "2XX" $status
+#              echo "$res"
+#              return 1
+#      fi
+#      echo ${res:0:${#res}-3}
+#      return 0
+# }
+
+# Excute a curl cmd towards the keycloak and check the response code is 2XX.
+# args: <operation> <url> <token> <json>
+# resp: <returned-payload> if return code is 0 otherwise <error-info>
+__execute_curl_to_keycloak2() {
+       proxyflag=""
+       if [ ! -z "$KUBE_PROXY_PATH" ]; then
+               if [ $KUBE_PROXY_HTTPX == "http" ]; then
+                       proxyflag=" --proxy $KUBE_PROXY_PATH"
+               else
+                       proxyflag=" --proxy-insecure --proxy $KUBE_PROXY_PATH"
+               fi
+       fi
+       if [ $1 == "POST" ]; then
+               if [ $# -eq 3 ]; then
+                       echo  curl -X POST -skw %{http_code} $proxyflag "$2" -H "Authorization: Bearer $3" >> $HTTPLOG
+                       res=$(curl -X POST -skw %{http_code} $proxyflag "$2" -H "Authorization: Bearer $3")
+                       retcode=$?
+               else
+                       echo  curl -X POST -skw %{http_code} $proxyflag "$2" -H "Content-Type: application/json" -H "Authorization: Bearer $3" --data-binary "$4" >> $HTTPLOG
+                       res=$(curl -X POST -skw %{http_code} $proxyflag "$2" -H "Content-Type: application/json" -H "Authorization: Bearer $3" --data-binary "$4")
+                       retcode=$?
+               fi
+       elif [ $1 == "PUT" ]; then
+               if [ $# -eq 3 ]; then
+                       echo  curl -X PUT -skw %{http_code} $proxyflag "$2" -H "Authorization: Bearer $3" >> $HTTPLOG
+                       res=$(curl -X PUT -skw %{http_code} $proxyflag "$2" -H "Authorization: Bearer $3")
+                       retcode=$?
+               else
+                       echo  curl -X PUT -skw %{http_code} $proxyflag "$2" -H "Content-Type: application/json" -H "Authorization: Bearer $3" --data-binary "$4" >> $HTTPLOG
+                       res=$(curl -X PUT -skw %{http_code} $proxyflag "$2" -H "Content-Type: application/json" -H "Authorization: Bearer $3" --data-binary "$4")
+                       retcode=$?
+               fi
+       elif [ $1 == "GET" ]; then
+               echo  curl -X GET -skw %{http_code} $proxyflag "$2" -H "Authorization: Bearer $3" >> $HTTPLOG
+               res=$(curl -X GET -skw %{http_code} $proxyflag "$2" -H "Authorization: Bearer $3")
+               retcode=$?
+       fi
+       echo " RESP: $res" >> $HTTPLOG
+    if [ $retcode -ne 0 ]; then
+        __log_conf_fail_general " Fatal error when executing curl, response: "$retcode
+        echo "$res"
+               return 1
+    fi
+    status=${res:${#res}-3}
+       if [ $status -lt 200 ] && [ $status -gt 299 ]; then
+               __log_conf_fail_status_code "2XX" $status
+               echo "$res"
+               return 1
+       fi
+       echo ${res:0:${#res}-3}
+       return 0
+}
+
+# Extract JWT access token from json structure
+# args: <json>
+__keycloak_decode_jwt() {
+    echo $1 | jq -r .access_token | jq -R 'split(".") | .[1] | @base64d | fromjson'
+       return 0
+}
+
+# Get the admin token to use for subsequent rest calls to keycloak
+# args: -
+keycloak_api_obtain_admin_token() {
+       __log_conf_start $@
+       __curl_string="-X POST $KEYCLOAK_SERVICE_PATH$KEYCLOAK_ADMIN_URL_PREFIX/protocol/openid-connect/token     -H Content-Type:application/x-www-form-urlencoded     -d username="$KEYCLOAK_ADMIN_USER" -d password="$KEYCLOAK_ADMIN_PWD" -d grant_type=password -d client_id="$KEYCLOAK_ADMIN_CLIENT
+       __TMP_TOKEN=$(__execute_curl_to_keycloak "$__curl_string")
+       if [ $? -ne 0 ]; then
+        __log_conf_fail_general " Fatal error when executing curl, response: "$?
+        return 1
+       fi
+
+       __KEYCLOAK_ADMIN_TOKEN=$(echo "$__TMP_TOKEN" | jq  -r '.access_token')
+       if [ $? -ne 0 ]; then
+        __log_conf_fail_general " Fatal error when extracting token, response: "$?
+        return 1
+       fi
+
+       echo "Decoded token:" >> $HTTPLOG
+       __keycloak_decode_jwt "$__TMP_TOKEN" >> $HTTPLOG
+
+       __KEYCLOAK_ADMIN_TOKEN_EXP=$(echo "$__TMP_TOKEN" | jq  -r '.expires_in')
+       if [ $? -ne 0 ]; then
+        __log_conf_fail_general " Fatal error when extracting expiry time, response: "$?
+        return 1
+       fi
+       echo " Admin token obtained. Expires in $__KEYCLOAK_ADMIN_TOKEN_EXP seconds"
+
+       __log_conf_ok
+       return 0
+}
+
+# Create a realm, name, enabled, expiry-time
+# args: <realm-name> true|false <token-expiry>
+keycloak_api_create_realm() {
+       __log_conf_start $@
+       __json='{"realm":"'$1'","enabled":'$2',"accessTokenLifespan":'$3'}'
+       res=$(__execute_curl_to_keycloak2 POST "$KEYCLOAK_SERVICE_PATH$KEYCLOAK_REALM_URL_PREFIX" "$__KEYCLOAK_ADMIN_TOKEN" "$__json")
+       if [ $? -ne 0 ]; then
+               __log_conf_fail_general " Fatal error when creating realm, response: "$?
+               return 1
+       fi
+       __log_conf_ok
+       return 0
+}
+
+# Update a realm, name, enabled, expiry-time
+# args: <realm-name> true|false <token-expiry>
+keycloak_api_update_realm() {
+       __log_conf_start $@
+       __json='{"realm":"'$1'","enabled":'$2',"accessTokenLifespan":'$3'}'
+       res=$(__execute_curl_to_keycloak2 PUT "$KEYCLOAK_SERVICE_PATH$KEYCLOAK_REALM_URL_PREFIX/$1" "$__KEYCLOAK_ADMIN_TOKEN" "$__json")
+       if [ $? -ne 0 ]; then
+               __log_conf_fail_general " Fatal error when updating realm, response: "$?
+               return 1
+       fi
+       __log_conf_ok
+       return 0
+}
+
+# Create a client
+# args: <realm-name> <client-name>
+keycloak_api_create_confidential_client() {
+       __log_conf_start $@
+       __json='{"clientId":"'$2'","publicClient":false,"serviceAccountsEnabled": true,"rootUrl":"https://example.com/example/","adminUrl":"https://example.com/example/"}'
+       res=$(__execute_curl_to_keycloak2 POST "$KEYCLOAK_SERVICE_PATH$KEYCLOAK_REALM_URL_PREFIX/$1/clients" "$__KEYCLOAK_ADMIN_TOKEN" "$__json")
+       if [ $? -ne 0 ]; then
+               __log_conf_fail_general " Fatal error when ucreating client, response: "$?
+               return 1
+       fi
+       __log_conf_ok
+       return 0
+}
+
+__keycloak_api_get_client_id() {
+       TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
+       echo "(${BASH_LINENO[0]}) - ${TIMESTAMP}: ${FUNCNAME[0]}" $@ >> $HTTPLOG
+
+       res=$(__execute_curl_to_keycloak2 GET "$KEYCLOAK_SERVICE_PATH$KEYCLOAK_REALM_URL_PREFIX/$1/clients?clientId=$2" "$__KEYCLOAK_ADMIN_TOKEN")
+       if [ $? -ne 0 ]; then
+               return 1
+       fi
+       echo $res | jq -r '.[0].id'
+       return 0
+}
+
+__keycloak_api_get_service_account_id() {
+       TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
+       echo "(${BASH_LINENO[0]}) - ${TIMESTAMP}: ${FUNCNAME[0]}" $@ >> $HTTPLOG
+
+       res=$(__execute_curl_to_keycloak2 GET "$KEYCLOAK_SERVICE_PATH$KEYCLOAK_REALM_URL_PREFIX/$1/clients$2/service-account-user" "$__KEYCLOAK_ADMIN_TOKEN")
+       if [ $? -ne 0 ]; then
+               return 1
+       fi
+       echo $res | jq -r '.[0].id'
+       return 0
+}
+
+# Generate secret for client
+# args: <realm-name> <client-name>
+keycloak_api_generate_client_secret() {
+       __log_conf_start $@
+       __c_id=$(__keycloak_api_get_client_id $1 $2)
+       if [ $? -ne 0 ]; then
+               __log_conf_fail_general " Fatal error when getting client id, response: "$?
+               return 1
+       fi
+       res=$(__execute_curl_to_keycloak2 POST "$KEYCLOAK_SERVICE_PATH$KEYCLOAK_REALM_URL_PREFIX/$1/clients/$__c_id/client-secret" "$__KEYCLOAK_ADMIN_TOKEN")
+       if [ $? -ne 0 ]; then
+               __log_conf_fail_general " Fatal error when generating client secret, response: "$?
+               return 1
+       fi
+       __c_sec=$(__execute_curl_to_keycloak2 GET "$KEYCLOAK_SERVICE_PATH$KEYCLOAK_REALM_URL_PREFIX/$1/clients/$__c_id/client-secret" "$__KEYCLOAK_ADMIN_TOKEN")
+       if [ $? -ne 0 ]; then
+               __log_conf_fail_general " Fatal error when getting client secret, response: "$?
+               return 1
+       fi
+       __c_sec=$(echo $__c_sec | jq -r .value)
+       echo " Client id    : $__c_id"
+       echo " Client secret: $__c_sec"
+       __log_conf_ok
+       return 0
+}
+
+# Get secret for client
+# args: <realm-name> <client-name>
+keycloak_api_get_client_secret() {
+       __log_conf_start $@
+       __c_id=$(__keycloak_api_get_client_id $1 $2)
+       if [ $? -ne 0 ]; then
+               __log_conf_fail_general " Fatal error when getting client id, response: "$?
+               return 1
+       fi
+       __c_sec=$(__execute_curl_to_keycloak2 GET "$KEYCLOAK_SERVICE_PATH$KEYCLOAK_REALM_URL_PREFIX/$1/clients/$__c_id/client-secret" "$__KEYCLOAK_ADMIN_TOKEN")
+       if [ $? -ne 0 ]; then
+               __log_conf_fail_general " Fatal error when getting client secret, response: "$?
+               return 1
+       fi
+       __c_sec=$(echo $__c_sec | jq -r .value)
+       echo " Client id    : $__c_id"
+       echo " Client secret: $__c_sec"
+       __log_conf_ok
+       return 0
+}
+
+# Create client roles
+# args: <realm-name> <client-name> <role>+
+keycloak_api_create_client_roles() {
+       __log_conf_start $@
+       __c_id=$(__keycloak_api_get_client_id $1 $2)
+       if [ $? -ne 0 ]; then
+               __log_conf_fail_general " Fatal error when getting client id, response: "$?
+               return 1
+       fi
+       __realm=$1
+       shift; shift;
+    while [ $# -gt 0 ]; do
+               __json='{"name":"'$1'"}'
+               res=$(__execute_curl_to_keycloak2 POST "$KEYCLOAK_SERVICE_PATH$KEYCLOAK_REALM_URL_PREFIX/$__realm/clients/$__c_id/roles" "$__KEYCLOAK_ADMIN_TOKEN" "$__json")
+               if [ $? -ne 0 ]; then
+                       __log_conf_fail_general " Fatal error when creating client role, response: "$?
+                       return 1
+               fi
+               shift
+       done
+       __log_conf_ok
+       return 0
+}
+
+# Get a client token
+# args: <realm-name> <client-name>
+keycloak_api_get_client_token() {
+       __log_conf_start $@
+       __c_id=$(__keycloak_api_get_client_id $1 $2)
+       if [ $? -ne 0 ]; then
+               __log_conf_fail_general " Fatal error when getting client id, response: "$?
+               return 1
+       fi
+       __c_sec=$(__execute_curl_to_keycloak2 GET "$KEYCLOAK_SERVICE_PATH$KEYCLOAK_REALM_URL_PREFIX/$1/clients/$__c_id/client-secret" "$__KEYCLOAK_ADMIN_TOKEN")
+       if [ $? -ne 0 ]; then
+               __log_conf_fail_general " Fatal error when getting client secret, response: "$?
+               return 1
+       fi
+       __c_sec=$(echo $__c_sec | jq -r .value)
+       __curl_string="-X POST $KEYCLOAK_SERVICE_PATH$KEYCLOAK_TOKEN_URL_PREFIX/$1/protocol/openid-connect/token     -H Content-Type:application/x-www-form-urlencoded     -d client_id="$2" -d client_secret="$__c_sec" -d grant_type=client_credentials"
+       __TMP_TOKEN=$(__execute_curl_to_keycloak "$__curl_string")
+       if [ $? -ne 0 ]; then
+               __log_conf_fail_general " Fatal error when getting client token, response: "$?
+               return 1
+       fi
+       echo $__TMP_TOKEN| jq -r .access_token
+       __log_conf_ok
+       return 0
+}
+
+# Read a client token
+# args: <realm-name> <client-name>
+keycloak_api_read_client_token() {
+       TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
+       echo "(${BASH_LINENO[0]}) - ${TIMESTAMP}: ${FUNCNAME[0]}" $@ >> $HTTPLOG
+       __c_id=$(__keycloak_api_get_client_id $1 $2)
+       if [ $? -ne 0 ]; then
+               echo "<error-no-token>"
+               return 1
+       fi
+       __c_sec=$(__execute_curl_to_keycloak2 GET "$KEYCLOAK_SERVICE_PATH$KEYCLOAK_REALM_URL_PREFIX/$1/clients/$__c_id/client-secret" "$__KEYCLOAK_ADMIN_TOKEN")
+       if [ $? -ne 0 ]; then
+               echo "<error-no-token>"
+               return 1
+       fi
+       __c_sec=$(echo $__c_sec | jq -r .value)
+       __curl_string="-X POST $KEYCLOAK_SERVICE_PATH$KEYCLOAK_TOKEN_URL_PREFIX/$1/protocol/openid-connect/token     -H Content-Type:application/x-www-form-urlencoded     -d client_id="$2" -d client_secret="$__c_sec" -d grant_type=client_credentials"
+       __TMP_TOKEN=$(__execute_curl_to_keycloak "$__curl_string")
+       if [ $? -ne 0 ]; then
+               echo "<error-no-token>"
+               return 1
+       fi
+       echo $__TMP_TOKEN| jq -r .access_token
+       return 0
+}
+
+# Read secret for client
+# args: <realm-name> <client-name>
+keycloak_api_read_client_secret() {
+       TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
+       echo "(${BASH_LINENO[0]}) - ${TIMESTAMP}: ${FUNCNAME[0]}" $@ >> $HTTPLOG
+       __c_id=$(__keycloak_api_get_client_id $1 $2)
+       if [ $? -ne 0 ]; then
+               echo "<error-no-secret>"
+               return 1
+       fi
+       __c_sec=$(__execute_curl_to_keycloak2 GET "$KEYCLOAK_SERVICE_PATH$KEYCLOAK_REALM_URL_PREFIX/$1/clients/$__c_id/client-secret" "$__KEYCLOAK_ADMIN_TOKEN")
+       if [ $? -ne 0 ]; then
+               echo "<error-no-secret>"
+               return 1
+       fi
+       __c_sec=$(echo $__c_sec | jq -r .value)
+       echo $__c_sec
+       return 0
+}
\ No newline at end of file
index 120fbb6..c66e63c 100644 (file)
@@ -393,3 +393,4 @@ set_kubeproxy_debug() {
        echo ""
        return 0
 }
+
index 9af00d8..7dbef77 100644 (file)
@@ -274,8 +274,14 @@ __execute_curl_to_prodstub() {
                        proxyflag=" --proxy-insecure --proxy $KUBE_PROXY_PATH"
                fi
        fi
-       echo " CMD: $3 $proxyflag" >> $HTTPLOG
-       res="$($3 $proxyflag)"
+       if [ ! -z "$KUBE_PROXY_CURL_JWT" ]; then
+               jwt="-H "\""Authorization: Bearer $KUBE_PROXY_CURL_JWT"\"
+               echo " CMD: $3 $proxyflag $jwt" >> $HTTPLOG
+               res=$($3 $proxyflag -H "Authorization: Bearer $KUBE_PROXY_CURL_JWT")
+       else
+               echo " CMD: $3 $proxyflag" >> $HTTPLOG
+               res="$($3 $proxyflag)"
+       fi
        echo " RESP: $res" >> $HTTPLOG
        retcode=$?
     if [ $retcode -ne 0 ]; then
index 26bee7b..4ffb522 100644 (file)
@@ -79,7 +79,7 @@ __PVCCLEANER_kube_delete_all() {
 # args: <log-dir> <file-prexix>
 __PVCCLEANER_store_docker_logs() {
        if [ $RUNMODE == "KUBE" ]; then
-               kubectl $KUBECONF  logs -l "autotest=PRODSTUB" -A --tail=-1 > $1$2_pvs_cleaner.log 2>&1
+               kubectl $KUBECONF  logs -l "autotest=PVCCLEANER" -A --tail=-1 > $1$2_pvs_cleaner.log 2>&1
        fi
 }
 
index 4623563..b8af532 100644 (file)
@@ -241,7 +241,7 @@ start_ric_simulators() {
                        export RIC_SIM_INTERNAL_PORT
                        export RIC_SIM_INTERNAL_SECURE_PORT
 
-                       echo -e " Creating $A1PMS_APP_NAME app and expose service"
+                       echo -e " Creating $RIC_SIM_PREFIX app and expose service"
 
                        #Check if nonrtric namespace exists, if not create it
                        __kube_create_namespace $KUBE_A1SIM_NAMESPACE
index 0824cc1..33ac6ac 100644 (file)
@@ -215,7 +215,7 @@ ICS_LOGPATH="/var/log/information-coordinator-service/application.log" # Path th
 ICS_APP_NAME_ALIAS="information-service-container"       # Alias name, name used by the control panel
 ICS_HOST_MNT_DIR="./mnt"                                 # Mounted dir, relative to compose file, on the host
 ICS_CONTAINER_MNT_DIR="/var/information-coordinator-service" # Mounted dir in the container
-ICS_ACTUATOR="/actuator/loggers/org.oransc.information"  # Url for trace/debug
+ICS_ACTUATOR="/actuator/loggers/org.oransc.ics"          # Url for trace/debug
 ICS_CERT_MOUNT_DIR="./cert"
 ICS_ALIVE_URL="/status"                                  # Base path for alive check
 ICS_COMPOSE_DIR="ics"                                    # Dir in simulator_group for docker-compose
index dbddca1..4e57ac5 100755 (executable)
@@ -214,7 +214,7 @@ KAFKAPC_IMAGE_BASE="kafka-procon"
 KAFKAPC_IMAGE_TAG_LOCAL="latest"
 #No local image for pvc cleaner, remote image always used
 
-#PVC Cleaner remote image and tag
+#Chartmusem remote image and tag
 CHART_MUS_IMAGE_BASE="ghcr.io/helm/chartmuseum"
 CHART_MUS_IMAGE_TAG_REMOTE_OTHER="v0.13.1"
 #No local image for chart museum, remote image always used
@@ -290,7 +290,7 @@ ICS_LOGPATH="/var/log/information-coordinator-service/application.log" # Path th
 ICS_APP_NAME_ALIAS="information-service-container"       # Alias name, name used by the control panel
 ICS_HOST_MNT_DIR="./mnt"                                 # Mounted db dir, relative to compose file, on the host
 ICS_CONTAINER_MNT_DIR="/var/information-coordinator-service" # Mounted dir in the container
-ICS_ACTUATOR="/actuator/loggers/org.oransc.information"  # Url for trace/debug
+ICS_ACTUATOR="/actuator/loggers/org.oransc.ics"          # Url for trace/debug
 ICS_CERT_MOUNT_DIR="./cert"
 ICS_ALIVE_URL="/status"                                  # Base path for alive check
 ICS_COMPOSE_DIR="ics"                                    # Dir in simulator_group for docker-compose
index eb26634..4beddd1 100755 (executable)
@@ -150,6 +150,13 @@ HELM_MANAGER_IMAGE_TAG_REMOTE_SNAPSHOT="1.2.0-SNAPSHOT"
 HELM_MANAGER_IMAGE_TAG_REMOTE="1.2.0"
 HELM_MANAGER_IMAGE_TAG_REMOTE_RELEASE="1.2.0"
 
+# Auth sidecar
+AUTHSIDECAR_IMAGE_BASE="o-ran-sc/nonrtric-auth-token-fetch"
+AUTHSIDECAR_IMAGE_TAG_LOCAL="1.0.0-SNAPSHOT"
+AUTHSIDECAR_IMAGE_TAG_REMOTE_SNAPSHOT="1.0.0-SNAPSHOT"
+AUTHSIDECAR_IMAGE_TAG_REMOTE="1.0.0"
+AUTHSIDECAR_IMAGE_TAG_REMOTE_RELEASE="1.0.0"
+
 #MR stub image and tag
 MRSTUB_IMAGE_BASE="mrstub"
 MRSTUB_IMAGE_TAG_LOCAL="latest"
@@ -202,13 +209,18 @@ KAFKAPC_IMAGE_BASE="kafka-procon"
 KAFKAPC_IMAGE_TAG_LOCAL="latest"
 #No local image for pvc cleaner, remote image always used
 
-#PVC Cleaner remote image and tag
+#Chartmusem remote image and tag
 CHART_MUS_IMAGE_BASE="ghcr.io/helm/chartmuseum"
 CHART_MUS_IMAGE_TAG_REMOTE_OTHER="v0.13.1"
 #No local image for chart museum, remote image always used
 
+#Keycloak remote image and tag
+KEYCLOAK_IMAGE_BASE="quay.io/keycloak/keycloak"
+KEYCLOAK_IMAGE_TAG_REMOTE_OTHER="17.0.0"
+#No local image for chart museum, remote image always used
+
 # List of app short names produced by the project
-PROJECT_IMAGES_APP_NAMES="A1PMS ICS CP RC RICSIM NGW DMAAPADP DMAAPMED HELMMANAGER"  # Add SDNC here if oran image is used
+PROJECT_IMAGES_APP_NAMES="A1PMS ICS CP RC RICSIM NGW DMAAPADP DMAAPMED HELMMANAGER AUTHSIDECAR"  # Add SDNC here if oran image is used
 
 # List of app short names which images pulled from ORAN
 ORAN_IMAGES_APP_NAMES=""  # Not used
@@ -241,6 +253,7 @@ KUBE_SIM_NAMESPACE="nonrtric-ft"                         # Namespace for simulat
 KUBE_A1SIM_NAMESPACE="a1-sim"                            # Namespace for a1-p simulators (RICSIM)
 KUBE_ONAP_NAMESPACE="onap"                               # Namespace for onap (only message router)
 KUBE_SDNC_NAMESPACE="onap"                               # Namespace for sdnc
+KUBE_KEYCLOAK_NAMESPACE="keycloak"                       # Namespace for keycloak
 
 A1PMS_EXTERNAL_PORT=8081                                   # A1PMS container external port (host -> container)
 A1PMS_INTERNAL_PORT=8081                                   # A1PMS container internal port (container -> container)
@@ -278,7 +291,7 @@ ICS_LOGPATH="/var/log/information-coordinator-service/application.log" # Path th
 ICS_APP_NAME_ALIAS="information-service-container"       # Alias name, name used by the control panel
 ICS_HOST_MNT_DIR="./mnt"                                 # Mounted db dir, relative to compose file, on the host
 ICS_CONTAINER_MNT_DIR="/var/information-coordinator-service" # Mounted dir in the container
-ICS_ACTUATOR="/actuator/loggers/org.oransc.information"  # Url for trace/debug
+ICS_ACTUATOR="/actuator/loggers/org.oransc.ics"          # Url for trace/debug
 ICS_CERT_MOUNT_DIR="./cert"
 ICS_ALIVE_URL="/status"                                  # Base path for alive check
 ICS_COMPOSE_DIR="ics"                                    # Dir in simulator_group for docker-compose
@@ -547,6 +560,29 @@ HELM_MANAGER_ALIVE_URL="/helm/charts"                    # Base path for alive c
 HELM_MANAGER_COMPOSE_DIR="helmmanager"                   # Dir in simulator_group for docker-compose
 HELM_MANAGER_USER="helmadmin"
 HELM_MANAGER_PWD="itisasecret"
+
+KEYCLOAK_APP_NAME="keycloak"                             # Name for the keycloak app
+KEYCLOAK_DISPLAY_NAME="Keycloak"
+KEYCLOAK_EXTERNAL_PORT=80                                # keycloak container external port (host -> container)
+KEYCLOAK_INTERNAL_PORT=8080                              # keycloak container internal port (container -> container)
+KEYCLOAK_ADMIN_URL_PREFIX="/realms/master"
+KEYCLOAK_REALM_URL_PREFIX="/admin/realms"
+KEYCLOAK_TOKEN_URL_PREFIX="/realms"
+KEYCLOAK_ALIVE_URL="/realms/master"                      # Base path for alive check
+KEYCLOAK_COMPOSE_DIR="keycloak"
+KEYCLOAK_ADMIN_USER="admin"
+KEYCLOAK_ADMIN_PWD="admin"
+KEYCLOAK_ADMIN_CLIENT="admin-cli"
+KEYCLOAK_KC_PROXY="edge"
+
+ISTIO_COMPOSE_DIR="istio"
+
+# See jwt-info.txt in simulator-group/kubeproxy for detailed info
+ISTIO_GENERIC_JWKS_KEY='{ "keys":[{"kty":"RSA","e":"AQAB","kid":"dc1b272d-124e-417f-b6e3-eda9c0e29509","n":"u1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0_IzW7yWR7QkrmBL7jTKEn5u-qKhbwKfBstIs-bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW_VDL5AaWTg0nLVkjRo9z-40RQzuVaE8AkAFmxZzow3x-VJYKdjykkJ0iT9wCS0DRTXu269V264Vf_3jvredZiKRkgwlL9xNAwxXFg0x_XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC-9aGVd-Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmw"}]}'
+ISTIO_GENERIC_JWT="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWJkb21haW4iLCJpc3MiOiJLVUJFUFJPWFkifQ.T5p9ip8yBRAYpArajFGhUlpfnV0HAbA7dPsSojYx1BNo6nwt_cpt6xJ8x66XwV-KqHud_S8hlnixLBYRtUiU8v7lWdk8RhBwW7w4CJs6n8ByvKsjJU8se18RWbSqsi-IQRsdkMiHz5fKosfCGVj6hI214S_yY988ICV7kl9anQhaD8zUPQQvso2zaAkT1qTgC5pxpZc3lB5526DvzsmYr_gaeE-GcbKW9hFoYppOhItL74IRVqRBs_pbaAauUg-9v_bRaJc5yOo3UMFDNiI2HCB6mdgJTLNb8bsT5qExgcbCRpUnOCF0I6PrvVlGft4zZkvz7I0I-8emVn4m-PV-BA"
+
+AUTHSIDECAR_APP_NAME="authsidecar"
+AUTHSIDECAR_DISPLAY_NAME="Authentication Token Fetcher"
 ########################################
 # Setting for common curl-base function
 ########################################
index 5ebc9d3..1c77eda 100755 (executable)
@@ -30,7 +30,7 @@ __print_args() {
        echo "      [--repo-policy local|remote] [--cluster-timeout <timeout-in seconds>] [--print-stats]"
        echo "      [--override <override-environment-filename>] [--pre-clean] [--gen-stats] [--delete-namespaces]"
        echo "      [--delete-containers] [--endpoint-stats] [--kubeconfig <config-file>] [--host-path-dir <local-host-dir>]"
-       echo "      [--kubecontext <context-name>]"
+       echo "      [--kubecontext <context-name>] [--docker-host <docker-host-url>] [--docker-proxy <host-or-ip>]"
 }
 
 if [ $# -eq 1 ] && [ "$1" == "help" ]; then
@@ -68,6 +68,8 @@ if [ $# -eq 1 ] && [ "$1" == "help" ]; then
        echo "--kubeconfig          -  Configure kubectl to use cluster specific cluster config file"
        echo "--host-path-dir       -  (Base-)path on local-hostmounted to all VMs (nodes), for hostpath volumes in kube"
        echo "--kubecontext         -  Configure kubectl to use a certain context, e.g 'minikube'"
+       echo "--docker-host         -  Configure docker to use docker in e.g. a VM"
+       echo "--docker-proxy        -  Configure ip/host to docker when docker is running in a VM"
        echo ""
        echo "List of app short names supported: "$APP_SHORT_NAMES
        exit 0
@@ -268,6 +270,14 @@ DELETE_CONTAINERS=0
 #Var to configure kubectl from a config file or context
 KUBECONF=""
 
+#Localhost, may be set to another host/ip by cmd parameter
+LOCALHOST_NAME="localhost"
+
+#Reseting vars related to token/keys used by kubeproxy when istio is enabled
+#The vars are populated if istio is used in the testcase
+KUBE_PROXY_CURL_JWT=""
+KUBE_PROXY_ISTIO_JWKS_KEYS=""
+
 #Var pointing to dir mounted to each kubernetes node (master and workers)
 #Persistent volumes using "hostpath" are allocated beneath the point.
 #Typically it is a dir on local host mounted to each VM running the master and worker.
@@ -883,6 +893,55 @@ while [ $paramerror -eq 0 ] && [ $foundparm -eq 0 ]; do
                                fi
                        else
                                HOST_PATH_BASE_DIR=$1
+                               echo "Option set - Host path for kube set to: "$1
+                               shift
+                               foundparm=0
+                       fi
+               fi
+       fi
+       if [ $paramerror -eq 0 ]; then
+               if [ "$1" == "--docker-host" ]; then
+                       shift;
+                       if [ -z "$1" ]; then
+                               paramerror=1
+                               if [ -z "$paramerror_str" ]; then
+                                       paramerror_str="No url found for : '--docker-host'"
+                               fi
+                       else
+                               export DOCKER_HOST="$1"
+                               echo "Option set - DOCKER_HOST set to: "$1
+                               shift
+                               foundparm=0
+                       fi
+               fi
+       fi
+       if [ $paramerror -eq 0 ]; then
+               if [ "$1" == "--docker-host" ]; then
+                       shift;
+                       if [ -z "$1" ]; then
+                               paramerror=1
+                               if [ -z "$paramerror_str" ]; then
+                                       paramerror_str="No url found for : '--docker-host'"
+                               fi
+                       else
+                               export DOCKER_HOST="$1"
+                               echo "Option set - DOCKER_HOST set to: "$1
+                               shift
+                               foundparm=0
+                       fi
+               fi
+       fi
+       if [ $paramerror -eq 0 ]; then
+               if [ "$1" == "--docker-proxy" ]; then
+                       shift;
+                       if [ -z "$1" ]; then
+                               paramerror=1
+                               if [ -z "$paramerror_str" ]; then
+                                       paramerror_str="No ip/host found for : '--docker-proxy'"
+                               fi
+                       else
+                               export LOCALHOST_NAME=$1
+                               echo "Option set - docker proxy set to: "$1
                                shift
                                foundparm=0
                        fi
@@ -905,11 +964,6 @@ if [ $paramerror -eq 1 ]; then
        exit 1
 fi
 
-#Localhost constants
-LOCALHOST_NAME="localhost"
-# if [ ! -z "$DOCKER_HOST" ]; then
-#      LOCALHOST_NAME=$(echo $DOCKER_HOST | awk -F[/:] '{print $4}' )
-# fi
 LOCALHOST_HTTP="http://$LOCALHOST_NAME"
 LOCALHOST_HTTPS="https://$LOCALHOST_NAME"
 
@@ -2293,7 +2347,7 @@ __kube_delete_all_resources() {
        namespace=$1
        labelname=$2
        labelid=$3
-       resources="deployments replicaset statefulset services pods configmaps persistentvolumeclaims persistentvolumes serviceaccounts clusterrolebindings secrets"
+       resources="deployments replicaset statefulset services pods configmaps persistentvolumeclaims persistentvolumes serviceaccounts clusterrolebindings secrets authorizationpolicies requestauthentications"
        deleted_resourcetypes=""
        for restype in $resources; do
                ns_flag="-n $namespace"
@@ -2409,7 +2463,7 @@ __kube_delete_namespace() {
        return 0
 }
 
-# Removes a namespace
+# Removes and re-create a namespace
 # args: <namespace>
 # (Not for test scripts)
 clean_and_create_namespace() {
@@ -2427,7 +2481,22 @@ clean_and_create_namespace() {
        if [ $? -ne 0 ]; then
                return 1
        fi
+}
+
+# Add/remove label on non-namespaced kube object
+# args: <api> <instance> <label>
+# (Not for test scripts)
+__kube_label_non_ns_instance() {
+       kubectl $KUBECONF label $1 $2 "$3" 1> /dev/null 2> ./tmp/kubeerr
+       return $?
+}
 
+# Add/remove label on namespaced kube object
+# args: <api> <instance> <namespace> <label>
+# (Not for test scripts)
+__kube_label_ns_instance() {
+       kubectl $KUBECONF label $1 $2 -n $3 "$4" 1> /dev/null 2> ./tmp/kubeerr
+       return $?
 }
 
 # Find the host ip of an app (using the service resource)
@@ -3010,10 +3079,19 @@ __do_curl() {
                        proxyflag=" --proxy-insecure --proxy $KUBE_PROXY_PATH"
                fi
        fi
-       curlString="curl -skw %{http_code} $proxyflag $@"
-       echo " CMD: $curlString" >> $HTTPLOG
-       res=$($curlString)
-       retcode=$?
+
+       if [ ! -z "$KUBE_PROXY_CURL_JWT" ]; then
+               jwt="-H "\""Authorization: Bearer $KUBE_PROXY_CURL_JWT"\"
+               curlString="curl -skw %{http_code} $proxyflag $@"
+               echo " CMD: $curlString $jwt" >> $HTTPLOG
+               res=$($curlString -H "Authorization: Bearer $KUBE_PROXY_CURL_JWT")
+               retcode=$?
+       else
+               curlString="curl -skw %{http_code} $proxyflag $@"
+               echo " CMD: $curlString" >> $HTTPLOG
+               res=$($curlString)
+               retcode=$?
+       fi
        echo " RESP: $res" >> $HTTPLOG
        echo " RETCODE: $retcode" >> $HTTPLOG
        if [ $retcode -ne 0 ]; then
@@ -3041,6 +3119,7 @@ __do_curl() {
 
                return 0
        fi
+
 }
 
 # Generic curl function, assumes all 200-codes are ok
index 367ad68..77b8ee0 100644 (file)
 #
 
 # List of short names for all supported apps, including simulators etc
-APP_SHORT_NAMES="A1PMS ICS SDNC CP NGW RC RICSIM HTTPPROXY CBS CONSUL DMAAPMR MR CR PRODSTUB KUBEPROXY DMAAPMED DMAAPADP PVCCLEANER KAFKAPC CHARTMUS HELMMANAGER LOCALHELM"
+APP_SHORT_NAMES="A1PMS ICS SDNC CP NGW RC RICSIM HTTPPROXY CBS CONSUL DMAAPMR MR CR PRODSTUB KUBEPROXY DMAAPMED DMAAPADP PVCCLEANER KAFKAPC CHARTMUS HELMMANAGER LOCALHELM KEYCLOAK ISTIO AUTHSIDECAR"
 
 # List of available apps that built and released of the project
-PROJECT_IMAGES="A1PMS ICS SDNC CP NGW RICSIM RC DMAAPMED DMAAPADP HELMMANAGER"
+PROJECT_IMAGES="A1PMS ICS SDNC CP NGW RICSIM RC DMAAPMED DMAAPADP HELMMANAGER AUTHSIDECAR"
 
 # List of available apps to override with local or remote staging/snapshot/release image
-AVAILABLE_IMAGES_OVERRIDE="A1PMS ICS SDNC CP NGW RICSIM RC DMAAPMED DMAAPADP HELMMANAGER"
+AVAILABLE_IMAGES_OVERRIDE="A1PMS ICS SDNC CP NGW RICSIM RC DMAAPMED DMAAPADP HELMMANAGER AUTHSIDECAR"
 
 # List of available apps where the image is built by the test environment
 LOCAL_IMAGE_BUILD="MR CR PRODSTUB KUBEPROXY HTTPPROXY KAFKAPC"
index 494db03..b3e70fb 100644 (file)
@@ -35,6 +35,29 @@ spec:
           name: ics-conf-name
         - mountPath: $ICS_CONTAINER_MNT_DIR
           name: ics-data-name
+#ICS_JWT_START
+        - mountPath: $ICS_SIDECAR_MOUNT
+          name: token-cache-volume
+#ICS_JWT_STOP
+#ICS_JWT_START
+      - name: $AUTHSIDECAR_APP_NAME
+        image: $AUTHSIDECAR_IMAGE
+        imagePullPolicy: $KUBE_IMAGE_PULL_POLICY
+        env:
+        - name: CREDS_GRANT_TYPE
+          value: $ICS_CREDS_GRANT_TYPE
+        - name: CREDS_CLIENT_SECRET
+          value: $ICS_CREDS_CLIENT_SECRET
+        - name: CREDS_CLIENT_ID
+          value: $ICS_CREDS_CLIENT_ID
+        - name: OUTPUT_FILE
+          value: $ICS_SIDECAR_JWT_FILE
+        - name: AUTH_SERVICE_URL
+          value: $ICS_AUTH_SERVICE_URL
+        volumeMounts:
+        - mountPath: $ICS_SIDECAR_MOUNT
+          name: token-cache-volume
+#ICS_JWT_STOP
       volumes:
       - configMap:
           defaultMode: 420
@@ -43,6 +66,11 @@ spec:
       - persistentVolumeClaim:
           claimName: $ICS_DATA_PVC_NAME
         name: ics-data-name
+#ICS_JWT_START
+      - name: token-cache-volume
+        emptyDir: {}
+#ICS_JWT_STOP
+
 # Selector will be set when pod is started first time
       nodeSelector:
 
index 3f15aff..786c14c 100644 (file)
@@ -63,3 +63,5 @@ app:
     http.proxy-host: $ICS_HTTP_PROXY_CONFIG_HOST_NAME
     http.proxy-port: $ICS_HTTP_PROXY_CONFIG_PORT
   vardata-directory: $ICS_CONTAINER_MNT_DIR
+  # If the file name is empty, no authorzation token is sent
+  auth-token-file: $ICS_SIDECAR_JWT_FILE
diff --git a/test/simulator-group/istio/ap-principal-template.yaml b/test/simulator-group/istio/ap-principal-template.yaml
new file mode 100644 (file)
index 0000000..c312351
--- /dev/null
@@ -0,0 +1,17 @@
+apiVersion: security.istio.io/v1beta1
+kind: AuthorizationPolicy
+metadata:
+  name: $ISTIO_TEMPLATE_REPLACE_AP_NAME
+  namespace: $ISTIO_TEMPLATE_REPLACE_AP_NS
+  labels:
+    autotest: ISTIO
+spec:
+  selector:
+    matchLabels:
+      run: $ISTIO_TEMPLATE_REPLACE_AP_APP_NAME
+  action: ALLOW
+  rules:
+    - from:
+      - source:
+          requestPrincipals: ["$ISTIO_TEMPLATE_REPLACE_AP_PRINCIPAL"]
+
diff --git a/test/simulator-group/istio/ra-jwks-template.yaml b/test/simulator-group/istio/ra-jwks-template.yaml
new file mode 100644 (file)
index 0000000..4cabff5
--- /dev/null
@@ -0,0 +1,15 @@
+apiVersion: "security.istio.io/v1beta1"
+kind: RequestAuthentication
+metadata:
+  name: $ISTIO_TEMPLATE_REPLACE_RA_NAME
+  namespace: $ISTIO_TEMPLATE_REPLACE_RA_NS
+  labels:
+    autotest: ISTIO
+spec:
+  selector:
+    matchLabels:
+      run: $ISTIO_TEMPLATE_REPLACE_RA_APP_NAME
+  jwtRules:
+  - issuer: "$ISTIO_TEMPLATE_REPLACE_RA_ISSUER"
+    jwks: |
+           $ISTIO_TEMPLATE_REPLACE_RA_JWKS
diff --git a/test/simulator-group/istio/ra-jwksuri-template.yaml b/test/simulator-group/istio/ra-jwksuri-template.yaml
new file mode 100644 (file)
index 0000000..002ebc2
--- /dev/null
@@ -0,0 +1,14 @@
+apiVersion: "security.istio.io/v1beta1"
+kind: RequestAuthentication
+metadata:
+  name: $ISTIO_TEMPLATE_REPLACE_RA_NAME
+  namespace: $ISTIO_TEMPLATE_REPLACE_RA_NS
+  labels:
+    autotest: ISTIO
+spec:
+  selector:
+    matchLabels:
+      run: $ISTIO_TEMPLATE_REPLACE_RA_APP_NAME
+  jwtRules:
+  - issuer: "$ISTIO_TEMPLATE_REPLACE_RA_ISSUER"
+    jwksUri: "$ISTIO_TEMPLATE_REPLACE_RA_JWKSURI"
diff --git a/test/simulator-group/keycloak/svc_app.yaml b/test/simulator-group/keycloak/svc_app.yaml
new file mode 100644 (file)
index 0000000..1d1296f
--- /dev/null
@@ -0,0 +1,59 @@
+apiVersion: v1
+kind: Service
+metadata:
+  name: $KEYCLOAK_APP_NAME
+  namespace: $KUBE_KEYCLOAK_NAMESPACE
+  labels:
+    run: $KEYCLOAK_APP_NAME
+    autotest: KEYCLOAK
+spec:
+  #type: ClusterIP
+  type: LoadBalancer
+  ports:
+  - port: $KEYCLOAK_EXTERNAL_PORT
+    targetPort: $KEYCLOAK_INTERNAL_PORT
+    protocol: TCP
+    name: http
+    nodePort: 32700
+  selector:
+    run: $KEYCLOAK_APP_NAME
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: $KEYCLOAK_APP_NAME
+  namespace: $KUBE_KEYCLOAK_NAMESPACE
+  labels:
+    run: $KEYCLOAK_APP_NAME
+    autotest: KEYCLOAK
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      run: $KEYCLOAK_APP_NAME
+  template:
+    metadata:
+      labels:
+        run: $KEYCLOAK_APP_NAME
+        autotest: KEYCLOAK
+    spec:
+      containers:
+      - name: $KEYCLOAK_APP_NAME
+        image: $KEYCLOAK_IMAGE
+        imagePullPolicy: $KUBE_IMAGE_PULL_POLICY
+        ports:
+        - name: http
+          containerPort: $KEYCLOAK_INTERNAL_PORT
+        args: ["start-dev"]
+        env:
+        - name: KEYCLOAK_ADMIN
+          value: "$KEYCLOAK_ADMIN_USER"
+        - name: KEYCLOAK_ADMIN_PASSWORD
+          value: "$KEYCLOAK_ADMIN_PWD"
+        - name: KC_PROXY
+          value: "$KEYCLOAK_KC_PROXY"
+        - name: KEYCLOAK_FRONTEND_URL
+          value: "http://$KEYCLOAK_APP_NAME.$KUBE_KEYCLOAK_NAMESPACE:$KEYCLOAK_INTERNAL_PORT"
+
+# Selector will be set when pod is started first time
+      nodeSelector:
\ No newline at end of file
diff --git a/test/simulator-group/kubeproxy/jwt-info.txt b/test/simulator-group/kubeproxy/jwt-info.txt
new file mode 100644 (file)
index 0000000..a738728
--- /dev/null
@@ -0,0 +1,82 @@
+
+Info about the generic token used by kubeproxy to all workloads
+New keys and token can be generated at jwt.io
+
+
+Public key:
+===========
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo
+4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u
++qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyeh
+kd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ
+0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdg
+cKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbc
+mwIDAQAB
+-----END PUBLIC KEY-----
+
+
+Private key
+===========
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKj
+MzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvu
+NMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZ
+qgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulg
+p2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlR
+ZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwi
+VuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskV
+laAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8
+sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83H
+mQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwY
+dgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cw
+ta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQ
+DM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2T
+N0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t
+0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPv
+t8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDU
+AhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk
+48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISL
+DY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnK
+xt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEA
+mNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh
+2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfz
+et6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhr
+VBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicD
+TQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cnc
+dn/RsYEONbwQSjIfMPkvxF+8HQ==
+-----END PRIVATE KEY-----
+
+
+Header:
+=======
+{
+  "alg": "RS256",
+  "typ": "JWT"
+}
+
+Payload:
+========
+{
+  "sub": "subdomain",
+  "iss": "KUBEPROXY"
+}
+
+
+Token:
+======
+eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWJkb21haW4iLCJpc3MiOiJLVUJFUFJPWFkifQ.T5p9ip8yBRAYpArajFGhUlpfnV0HAbA7dPsSojYx1BNo6nwt_cpt6xJ8x66XwV-KqHud_S8hlnixLBYRtUiU8v7lWdk8RhBwW7w4CJs6n8ByvKsjJU8se18RWbSqsi-IQRsdkMiHz5fKosfCGVj6hI214S_yY988ICV7kl9anQhaD8zUPQQvso2zaAkT1qTgC5pxpZc3lB5526DvzsmYr_gaeE-GcbKW9hFoYppOhItL74IRVqRBs_pbaAauUg-9v_bRaJc5yOo3UMFDNiI2HCB6mdgJTLNb8bsT5qExgcbCRpUnOCF0I6PrvVlGft4zZkvz7I0I-8emVn4m-PV-BA
+
+
+Curl header:
+============
+-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWJkb21haW4iLCJpc3MiOiJLVUJFUFJPWFkifQ.T5p9ip8yBRAYpArajFGhUlpfnV0HAbA7dPsSojYx1BNo6nwt_cpt6xJ8x66XwV-KqHud_S8hlnixLBYRtUiU8v7lWdk8RhBwW7w4CJs6n8ByvKsjJU8se18RWbSqsi-IQRsdkMiHz5fKosfCGVj6hI214S_yY988ICV7kl9anQhaD8zUPQQvso2zaAkT1qTgC5pxpZc3lB5526DvzsmYr_gaeE-GcbKW9hFoYppOhItL74IRVqRBs_pbaAauUg-9v_bRaJc5yOo3UMFDNiI2HCB6mdgJTLNb8bsT5qExgcbCRpUnOCF0I6PrvVlGft4zZkvz7I0I-8emVn4m-PV-BA"
+
+
+JWKS keys and rule for istio RequestAuthentication
+==================================================
+JWKS is json web keys, converted from the public key above
+  jwtRules:
+  - issuer: "KUBEPROXY"
+    jwks: |
+       { "keys":[{"kty":"RSA","e":"AQAB","kid":"dc1b272d-124e-417f-b6e3-eda9c0e29509","n":"u1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0_IzW7yWR7QkrmBL7jTKEn5u-qKhbwKfBstIs-bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW_VDL5AaWTg0nLVkjRo9z-40RQzuVaE8AkAFmxZzow3x-VJYKdjykkJ0iT9wCS0DRTXu269V264Vf_3jvredZiKRkgwlL9xNAwxXFg0x_XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC-9aGVd-Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmw"}]}
\ No newline at end of file