Add support for cert-manager 54/10954/3
authorktimoney <kevin.timoney@est.tech>
Wed, 19 Apr 2023 10:32:34 +0000 (11:32 +0100)
committerktimoney <kevin.timoney@est.tech>
Thu, 20 Apr 2023 14:24:56 +0000 (15:24 +0100)
Issue-ID: NONRTRIC-866
Change-Id: Id8b9340a5c99d7feff3691ce83295f26dbd5b03d
Signed-off-by: ktimoney <kevin.timoney@est.tech>
21 files changed:
service-exposure/MutatingWebhookConfiguration.yaml
service-exposure/README.md
service-exposure/cert-manager.sh [new file with mode: 0755]
service-exposure/charts/rapp-helloworld-invoker1/values.yaml
service-exposure/charts/rapp-helloworld-invoker2/values.yaml
service-exposure/charts/rapp-helloworld-provider/values.yaml
service-exposure/cluster-issuer.yaml [new file with mode: 0644]
service-exposure/copy_tls_secret.sh [new file with mode: 0755]
service-exposure/issuer.yaml [new file with mode: 0644]
service-exposure/keycloak-client-certificate.yaml [new file with mode: 0644]
service-exposure/keycloak-server-certificate.yaml [new file with mode: 0644]
service-exposure/keycloak.yaml
service-exposure/rapps-helm-installer.go
service-exposure/rapps-istio-mgr.go
service-exposure/rapps-jwt.go
service-exposure/rapps-keycloak-mgr.go
service-exposure/rapps-keycloak-mgr.yaml
service-exposure/rapps-webhook.go
service-exposure/rapps-webhook.yaml
service-exposure/templates/EnvoyFilter-template.txt
service-exposure/webhook-server-certificate.yaml [new file with mode: 0644]

index 1a3dd20..bb5cd6a 100644 (file)
@@ -1,6 +1,6 @@
 #
 # ============LICENSE_START=======================================================
-#  Copyright (C) 2022 Nordix Foundation.
+#  Copyright (C) 2022-2023 Nordix Foundation.
 # ================================================================================
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -22,6 +22,8 @@ kind: MutatingWebhookConfiguration
 metadata:
   name: jwt-proxy-webhook
   namespace: default
+  annotations:
+    cert-manager.io/inject-ca-from: default/webhook-server-cert
 webhooks:
   - name: rapps-webhook.default.svc.cluster.local
     admissionReviewVersions:
@@ -33,10 +35,9 @@ webhooks:
         app.kubernetes.io/name: rapp-helloworld-invoker1
     clientConfig:
       service:
-        name: jwt-proxy-admission-controller
+        name: jwt-proxy-admission-controller 
         namespace: default
         path: "/inject-sidecar"
-      caBundle: "${CA_PEM_B64}" 
     rules:
       - operations: [ "CREATE" ]
         apiGroups: [""]
index 938e4ae..8afe45b 100644 (file)
@@ -21,32 +21,17 @@ This collection of files represent rapp service exposure prototyping in O-RAN.
 Prerequisites: Istio should be installed on your cluster with the demo profile. You may need to add istioctl to you $PATH variable.
   istioctl install --set profile=demo
 Please refer to the istio documentation for more information.
-You will also need cfssl installed on your system: sudo apt install golang-cfssl
 Please refer to the K8s documentation: Manage TLS Certificates in a Cluster
 The deployments have been implemented and tested using minikube.
 If you are not using minikube, references to "minikube ip" should be changed to the appropiate value for you host.
+The ipAddresses field in cluster-issuer.yaml not referring to the generic localhost ip should be changed to your own ip.
 To replicate these tests you will need to setup the various host path referenced in the yaml files on your own machine.
 
 chartmuseum.yaml:             path: /var/chartmuseum/charts
-keycloak.yaml:                path: /var/keycloak/certs
 postgres.yaml:                path: "/var/keycloak/data2"
-rapps-keycloak-mgr.yaml:      path: /var/rapps/certs
-rapps-webhook.yaml:           "-hostPath", "/var/rapps/certs"
 
 or change them to match your own setup.
 
-The certs directory contains 3 shell scripts for creating the server, client and webhook certs: server_certs.sh, client_certs.sh and webhook_certs.sh
-Keystores/Truststores generated by the server_certs.sh script: server.keystore and server.truststore go in the "/var/keycloak/certs" directory
-Certs generated by the client_certs.sh script: client.crt, client.key and rootCA.crt go in the "/var/rapps/certs" directory
-The webhook_certs.sh script generates certs for use in the MutatingWebhookConfiguration.yaml and the rapps-webhook.yaml files.
-To configure MutatingWebhookConfiguration.yaml run the following commands:
-1. ca_pem_b64="$(openssl base64 -A <"./certs/ca.pem")"
-2. sed -i 's/${CA_PEM_B64}/'"$ca_pem_b64"'/g' MutatingWebhookConfiguration.yaml
-
-To configure rapps-webhook.yaml append the rapps-webhook-tls.yaml file to the end of it
-1. cat rapps-webhook.yaml ./certs/rapps-webhook-tls.yaml >> rapps-webhook.yaml.tmp
-2. mv rapps-webhook.yaml.tmp rapps-webhook.yaml
-
 Create the istio-nonrtric namespace and enable it for istio injection
 
    kubectl create ns istio-nonrtric
@@ -90,10 +75,15 @@ You will need to package your rapp charts and copy them to the /var/chartmuseum/
    helm package rapp-helloworld-invoker2
    scp -i $(minikube ssh-key) rapp-helloworld-invoker2-0.1.0.tgz docker@$(minikube ip):/var/chartmuseum/charts
 
+Start cert-manager using the following command:
+   ./cert-manager.sh deploy
 
-Start keycloak and postgres in the default namespace with istio injection:
+Copy keycloak client certs into the istio-nonrtric namespace by running:
+   ./copy_tls_secret.sh -n cm-keycloak-client-certs -s default -d istio-nonrtric
 
-Run  ./keycloak.sh deploy
+Start keycloak and postgres in the default namespace with istio injection by running:
+
+   ./keycloak.sh deploy
 
 To start the management pods run:
 
@@ -138,5 +128,8 @@ To uninstall the management pods and and rapps run:
 You can also uninstall individual rapp using the undeploy_rapp.sh script.
    e.g. ./undeploy_rapp.sh rapp-helloworld-provider
 
-Remove postgres and keycloak with the following commands:
+Remove postgres and keycloak with the following command:
    ./keycloak.sh undeploy
+
+Remove cert-manager with the following command:
+  ./cert-manager.sh undeploy
diff --git a/service-exposure/cert-manager.sh b/service-exposure/cert-manager.sh
new file mode 100755 (executable)
index 0000000..1b5f815
--- /dev/null
@@ -0,0 +1,79 @@
+#!/bin/bash
+#
+# ============LICENSE_START=======================================================
+#  Copyright (C) 2023 Nordix Foundation.
+# ================================================================================
+# 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.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=========================================================
+#
+
+if [ -z "$1" ]
+  then
+    echo "No argument supplied"
+    exit 1
+fi
+
+OPERATION=$1
+WORKDIR=$(dirname "$(realpath "$0")")
+
+if [ "$OPERATION" == "deploy" ]; then
+        echo "Deploying cert-manager application..."
+        echo "-------------------------------------"
+        kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.11.0/cert-manager.yaml
+        echo ""
+        echo "Waiting for pods to start..."
+        echo "----------------------------"
+       kubectl wait deployment -n cert-manager cert-manager --for=condition=available --timeout=300s
+       kubectl wait deployment -n cert-manager cert-manager-cainjector --for=condition=available --timeout=300s
+        kubectl wait deployment -n cert-manager cert-manager-webhook --for=condition=available --timeout=300s
+        echo ""
+        echo "Checking pod status..."
+        echo "----------------------"
+        kubectl get pods -n cert-manager
+        echo ""
+       # Once the pods are up and running we still need to wait for the certificate controller process to start
+       # before certificates can be issued
+        echo "Waiting for certificate controller..."
+        echo "------------------------------------"
+       sleep 100
+        echo ""
+        echo "Creating certificates..."
+        echo "------------------------"
+       kubectl apply -f $WORKDIR/cluster-issuer.yaml
+        kubectl apply -f $WORKDIR/issuer.yaml
+        kubectl apply -f $WORKDIR/webhook-server-certificate.yaml
+       kubectl apply -f $WORKDIR/keycloak-server-certificate.yaml
+        kubectl apply -f $WORKDIR/keycloak-client-certificate.yaml
+elif [ "$OPERATION" == "undeploy" ]; then
+        echo "Deleting certificates..."
+        echo "------------------------"
+       kubectl delete -f $WORKDIR/cluster-issuer.yaml
+        kubectl delete -f $WORKDIR/issuer.yaml
+        kubectl delete -f $WORKDIR/webhook-server-certificate.yaml
+       kubectl delete -f $WORKDIR/keycloak-server-certificate.yaml
+        kubectl delete -f $WORKDIR/keycloak-client-certificate.yaml
+       kubectl delete secret -n default cm-cluster-issuer-rootca-secret
+        kubectl delete secret -n default cm-keycloak-client-certs
+        kubectl delete secret -n default cm-keycloak-server-certs
+        kubectl delete secret -n default cm-webhook-server-certs
+        echo "Undeploying cert-manager application..."
+        echo "---------------------------------------"
+       kubectl delete -f https://github.com/cert-manager/cert-manager/releases/download/v1.11.0/cert-manager.yaml
+else
+       echo "Unrecogized operation ${OPERATION}"
+       exit 1
+fi
+
+exit 0
index 8e89cff..f8c36e3 100644 (file)
@@ -1,6 +1,6 @@
 #
 # ============LICENSE_START=======================================================
-#  Copyright (C) 2022 Nordix Foundation.
+#  Copyright (C) 2022-2023 Nordix Foundation.
 # ================================================================================
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -113,6 +113,9 @@ rapp:
   realm: demo
   client: demoprovider-cli
   authenticator: client-jwt
+  caCrt: "/certs/ca.crt"
+  tlsCrt: "/certs/tls.crt"
+  tlsKey: "/certs/tls.key"
   roles:
   - role : provider-viewer
     grants:
index bd6f5ab..4d7a1fa 100644 (file)
@@ -1,6 +1,6 @@
 #
 # ============LICENSE_START=======================================================
-#  Copyright (C) 2022 Nordix Foundation.
+#  Copyright (C) 2022-2023 Nordix Foundation.
 # ================================================================================
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -113,6 +113,9 @@ rapp:
   realm: demo
   client: demoprovider-cli
   authenticator: client-jwt
+  caCrt: "/certs/ca.crt"
+  tlsCrt: "/certs/tls.crt"
+  tlsKey: "/certs/tls.key"
   roles:
   - role : provider-viewer
     grants:
index bb6ef19..d5435f9 100644 (file)
@@ -1,6 +1,6 @@
 #
 # ============LICENSE_START=======================================================
-#  Copyright (C) 2022 Nordix Foundation.
+#  Copyright (C) 2022-2023 Nordix Foundation.
 # ================================================================================
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -112,6 +112,10 @@ rapp:
   realm: demo
   client: demoprovider-cli
   authenticator: client-jwt
+  tlsCrt: "/certs/tls.crt"
+  email: "client@mail.com"
+  subjectDN: oran
+  mappingSource: "Subject's Alternative Name E-mail"
   roles:
   - role : provider-viewer
     grants:
diff --git a/service-exposure/cluster-issuer.yaml b/service-exposure/cluster-issuer.yaml
new file mode 100644 (file)
index 0000000..e6d96b2
--- /dev/null
@@ -0,0 +1,66 @@
+#
+# ============LICENSE_START=======================================================
+#  Copyright (C) 2023 Nordix Foundation.
+# ================================================================================
+# 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.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=========================================================
+#
+apiVersion: cert-manager.io/v1
+kind: ClusterIssuer
+metadata:
+  name: selfsigned-rootca-cluster-issuer
+spec:
+  selfSigned: {}
+---
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+  name: selfsigned-rootca
+  namespace: default
+spec:
+  isCA: true
+  duration: 2160h # 90d
+  renewBefore: 360h # 15d
+  commonName: selfsigned-rootca
+  subject:
+    organizations:
+      - oran
+    organizationalUnits:
+      - oran
+    countries:
+      - Ireland
+    localities:
+      - Dublin
+    streetAddresses:
+      - Main Street
+  secretName: cm-cluster-issuer-rootca-secret
+  privateKey:
+    rotationPolicy: Always
+    algorithm: RSA
+    encoding: PKCS1
+    size: 2048
+  issuerRef:
+    name: selfsigned-rootca-cluster-issuer
+    kind: ClusterIssuer
+    group: cert-manager.io
+  dnsNames:
+  - localhost
+  - minikube
+  ipAddresses:
+    - 127.0.0.1
+    - 192.168.49.2
+  emailAddresses:
+    - ca@mail.com
+---
diff --git a/service-exposure/copy_tls_secret.sh b/service-exposure/copy_tls_secret.sh
new file mode 100755 (executable)
index 0000000..5b2fbe7
--- /dev/null
@@ -0,0 +1,83 @@
+#!/bin/bash
+#
+# ============LICENSE_START=======================================================
+#  Copyright (C) 2023 Nordix Foundation.
+# ================================================================================
+# 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.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=========================================================
+#
+
+function usage()
+{
+   echo ""
+   echo "Usage: $0 -n secretName -s sourceNamespace -d destinationNamespace"
+   echo -e "\t-n Name of the secret"
+   echo -e "\t-s Namespace of the secret"
+   echo -e "\t-d Namespace to copy the secret to"
+   exit 1
+}
+
+while getopts "n:s:d:" opt
+do
+   case "$opt" in
+      n ) secretName="$OPTARG" ;;
+      s ) sourceNS="$OPTARG" ;;
+      d ) destinationNS="$OPTARG" ;;
+      ? ) usage ;;
+   esac
+done
+
+# Check if any of the paramters are empty
+if [ -z "$secretName" ] || [ -z "$sourceNS" ] || [ -z "$destinationNS" ]
+then
+   echo "Some or all of the parameters are empty";
+   usage
+fi
+
+# Check if the secret exits
+kubectl get secret $secretName -n $sourceNS >/dev/null 2>/dev/null
+if [ $? -ne 0 ]
+then
+   echo "$secretName in $sourceNS does not exist"
+   usage
+fi
+
+# Check if the destination namespace exists
+kubectl get ns $destinationNS >/dev/null 2>/dev/null
+if [ $? -ne 0 ]
+then
+   echo "$destinationNS does not exist"
+   usage
+fi
+
+# Begin script in case all parameters are correct
+echo "Copying $secretName from $sourceNS to $destinationNS"
+
+tlsCrt=$(kubectl get secret ${secretName} -n ${sourceNS} -o json -o=jsonpath="{.data.tls\.crt}")
+tlsKey=$(kubectl get secret ${secretName} -n ${sourceNS} -o json -o=jsonpath="{.data.tls\.key}")
+caCrt=$(kubectl get secret ${secretName} -n ${sourceNS} -o json -o=jsonpath="{.data.ca\.crt}")
+
+kubectl apply -f - <<EOF
+apiVersion: v1
+data:
+  tls.crt: ${tlsCrt}
+  tls.key: ${tlsKey}
+  ca.crt: ${caCrt}
+kind: Secret
+metadata:
+  name: ${secretName}
+  namespace: ${destinationNS}
+type: kubernetes.io/tls
+EOF
diff --git a/service-exposure/issuer.yaml b/service-exposure/issuer.yaml
new file mode 100644 (file)
index 0000000..1484d52
--- /dev/null
@@ -0,0 +1,27 @@
+#
+# ============LICENSE_START=======================================================
+#  Copyright (C) 2023 Nordix Foundation.
+# ================================================================================
+# 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.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=========================================================
+#
+apiVersion: cert-manager.io/v1
+kind: Issuer
+metadata:
+  name: cm-ca-issuer
+  namespace: default
+spec:
+  ca:
+    secretName: cm-cluster-issuer-rootca-secret
diff --git a/service-exposure/keycloak-client-certificate.yaml b/service-exposure/keycloak-client-certificate.yaml
new file mode 100644 (file)
index 0000000..9dbd327
--- /dev/null
@@ -0,0 +1,57 @@
+#
+# ============LICENSE_START=======================================================
+#  Copyright (C) 2023 Nordix Foundation.
+# ================================================================================
+# 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.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=========================================================
+#
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+  name: keycloak-client-cert
+  namespace: default
+spec:
+  secretName: cm-keycloak-client-certs
+  duration: 2160h # 90d
+  renewBefore: 360h # 15d
+  subject:
+    organizations:
+      - oran
+    organizationalUnits:
+      - oran
+    countries:
+      - IE
+    localities:
+      - Dublin
+    streetAddresses:
+      - Main Street
+  commonName: keycloak
+  isCA: false
+  privateKey:
+    algorithm: RSA
+    encoding: PKCS1
+    size: 2048
+  usages:
+    - client auth
+  dnsNames:
+    - keycloak.default
+    - keycloak
+    - keycloak.est.tech
+  emailAddresses:
+    - client@mail.com
+  issuerRef:
+    name: cm-ca-issuer
+    kind: Issuer
+    group: cert-manager.io
diff --git a/service-exposure/keycloak-server-certificate.yaml b/service-exposure/keycloak-server-certificate.yaml
new file mode 100644 (file)
index 0000000..9196b2b
--- /dev/null
@@ -0,0 +1,72 @@
+#
+# ============LICENSE_START=======================================================
+#  Copyright (C) 2023 Nordix Foundation.
+# ================================================================================
+# 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.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=========================================================
+#
+apiVersion: v1
+kind: Secret
+metadata:
+  name: cm-keycloak-jwk-pw
+  namespace:  default
+type: Opaque
+data:
+  password: Y2hhbmdlaXQ=
+---
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+  name: keycloak-server-cert
+  namespace: default
+spec:
+  secretName: cm-keycloak-server-certs
+  duration: 2160h # 90d
+  renewBefore: 360h # 15d
+  subject:
+    organizations:
+      - oran
+    organizationalUnits:
+      - oran
+    countries:
+      - IE
+    localities:
+      - Dublin
+    streetAddresses:
+      - Main Street
+  commonName: keycloak
+  isCA: false
+  keystores:
+    jks:
+      create: true
+      passwordSecretRef:
+        name: cm-keycloak-jwk-pw
+        key: password
+  privateKey:
+    algorithm: RSA
+    encoding: PKCS1
+    size: 2048
+  usages:
+    - server auth
+  dnsNames:
+    - keycloak.default
+    - keycloak
+    - keycloak.est.tech
+  emailAddresses:
+    - server@mail.com
+  issuerRef:
+    name: cm-ca-issuer
+    kind: Issuer
+    group: cert-manager.io
index b6a18c3..a23b2cc 100644 (file)
@@ -1,6 +1,6 @@
 #
 # ============LICENSE_START=======================================================
-#  Copyright (C) 2022-2023 Nordix Foundation.
+#  Copyright (C) 2022-23 Nordix Foundation.
 # ================================================================================
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@
 apiVersion: v1
 kind: ServiceAccount
 metadata:
-  name: keycloak 
+  name: keycloak
   namespace: default
 ---
 apiVersion: v1
@@ -29,14 +29,16 @@ metadata:
   name: keycloak
   labels:
     app: keycloak
+    app.kubernetes.io/instance: keycloak
+    app.kubernetes.io/name: keycloak
 spec:
   type: ExternalName
-  externalName: keycloak.local 
+  externalName: keycloak.local
   ports:
   - name: http
     port: 8080
     targetPort: 8080
-    nodePort: 31560 
+    nodePort: 31560
   - name: https
     port: 8443
     targetPort: 8443
@@ -52,6 +54,8 @@ metadata:
   namespace: default
   labels:
     app: keycloak
+    app.kubernetes.io/instance: keycloak
+    app.kubernetes.io/name: keycloak
 spec:
   replicas: 1
   selector:
@@ -61,52 +65,58 @@ spec:
     metadata:
       labels:
         app: keycloak
+        app.kubernetes.io/instance: keycloak
+        app.kubernetes.io/name: keycloak
     spec:
       initContainers:
       - name: init-postgres
         image: busybox
-        imagePullPolicy: IfNotPresent 
+        imagePullPolicy: IfNotPresent
         command: ['sh', '-c', 'until nc -vz postgres 5432; do echo waiting for postgres db; sleep 2; done;']
-      serviceAccountName: keycloak 
+      serviceAccountName: keycloak
       containers:
       - name: keycloak
         image: quay.io/keycloak/keycloak:latest
-        imagePullPolicy: IfNotPresent 
-        args: [ 
+        imagePullPolicy: IfNotPresent
+        args: [
                 'start',
-                '--https-key-store-file=/etc/x509/https/server.keystore',
-                '--https-key-store-password=changeit',
-                '--https-key-store-type=PKCS12',
-                '--https-trust-store-file=/etc/x509/https/server.truststore',
-                '--https-trust-store-password=changeit',
-                '--https-trust-store-type=PKCS12',
+                '--https-key-store-file=/etc/x509/https/keystore.jks',
+                '--https-key-store-password=$(KC_KEYSTORE_PASSWORD)',
+                '--https-key-store-type=JKS',
+                '--https-trust-store-file=/etc/x509/https/truststore.jks',
+                '--https-trust-store-password=$(KC_KEYSTORE_PASSWORD)',
+                '--https-trust-store-type=JKS',
                 '--https-client-auth=request',
                 '--http-enabled=true'
               ]
-
         env:
-        - name : X509_CA_BUNDLE 
-          value: /etc/x509/https/rootCA.crt 
-        - name : KEYCLOAK_ADMIN 
-          value: admin 
-        - name : KEYCLOAK_ADMIN_PASSWORD 
-          value: admin 
-        - name : KC_DB 
-          value: postgres 
-        - name : KC_DB_URL 
-          value: "jdbc:postgresql://postgres:5432/keycloak"  
+        - name : KEYCLOAK_ADMIN
+          value: admin
+        - name : KEYCLOAK_ADMIN_PASSWORD
+          value: admin
+        - name : KC_DB
+          value: postgres
+        - name : KC_DB_URL
+          value: "jdbc:postgresql://postgres:5432/keycloak"
         - name : KC_DB_USERNAME
           value: keycloak
         - name : KC_DB_PASSWORD
           value: keycloak
         - name : KC_HOSTNAME
-          value: keycloak 
-        - name : MY_PROVIDER_JAR_URL 
-          value: /opt/jboss/keycloak/standalone/deployments/authz-js-policies.jar 
+          value: keycloak
+        - name:  KC_DB_URL_DATABASE
+          value: keycloak
+        - name : MY_PROVIDER_JAR_URL
+          value: /opt/jboss/keycloak/standalone/deployments/authz-js-policies.jar
         - name: KC_HEALTH_ENABLED
           value: "true"
-        - name: KC_METRICS_ENABLED 
+        - name: KC_METRICS_ENABLED
           value: "true"
+        - name: KC_KEYSTORE_PASSWORD
+          valueFrom:
+            secretKeyRef:
+              name: cm-keycloak-jwk-pw
+              key: password
         ports:
         - name: http
           containerPort: 8080
@@ -114,22 +124,22 @@ spec:
           containerPort: 8443
         readinessProbe:
           httpGet:
-            scheme: HTTPS 
-            path: /health/ready 
+            scheme: HTTPS
+            path: /health/ready
             port: 8443
         volumeMounts:
-        - name: keycloak-certs 
-          mountPath: /etc/x509/https 
-        - name: authz-js-policies 
-          mountPath: /opt/jboss/keycloak/standalone/deployments/authz-js-policies.jar 
+        - name: keycloak-certs
+          mountPath: /etc/x509/https
+          readOnly: true
+        - name: authz-js-policies
+          mountPath: /opt/jboss/keycloak/standalone/deployments/authz-js-policies.jar
       volumes:
-      - name: keycloak-certs 
+      - name: keycloak-certs
+        secret:
+          secretName: cm-keycloak-server-certs
+      - name: authz-js-policies
         hostPath:
-           path: /var/keycloak/certs 
-           type: Directory
-      - name: authz-js-policies 
-        hostPath:
-           path: /var/keycloak/deployments/authz-js-policies.jar 
+           path: /var/keycloak/deployments/authz-js-policies.jar
            type: File
 ---
 apiVersion: networking.istio.io/v1alpha3
@@ -183,7 +193,7 @@ spec:
   hosts:
   - "*"
   gateways:
-  - kcgateway 
+  - kcgateway
   http:
   - name: "keycloak-routes"
     match:
index 4a5d464..857be9b 100644 (file)
@@ -1,25 +1,26 @@
 // -
-//   ========================LICENSE_START=================================
-//   O-RAN-SC
-//   %%
-//   Copyright (C) 2022-2023: Nordix Foundation
-//   %%
-//   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
+//     ========================LICENSE_START=================================
+//     O-RAN-SC
+//     %%
+//     Copyright (C) 2022-2023: Nordix Foundation
+//     %%
+//     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
 //
-//   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===================================
+//          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===================================
 package main
 
 import (
+       "bytes"
        "context"
        "database/sql"
        "encoding/json"
@@ -42,12 +43,11 @@ import (
        "net/http"
        "os"
        "path/filepath"
+       "time"
 )
 
 var settings *cli.EnvSettings
 var chartRequested *chart.Chart
-
-//var url string
 var repoName string
 var chartName string
 var releaseName string
@@ -70,6 +70,12 @@ type Rapp struct {
        Realm           string
        Client          string
        Authenticator   string
+       CaCrt           string
+       TlsCrt          string
+       TlsKey          string
+       Email           string
+       SubjectDN       string
+       MappingSource   string
        Roles           []struct {
                Role   string
                Grants []string
@@ -147,6 +153,16 @@ func installSecurity(rapp Rapp) error {
        realm := rapp.Realm
        client := rapp.Client
        authenticator := rapp.Authenticator
+       caCrt := rapp.CaCrt
+       tlsCrt := rapp.TlsCrt
+       tlsKey := rapp.TlsKey
+       email := rapp.Email
+       subjectDN := rapp.SubjectDN
+       mappingSource := rapp.MappingSource
+
+       httpClient := &http.Client{
+               Timeout: time.Second * 10,
+       }
 
        if !rapp.SecurityEnabled {
                return nil
@@ -155,11 +171,19 @@ func installSecurity(rapp Rapp) error {
        if rapp.Type == "provider" {
                // keycloak client setup
                fmt.Println("Setting up keycloak")
-               url = "http://rapps-keycloak-mgr.default/create?"
-               params = "realm=" + realm + "&name=" + client + "&role=" + role + "&authType=" + authenticator
-               url += params
-               _, err := http.Get(url)
+               url = "http://rapps-keycloak-mgr.default/create"
+               values := map[string]string{"realm": realm, "name": client, "role": role, "authType": authenticator,
+                       "tlsCrt": tlsCrt, "email": email, "subjectDN": subjectDN, "mappingSource": mappingSource}
+               jsonValue, _ := json.Marshal(values)
+               req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonValue))
+               if err != nil {
+                       fmt.Printf("Got error %s", err.Error())
+               }
+               req.Header.Set("Content-type", "application/json")
+               resp, err := httpClient.Do(req)
+               fmt.Println("Keycloak response status:", resp.Status)
                if err != nil {
+                       fmt.Printf("Got error %s", err.Error())
                        return err
                } else {
                        fmt.Println("Setting up istio")
@@ -175,7 +199,8 @@ func installSecurity(rapp Rapp) error {
        } else {
                fmt.Println("Setting up istio")
                url = "http://rapps-istio-mgr.default/create-filter?"
-               params = "name=" + chartName + "&realm=" + realm + "&client=" + client + "&authType=" + authenticator
+               params = "name=" + chartName + "&realm=" + realm + "&client=" + client + "&authType=" + authenticator +
+                       "&tlsCrt=" + tlsCrt + "&tlsKey=" + tlsKey + "&caCrt=" + caCrt
                url += params
                _, err := http.Get(url)
                if err != nil {
@@ -272,7 +297,6 @@ func runList(res http.ResponseWriter, req *http.Request) {
 }
 
 func main() {
-       //flag.StringVar(&url, "url", "http://chartmuseum:8080", "ChartMuseum url")
        flag.StringVar(&repoName, "repoName", "local-dev", "Repository name")
        flag.StringVar(&namespace, "namespace", "istio-nonrtric", "namespace for install")
        flag.Parse()
@@ -341,10 +365,10 @@ func dryRun() (*action.Install, error) {
 
        install := action.NewInstall(actionConfig)
 
-       fmt.Printf("Repo Name: %s\n",repoName)
-       fmt.Printf("Chart Name: %s\n",chartName)
+       fmt.Printf("Repo Name: %s\n", repoName)
+       fmt.Printf("Chart Name: %s\n", chartName)
        cp, err := install.ChartPathOptions.LocateChart(fmt.Sprintf("%s/%s", repoName, chartName), settings)
-       fmt.Printf("Chart location: %s\n",cp)
+       fmt.Printf("Chart location: %s\n", cp)
 
        chartRequested, err = loader.Load(cp)
 
@@ -540,7 +564,7 @@ func registrerRapp(chartName, chartType string) {
        id serial PRIMARY KEY,
        name VARCHAR ( 50 ) UNIQUE NOT NULL,
        type VARCHAR ( 50 ) NOT NULL,
-       created_on TIMESTAMP DEFAULT NOW() 
+       created_on TIMESTAMP DEFAULT NOW()
         );`
        _, err = db.Exec(createStmt)
        if err != nil {
index fb584bd..fe68cb0 100644 (file)
@@ -1,30 +1,30 @@
 // -
-//   ========================LICENSE_START=================================
-//   O-RAN-SC
-//   %%
-//   Copyright (C) 2022: Nordix Foundation
-//   %%
-//   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
+//     ========================LICENSE_START=================================
+//     O-RAN-SC
+//     %%
+//     Copyright (C) 2022-2023: Nordix Foundation
+//     %%
+//     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
 //
-//   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===================================
+//          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===================================
 package main
 
 import (
        "bytes"
        "context"
        "fmt"
-       netv1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1"
        netv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3"
+       netv1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1"
        secv1beta1 "istio.io/client-go/pkg/apis/security/v1beta1"
        versioned "istio.io/client-go/pkg/clientset/versioned"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -43,13 +43,16 @@ const (
 )
 
 type TemplateConfig struct {
-    Name string
-    Namespace string
-    Realm string
-    Client string
-    Authenticator string
-    Role string
-    Method string
+       Name          string
+       Namespace     string
+       Realm         string
+       Client        string
+       Authenticator string
+       Role          string
+       Method        string
+       TlsCrt        string
+       TlsKey        string
+       CaCrt         string
 }
 
 var inputs TemplateConfig
@@ -86,12 +89,12 @@ func connectToK8s() *versioned.Clientset {
 
 func createGateway(clientset *versioned.Clientset) (string, error) {
        gtClient := clientset.NetworkingV1beta1().Gateways(NAMESPACE)
-        config = template.Must(template.ParseFiles("./templates/Gateway-template.txt"))
-        var manifest bytes.Buffer
-        err := config.Execute(&manifest, inputs)
-        if err != nil {
-                return "", err
-        }
+       config = template.Must(template.ParseFiles("./templates/Gateway-template.txt"))
+       var manifest bytes.Buffer
+       err := config.Execute(&manifest, inputs)
+       if err != nil {
+               return "", err
+       }
 
        gt := &netv1beta1.Gateway{}
        dec := k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(manifest.String())), 1000)
@@ -112,12 +115,12 @@ func createGateway(clientset *versioned.Clientset) (string, error) {
 
 func createVirtualService(clientset *versioned.Clientset) (string, error) {
        vsClient := clientset.NetworkingV1beta1().VirtualServices(NAMESPACE)
-        config = template.Must(template.ParseFiles("./templates/VirtualService-template.txt"))
-        var manifest bytes.Buffer
-        err := config.Execute(&manifest, inputs)
-        if err != nil {
-                return "", err
-        }
+       config = template.Must(template.ParseFiles("./templates/VirtualService-template.txt"))
+       var manifest bytes.Buffer
+       err := config.Execute(&manifest, inputs)
+       if err != nil {
+               return "", err
+       }
 
        vs := &netv1beta1.VirtualService{}
        dec := k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(manifest.String())), 1000)
@@ -138,12 +141,12 @@ func createVirtualService(clientset *versioned.Clientset) (string, error) {
 
 func createRequestAuthentication(clientset *versioned.Clientset) (string, error) {
        raClient := clientset.SecurityV1beta1().RequestAuthentications(NAMESPACE)
-        config = template.Must(template.ParseFiles("./templates/RequestAuthentication-template.txt"))
-        var manifest bytes.Buffer
-        err := config.Execute(&manifest, inputs)
-        if err != nil {
-                return "", err
-        }
+       config = template.Must(template.ParseFiles("./templates/RequestAuthentication-template.txt"))
+       var manifest bytes.Buffer
+       err := config.Execute(&manifest, inputs)
+       if err != nil {
+               return "", err
+       }
 
        ra := &secv1beta1.RequestAuthentication{}
        dec := k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(manifest.String())), 1000)
@@ -164,12 +167,12 @@ func createRequestAuthentication(clientset *versioned.Clientset) (string, error)
 
 func createAuthorizationPolicy(clientset *versioned.Clientset) (string, error) {
        apClient := clientset.SecurityV1beta1().AuthorizationPolicies(NAMESPACE)
-        config = template.Must(template.ParseFiles("./templates/AuthorizationPolicy-template.txt"))
-        var manifest bytes.Buffer
-        err := config.Execute(&manifest, inputs)
-        if err != nil {
-                return "", err
-        }
+       config = template.Must(template.ParseFiles("./templates/AuthorizationPolicy-template.txt"))
+       var manifest bytes.Buffer
+       err := config.Execute(&manifest, inputs)
+       if err != nil {
+               return "", err
+       }
 
        ap := &secv1beta1.AuthorizationPolicy{}
        dec := k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(manifest.String())), 1000)
@@ -189,29 +192,29 @@ func createAuthorizationPolicy(clientset *versioned.Clientset) (string, error) {
 }
 
 func createEnvoyFilter(clientset *versioned.Clientset) (string, error) {
-        efClient := clientset.NetworkingV1alpha3().EnvoyFilters(NAMESPACE)
+       efClient := clientset.NetworkingV1alpha3().EnvoyFilters(NAMESPACE)
        config = template.Must(template.ParseFiles("./templates/EnvoyFilter-template.txt"))
        var manifest bytes.Buffer
-        err := config.Execute(&manifest, inputs)
-        if err != nil {
-                return "", err
-        }
+       err := config.Execute(&manifest, inputs)
+       if err != nil {
+               return "", err
+       }
 
-        ef := &netv1alpha3.EnvoyFilter{}
-        dec := k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(manifest.String())), 1000)
+       ef := &netv1alpha3.EnvoyFilter{}
+       dec := k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(manifest.String())), 1000)
 
-        if err = dec.Decode(&ef); err != nil {
-                return "", err
-        }
+       if err = dec.Decode(&ef); err != nil {
+               return "", err
+       }
 
-        result, err := efClient.Create(context.TODO(), ef, metav1.CreateOptions{})
+       result, err := efClient.Create(context.TODO(), ef, metav1.CreateOptions{})
 
-        if err != nil {
-                return "", err
-        }
+       if err != nil {
+               return "", err
+       }
 
-        fmt.Printf("Create Envoy Filter %s \n", result.GetName())
-        return result.GetName(), nil
+       fmt.Printf("Create Envoy Filter %s \n", result.GetName())
+       return result.GetName(), nil
 }
 
 func removeGateway(clientset *versioned.Clientset) {
@@ -256,12 +259,12 @@ func removeAuthorizationPolicy(clientset *versioned.Clientset) {
 
 func removeEnvoyFilter(clientset *versioned.Clientset) {
        efClient := clientset.NetworkingV1alpha3().EnvoyFilters(NAMESPACE)
-        err := efClient.Delete(context.TODO(), appName+"-outbound-filter", metav1.DeleteOptions{})
-        if err != nil {
-                fmt.Println(err)
-        } else {
-                fmt.Println("Deleted EnvoyFilter " + appName + "-outbound-filter")
-        }
+       err := efClient.Delete(context.TODO(), appName+"-outbound-filter", metav1.DeleteOptions{})
+       if err != nil {
+               fmt.Println(err)
+       } else {
+               fmt.Println("Deleted EnvoyFilter " + appName + "-outbound-filter")
+       }
 }
 
 func createIstioPolicy(res http.ResponseWriter, req *http.Request) {
@@ -270,7 +273,7 @@ func createIstioPolicy(res http.ResponseWriter, req *http.Request) {
        appName := query.Get("name")
        roleName := query.Get("role")
        methodName := query.Get("method")
-       inputs = TemplateConfig{Name: appName, Namespace: NAMESPACE, Realm: realmName, Role: roleName, Method: methodName }
+       inputs = TemplateConfig{Name: appName, Namespace: NAMESPACE, Realm: realmName, Role: roleName, Method: methodName}
        var msg string
        clientset := connectToK8s()
        _, err := createGateway(clientset)
@@ -306,23 +309,27 @@ func createIstioPolicy(res http.ResponseWriter, req *http.Request) {
 }
 
 func createIstioFilter(res http.ResponseWriter, req *http.Request) {
-        query := req.URL.Query()
-        realmName := query.Get("realm")
-        clientId := query.Get("client")
-        appName := query.Get("name")
-        authType := query.Get("authType")
-       inputs = TemplateConfig{Name: appName, Namespace: NAMESPACE, Realm: realmName, Client: clientId, Authenticator: authType}
-        var msg string
-        clientset := connectToK8s()
-        _, err := createEnvoyFilter(clientset)
-        if err != nil {
-                msg = err.Error()
-                fmt.Println(err.Error())
-        }
-        // create response binary data
-        data := []byte(msg) // slice of bytes
-        // write `data` to response
-        res.Write(data)
+       query := req.URL.Query()
+       realmName := query.Get("realm")
+       clientId := query.Get("client")
+       appName := query.Get("name")
+       authType := query.Get("authType")
+       tlsCrt := query.Get("tlsCrt")
+       tlsKey := query.Get("tlsKey")
+       caCrt := query.Get("caCrt")
+       inputs = TemplateConfig{Name: appName, Namespace: NAMESPACE, Realm: realmName, Client: clientId,
+               Authenticator: authType, TlsCrt: tlsCrt, TlsKey: tlsKey, CaCrt: caCrt}
+       var msg string
+       clientset := connectToK8s()
+       _, err := createEnvoyFilter(clientset)
+       if err != nil {
+               msg = err.Error()
+               fmt.Println(err.Error())
+       }
+       // create response binary data
+       data := []byte(msg) // slice of bytes
+       // write `data` to response
+       res.Write(data)
 }
 
 func removeIstioPolicy(res http.ResponseWriter, req *http.Request) {
@@ -336,10 +343,10 @@ func removeIstioPolicy(res http.ResponseWriter, req *http.Request) {
 }
 
 func removeIstioFilter(res http.ResponseWriter, req *http.Request) {
-        query := req.URL.Query()
-        appName = query.Get("name")
-        clientset := connectToK8s()
-        removeEnvoyFilter(clientset)
+       query := req.URL.Query()
+       appName = query.Get("name")
+       clientset := connectToK8s()
+       removeEnvoyFilter(clientset)
 }
 
 func main() {
index ae00432..d0e1d3d 100644 (file)
@@ -1,25 +1,26 @@
 // -
-//   ========================LICENSE_START=================================
-//   O-RAN-SC
-//   %%
-//   Copyright (C) 2022-2023: Nordix Foundation
-//   %%
-//   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
+//     ========================LICENSE_START=================================
+//     O-RAN-SC
+//     %%
+//     Copyright (C) 2022-2023: Nordix Foundation
+//     %%
+//     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
 //
-//   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===================================
+//          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===================================
 package main
 
 import (
+       "context"
        "crypto/tls"
        "crypto/x509"
        "encoding/json"
@@ -29,11 +30,10 @@ import (
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
        kubernetes "k8s.io/client-go/kubernetes"
        "k8s.io/client-go/rest"
+       "net"
        "net/http"
        "net/url"
        "rapps/utils/generatejwt"
-       "context"
-       "net"
        "time"
 )
 
@@ -55,6 +55,9 @@ var realmName string
 var clientId string
 var namespace string
 var authenticator string
+var tlsCrt string
+var tlsKey string
+var caCrt string
 var healthy bool = true
 var jwt Jwttoken
 
@@ -70,6 +73,9 @@ func getToken(res http.ResponseWriter, req *http.Request) {
        clientId = req.Header.Get("client")
        realmName = req.Header.Get("realm")
        namespace = req.Header.Get("ns")
+       tlsCrt = req.Header.Get("tlsCrt")
+       tlsKey = req.Header.Get("tlsKey")
+       caCrt = req.Header.Get("caCrt")
        keycloakUrl := "http://" + keycloakHost + ":" + keycloakPort + "/realms/" + realmName + "/protocol/openid-connect/token"
        fmt.Printf("Making token request to %s\n", keycloakUrl)
        res.Header().Set("Content-type", "application/json")
@@ -80,7 +86,7 @@ func getToken(res http.ResponseWriter, req *http.Request) {
        } else if authenticator == "client-x509" {
                keycloakPort = "443"
                keycloakUrl := "https://" + keycloakAlias + ":" + keycloakPort + "/realms/" + realmName + "/protocol/openid-connect/token"
-               resp, err = getx509Token(keycloakUrl, clientId)
+               resp, err = getx509Token(keycloakUrl, clientId, tlsCrt, tlsKey, caCrt)
        } else {
                resp, err = getSecretToken(keycloakUrl, clientId)
        }
@@ -121,14 +127,12 @@ func getJwtToken(keycloakUrl, clientId string) (*http.Response, error) {
 }
 
 func getClientAssertion() string {
-       //aud := "http://" + keycloakHost + ":" + keycloakPort + "/auth/realms/" + realmName
-       //aud := "http://keycloak/auth/realms/" + realmName
        aud := "https://keycloak:8443/realms/" + realmName
-       clientAssertion := generatejwt.CreateJWT("/certs/client.key", "", clientId, aud)
+       clientAssertion := generatejwt.CreateJWT(tlsKey, "", clientId, aud)
        return clientAssertion
 }
 
-func getx509Token(keycloakUrl, clientId string) (*http.Response, error) {
+func getx509Token(keycloakUrl, clientId, tlsCrt, tlsKey, caCrt string) (*http.Response, error) {
        var resp = &http.Response{}
        var err error
 
@@ -139,11 +143,11 @@ func getx509Token(keycloakUrl, clientId string) (*http.Response, error) {
 }
 
 func getClient() *http.Client {
-       caCert, _ := ioutil.ReadFile("/certs/rootCA.crt")
+       caCert, _ := ioutil.ReadFile(caCrt)
        caCertPool := x509.NewCertPool()
        caCertPool.AppendCertsFromPEM(caCert)
 
-       cert, _ := tls.LoadX509KeyPair("/certs/client.crt", "/certs/client.key")
+       cert, _ := tls.LoadX509KeyPair(tlsCrt, tlsKey)
 
        dialer := &net.Dialer{
                Timeout:   30 * time.Second,
index 6c72d87..6e90992 100644 (file)
@@ -117,13 +117,23 @@ var token Jwttoken
 var flowAlias string = "x509 direct grant"
 
 func createClient(res http.ResponseWriter, req *http.Request) {
-       query := req.URL.Query()
-       realmName := query.Get("realm")
-       clientName := query.Get("name")
-       role := query.Get("role")
-       authType := query.Get("authType")
+       body, err := ioutil.ReadAll(req.Body)
+       if err != nil {
+               panic(err.Error())
+       }
+       keyVal := make(map[string]string)
+       json.Unmarshal(body, &keyVal)
+       realmName := keyVal["realm"]
+       clientName := keyVal["name"]
+       role := keyVal["role"]
+       authType := keyVal["authType"]
+       tlsCrt := keyVal["tlsCrt"]
+       email := keyVal["email"]
+       subjectDN := keyVal["subjectDN"]
+       mappingSource := keyVal["mappingSource"]
+
        var msg string
-       msg, err := create(realmName, clientName, role, authType)
+       msg, err = create(realmName, clientName, role, authType, tlsCrt, email, subjectDN, mappingSource)
        if err != nil {
                msg = err.Error()
        }
@@ -197,7 +207,7 @@ func sendRequest(method, url string, data []byte) (int, string) {
        return resp.StatusCode, respString
 }
 
-func create(realmName, clientName, clientRoleName, authType string) (string, error) {
+func create(realmName, clientName, clientRoleName, authType, tlsCrt, email, subjectDN, mappingSource string) (string, error) {
        getAdminToken()
        var userId string = ""
        var jsonValue []byte = []byte{}
@@ -220,13 +230,13 @@ func create(realmName, clientName, clientRoleName, authType string) (string, err
        if authType == "client-x509" {
                flowId = getFlowId(realmName)
                if flowId == "" {
-                       createx509Flow(realmName)
+                       createx509Flow(realmName, mappingSource)
                        flowId = getFlowId(realmName)
                }
                newUser := User{
                        ID:       realmName + "user",
                        Username: realmName + "user",
-                       Email:    "client@mail.com",
+                       Email:    email,
                        Enabled:  true,
                }
                restUrl = keycloakUrl + "/admin/realms/" + realmName + "/users"
@@ -235,7 +245,7 @@ func create(realmName, clientName, clientRoleName, authType string) (string, err
                userId = getUserId(realmName, realmName+"user")
        }
 
-       newClient := getClient(authType, clientName, flowId)
+       newClient := getClient(authType, clientName, flowId, tlsCrt, subjectDN)
        restUrl = keycloakUrl + "/admin/realms/" + realmName + "/clients"
        jsonValue, _ = json.Marshal(newClient)
        statusCode, _ = sendRequest("POST", restUrl, jsonValue)
@@ -302,7 +312,7 @@ func create(realmName, clientName, clientRoleName, authType string) (string, err
        return clientSecret, nil
 }
 
-func getClient(authType, clientName, flowId string) Client {
+func getClient(authType, clientName, flowId, tlsCrt, subjectDN string) Client {
        var newClient Client
        newClient.ClientID = clientName
        newClient.Enabled = true
@@ -320,12 +330,12 @@ func getClient(authType, clientName, flowId string) Client {
                newClient.Attributes = map[string]string{
                        "use.refresh.tokens":                   "true",
                        "client_credentials.use_refresh_token": "true",
-                       "x509.subjectdn":                       ".*client@mail.com.*",
+                       "x509.subjectdn":                       ".*" + subjectDN + ".*",
                        "x509.allow.regex.pattern.comparison":  "true"}
                newClient.AuthenticationFlowBindingOverrides = map[string]string{
                        "direct_grant": flowId}
        } else {
-               jwksString, publicKey, kid := pemtojwks.CreateJWKS("/certs/client.crt")
+               jwksString, publicKey, kid := pemtojwks.CreateJWKS(tlsCrt)
                newClient.Attributes = map[string]string{
                        "token.endpoint.auth.signing.alg":      "RS256",
                        "jwt.credential.public.key":            publicKey,
@@ -353,7 +363,7 @@ func getClientInfo(realmName, clientName string) (string, string) {
        return clientId, clientSecret
 }
 
-func createx509Flow(realmName string) {
+func createx509Flow(realmName, mappingSource string) {
        var jsonValue []byte = []byte{}
        authenticationFlowRepresentation := AuthenticationFlowRepresentation{
                Alias:                   flowAlias,
@@ -397,7 +407,7 @@ func createx509Flow(realmName string) {
                        "x509-cert-auth.mapper-selection":               "Username or Email",
                        "x509-cert-auth.revalidate-certificate-enabled": "false",
                        "x509-cert-auth.crldp-checking-enabled":         "false",
-                       "x509-cert-auth.mapping-source-selection":       "Subject's e-mail",
+                       "x509-cert-auth.mapping-source-selection":       mappingSource,
                        "x509-cert-auth.ocsp-checking-enabled":          "false",
                },
        }
index 6774746..ffcc786 100644 (file)
@@ -1,6 +1,6 @@
 #
 # ============LICENSE_START=======================================================
-#  Copyright (C) 2022 Nordix Foundation.
+#  Copyright (C) 2022-2023 Nordix Foundation.
 # ================================================================================
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -24,6 +24,8 @@ metadata:
   namespace: default
   labels:
     app: rapps-keycloak-mgr
+    app.kubernetes.io/instance: rapps-keycloak-mgr
+    app.kubernetes.io/name: rapps-keycloak-mgr
 spec:
   selector:
     matchLabels:
@@ -33,6 +35,8 @@ spec:
       labels:
         app: rapps-keycloak-mgr
         version: v1
+        app.kubernetes.io/instance: rapps-keycloak-mgr
+        app.kubernetes.io/name: rapps-keycloak-mgr
     spec:
       containers:
       - name: rapps-keycloak-mgr
@@ -53,10 +57,8 @@ spec:
           readOnly: true
       volumes:
       - name: certsdir
-        hostPath:
-          # Ensure the file directory is created.
-           path: /var/rapps/certs
-           type: DirectoryOrCreate
+        secret:
+          secretName: cm-keycloak-client-certs
       serviceAccountName: helm-app
   replicas: 1
 ---
@@ -65,6 +67,10 @@ kind: Service
 metadata:
   name: rapps-keycloak-mgr
   namespace: default
+  labels:
+    app: rapps-keycloak-mgr
+    app.kubernetes.io/instance: rapps-keycloak-mgr
+    app.kubernetes.io/name: rapps-keycloak-mgr
 spec:
   selector:
     app: rapps-keycloak-mgr
index 242d622..69dcb3f 100644 (file)
@@ -1,22 +1,22 @@
 // -
-//   ========================LICENSE_START=================================
-//   O-RAN-SC
-//   %%
-//   Copyright (C) 2022: Nordix Foundation
-//   %%
-//   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
+//     ========================LICENSE_START=================================
+//     O-RAN-SC
+//     %%
+//     Copyright (C) 2022-2023: Nordix Foundation
+//     %%
+//     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
 //
-//   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===================================
+//          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===================================
 package main
 
 import (
@@ -34,10 +34,10 @@ import (
 )
 
 type ServerParameters struct {
-       port      string // webhook server port
-       certFile  string // path to the x509 cert
-       keyFile   string // path to the x509 private key
-       hostPath  string // path to the x509 private key
+       port     string // webhook server port
+       certFile string // path to the x509 cert
+       keyFile  string // path to the x509 private key
+       secret   string
 }
 
 type patchOperation struct {
@@ -56,7 +56,7 @@ func main() {
        flag.StringVar(&parameters.port, "port", "8443", "Webhook server port.")
        flag.StringVar(&parameters.certFile, "tlsCertFile", "/certs/tls.crt", "File containing the x509 certificate")
        flag.StringVar(&parameters.keyFile, "tlsKeyFile", "/certs/tls.key", "File containing the x509 private key")
-       flag.StringVar(&parameters.hostPath, "hostPath", "/var/rapps/certs", "Host Path containing rapp cert files")
+       flag.StringVar(&parameters.secret, "secret", "cm-keycloak-client-certs", "Secret containing rapp cert files")
        flag.Parse()
 
        http.HandleFunc("/inject-sidecar", HandleSideCarInjection)
@@ -137,16 +137,13 @@ func HandleSideCarInjection(w http.ResponseWriter, r *http.Request) {
                Value: containers,
        })
 
-       pathType := v1.HostPathDirectoryOrCreate
-       pathTypePtr := &pathType
        var volumes []v1.Volume
        volumes = append(volumes, pod.Spec.Volumes...)
        volume := v1.Volume{
                Name: "certsdir",
                VolumeSource: v1.VolumeSource{
-                       HostPath: &v1.HostPathVolumeSource{
-                               Path: parameters.hostPath,
-                               Type: pathTypePtr,
+                       Secret: &v1.SecretVolumeSource{
+                               SecretName: parameters.secret,
                        },
                },
        }
@@ -154,9 +151,9 @@ func HandleSideCarInjection(w http.ResponseWriter, r *http.Request) {
        fmt.Println(volumes)
 
        patches = append(patches, patchOperation{
-               Op:    "add",
-               Path:  "/spec/volumes",
-               Value: volumes,
+               Op:    "add",
+               Path:  "/spec/volumes",
+               Value: volumes,
        })
        fmt.Println(patches)
 
index d41ddf4..c04c8a9 100644 (file)
@@ -1,6 +1,6 @@
 #
 # ============LICENSE_START=======================================================
-#  Copyright (C) 2022 Nordix Foundation.
+#  Copyright (C) 2022-2023 Nordix Foundation.
 # ================================================================================
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -44,6 +44,8 @@ metadata:
   namespace: default
   labels:
     app: jwt-proxy-admission-controller
+    app.kubernetes.io/instance: jwt-proxy-admission-controller
+    app.kubernetes.io/name: jwt-proxy-admission-controller
 spec:
   selector:
     matchLabels:
@@ -53,6 +55,8 @@ spec:
       labels:
         app: jwt-proxy-admission-controller
         version: v1
+        app.kubernetes.io/instance: jwt-proxy-admission-controller
+        app.kubernetes.io/name: jwt-proxy-admission-controller
     spec:
       serviceAccountName: webhook-app
       containers:
@@ -64,7 +68,7 @@ spec:
                 "-port", "8443",
                 "-tlsCertFile", "/certs/tls.crt",
                 "-tlsKeyFile", "/certs/tls.key",
-                "-hostPath", "/var/rapps/certs"
+                "-secret", "cm-keycloak-client-certs",
               ]
         ports:
         - containerPort: 8443
@@ -82,7 +86,7 @@ spec:
       volumes:
         - name: webhook-cert
           secret:
-            secretName: webhook-cert
+            secretName: cm-webhook-server-certs
   replicas: 1
 ---
 apiVersion: v1
@@ -90,6 +94,10 @@ kind: Service
 metadata:
   name: jwt-proxy-admission-controller
   namespace: default
+  labels:
+    app: jwt-proxy-admission-controller
+    app.kubernetes.io/instance: jwt-proxy-admission-controller
+    app.kubernetes.io/name: jwt-proxy-admission-controller
 spec:
   selector:
     app: jwt-proxy-admission-controller
index f023fa6..7e91f5f 100644 (file)
@@ -1,31 +1,12 @@
-#
-# ============LICENSE_START=======================================================
-#  Copyright (C) 2022 Nordix Foundation.
-# ================================================================================
-# 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.
-#
-# SPDX-License-Identifier: Apache-2.0
-# ============LICENSE_END=========================================================
-#
 apiVersion: networking.istio.io/v1alpha3
 kind: EnvoyFilter
 metadata:
   name: {{.Name}}-outbound-filter
-  namespace: {{.Namespace}}
+  namespace: {{.Namespace}} 
 spec:
   workloadSelector:
     labels:
-      app.kubernetes.io/name: {{.Name}}
+      app.kubernetes.io/name: {{.Name}} 
   configPatches:
     # The first patch adds the lua filter to the listener/http connection manager
   - applyTo: HTTP_FILTER
@@ -59,6 +40,9 @@ spec:
                  ["realm"] = "{{.Realm}}",
                  ["client"] = "{{.Client}}",
                  ["authenticator"] = "{{.Authenticator}}",
+                 ["caCrt"] = "{{.CaCrt}}",
+                 ["tlsCrt"] = "{{.TlsCrt}}",
+                 ["tlsKey"] = "{{.TlsKey}}",
                  ["ns"] = "{{.Namespace}}"
                 },
                "jwt call",
diff --git a/service-exposure/webhook-server-certificate.yaml b/service-exposure/webhook-server-certificate.yaml
new file mode 100644 (file)
index 0000000..b9ed679
--- /dev/null
@@ -0,0 +1,57 @@
+#
+# ============LICENSE_START=======================================================
+#  Copyright (C) 2023 Nordix Foundation.
+# ================================================================================
+# 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.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=========================================================
+#
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+  name: webhook-server-cert
+  namespace: default
+spec:
+  secretName: cm-webhook-server-certs
+  duration: 2160h # 90d
+  renewBefore: 360h # 15d
+  subject:
+    organizations:
+      - oran
+    organizationalUnits:
+      - oran
+    countries:
+      - IE
+    localities:
+      - Dublin
+    streetAddresses:
+      - Main Street
+  commonName: webhook
+  isCA: false
+  privateKey:
+    algorithm: RSA
+    encoding: PKCS1
+    size: 2048
+  usages:
+    - server auth
+  dnsNames:
+    - jwt-proxy-admission-controller
+    - jwt-proxy-admission-controller.default.svc.cluster.local
+    - jwt-proxy-admission-controller.default.svc
+    - localhost
+    - 127.0.0.1
+  issuerRef:
+    name: cm-ca-issuer
+    kind: Issuer
+    group: cert-manager.io