From: ktimoney Date: Mon, 3 Oct 2022 14:02:44 +0000 (+0100) Subject: Add jwt-proxy functionality X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;h=2513eea5c9c4a1685ab6cbf0c2727d21399de5c7;p=nonrtric.git Add jwt-proxy functionality Issue-ID: NONRTRIC-634 Change-Id: Iedb39691a1dad212f9ad69b127287291496eaaf8 Signed-off-by: ktimoney --- diff --git a/service-exposure/Dockerfile_jwt b/service-exposure/Dockerfile_jwt new file mode 100644 index 00000000..104d3a85 --- /dev/null +++ b/service-exposure/Dockerfile_jwt @@ -0,0 +1,25 @@ +# +# ============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========================================================= +# +FROM golang:latest +RUN mkdir /app +COPY ./rapps-jwt /app +RUN chmod +x /app/rapps-jwt +WORKDIR /app +ENTRYPOINT ["/app/rapps-jwt"] diff --git a/service-exposure/Dockerfile_rhi b/service-exposure/Dockerfile_rhi index 6332b203..1194c35e 100644 --- a/service-exposure/Dockerfile_rhi +++ b/service-exposure/Dockerfile_rhi @@ -17,9 +17,9 @@ # SPDX-License-Identifier: Apache-2.0 # ============LICENSE_END========================================================= # -FROM golang:latest -RUN mkdir /app +FROM golang:latest +RUN mkdir /app COPY ./rapps-helm-installer /app RUN chmod +x /app/rapps-helm-installer -WORKDIR /app +WORKDIR /app ENTRYPOINT ["/app/rapps-helm-installer"] diff --git a/service-exposure/Dockerfile_rhwi1 b/service-exposure/Dockerfile_rhwi1 new file mode 100644 index 00000000..4b5413f5 --- /dev/null +++ b/service-exposure/Dockerfile_rhwi1 @@ -0,0 +1,25 @@ +# +# ============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========================================================= +# +FROM golang:latest +RUN mkdir /app +COPY ./rapps-rapp-helloworld-invoker1 /app +RUN chmod +x /app/rapps-rapp-helloworld-invoker1 +WORKDIR /app +ENTRYPOINT ["/app/rapps-rapp-helloworld-invoker1"] diff --git a/service-exposure/Dockerfile_rhwi2 b/service-exposure/Dockerfile_rhwi2 new file mode 100644 index 00000000..dd012434 --- /dev/null +++ b/service-exposure/Dockerfile_rhwi2 @@ -0,0 +1,25 @@ +# +# ============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========================================================= +# +FROM golang:latest +RUN mkdir /app +COPY ./rapps-rapp-helloworld-invoker2 /app +RUN chmod +x /app/rapps-rapp-helloworld-invoker2 +WORKDIR /app +ENTRYPOINT ["/app/rapps-rapp-helloworld-invoker2"] diff --git a/service-exposure/Dockerfile_rhwp b/service-exposure/Dockerfile_rhwp new file mode 100644 index 00000000..099315a0 --- /dev/null +++ b/service-exposure/Dockerfile_rhwp @@ -0,0 +1,25 @@ +# +# ============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========================================================= +# +FROM golang:latest +RUN mkdir /app +COPY ./rapps-rapp-helloworld-provider /app +RUN chmod +x /app/rapps-rapp-helloworld-provider +WORKDIR /app +ENTRYPOINT ["/app/rapps-rapp-helloworld-provider"] diff --git a/service-exposure/Dockerfile_rim b/service-exposure/Dockerfile_rim index f75d7fd1..dc4c26b8 100644 --- a/service-exposure/Dockerfile_rim +++ b/service-exposure/Dockerfile_rim @@ -17,9 +17,11 @@ # SPDX-License-Identifier: Apache-2.0 # ============LICENSE_END========================================================= # -FROM golang:latest -RUN mkdir /app +FROM golang:latest +RUN mkdir /app COPY ./rapps-istio-mgr /app +RUN mkdir /app/templates +ADD templates /app/templates RUN chmod +x /app/rapps-istio-mgr -WORKDIR /app +WORKDIR /app ENTRYPOINT ["/app/rapps-istio-mgr"] diff --git a/service-exposure/Dockerfile_rkm b/service-exposure/Dockerfile_rkm index cb4fc652..f1cd6bcf 100644 --- a/service-exposure/Dockerfile_rkm +++ b/service-exposure/Dockerfile_rkm @@ -17,9 +17,9 @@ # SPDX-License-Identifier: Apache-2.0 # ============LICENSE_END========================================================= # -FROM golang:latest -RUN mkdir /app +FROM golang:latest +RUN mkdir /app COPY ./rapps-keycloak-mgr /app RUN chmod +x /app/rapps-keycloak-mgr -WORKDIR /app +WORKDIR /app ENTRYPOINT ["/app/rapps-keycloak-mgr"] diff --git a/service-exposure/IstioOperator.yaml b/service-exposure/IstioOperator.yaml new file mode 100644 index 00000000..c2c14844 --- /dev/null +++ b/service-exposure/IstioOperator.yaml @@ -0,0 +1,82 @@ +# +# ============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: install.istio.io/v1alpha1 +kind: IstioOperator +spec: + profile: demo + meshConfig: + extensionProviders: + - name: "opa-default-grpc" + envoyExtAuthzGrpc: + service: "opa.default.svc.cluster.local" + port: "9191" + includeRequestBodyInCheck: + maxRequestBytes: 1000000 + - name: "opa-default-http" + envoyExtAuthzHttp: + service: "opa.default.svc.cluster.local" + port: "8181" + includeRequestHeadersInCheck: ["authorization", "path"] + includeRequestBodyInCheck: + maxRequestBytes: 1000000 + - name: "opa-local" + envoyExtAuthzGrpc: + service: "local-opa-grpc.local" + port: "9191" + accessLogEncoding: TEXT + accessLogFile: "/dev/stdout" + accessLogFormat: "" + outboundTrafficPolicy: + mode: REGISTRY_ONLY + values: + pilot: + jwksResolverExtraRootCA: | + -----BEGIN CERTIFICATE----- + MIIFdTCCA12gAwIBAgIUb2mMsNxZ3fpdLt0memNEwSs+yCUwDQYJKoZIhvcNAQEL + BQAwSjELMAkGA1UEBhMCSUUxDDAKBgNVBAsMA0VTVDERMA8GA1UEAwwIZXN0LnRl + Y2gxGjAYBgkqhkiG9w0BCQEWC2NhQG1haWwuY29tMB4XDTIyMDMyOTEyMjMxOVoX + DTMyMDMyNjEyMjMxOVowSjELMAkGA1UEBhMCSUUxDDAKBgNVBAsMA0VTVDERMA8G + A1UEAwwIZXN0LnRlY2gxGjAYBgkqhkiG9w0BCQEWC2NhQG1haWwuY29tMIICIjAN + BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAy2E9PlJyZBh64gxnqrhB7ELS59TK + mKRZEgyWYCyS54sOsVLt7HwrzeHxMqhccyEB3+S0jqgVFe7RQ2dEIqc9H1upG2TH + Cznz7+epYXFj7wRfQAXM53mEJYIVPcjJ31iFBHKURC6l/ZBLENNG+mXBN9cO7nMe + b99w8Sc5jVMy9VmKDZMzJildtWhyEGEDq4C69TAJq8zfvPExkOZW9iSg25FaCoip + IO19EYVxl6BYnjgKr48s1XyREBUnOkw6IeVLzD/2co5UpJd40yolXAG8eDxxSGzT + EjyVMR3tph86FQ8H053lYB5Y3u6iwCdALf9TvUpEv+ZL4BcB+I4U0RdtLQGL2iuv + 9NLeqVAfmtXC3st+DgukxvJA3+iGDGyssvY3EF3eCB9QnjjbDwvZ4raG4DIcBNQ3 + FfpfpoSswXI4KU2JXgS/V28Az46NIFwwT3WvwhFT5aCUcInNPAF2vDSUfDvlHl39 + BSSKAqsPnvJIDTnlmJoSo28uca2SkSkXL2N43vGOPV4/UYRIz+bqSFNfu48nfe1I + E83PKTCTDum+iOscteF1xMU3KrWLpdkBzPW1PfVK6OcAgbKZvfBGNdNOmygfMj5t + Slw0bc2Gpd1ISJyQK0L2DVOSMeB6+PyDdJEYUVe+Xh2uqnaGJnAS90//X/FiOJrJ + Y5GrgeVLAkDyOjcCAwEAAaNTMFEwHQYDVR0OBBYEFHoCuHWgHsN1cS7TRuJgk1Yv + deY7MB8GA1UdIwQYMBaAFHoCuHWgHsN1cS7TRuJgk1YvdeY7MA8GA1UdEwEB/wQF + MAMBAf8wDQYJKoZIhvcNAQELBQADggIBALp0D+Sw09OxZhq8CGw/fQn+AScY9JSE + E/4C+jVwSVygi7BKcJfqy8aq7cGe+O9sAEnmxDrle1oECVIXX+mhhS7cD5kRdOsb + WAjJBqi+B6YgNuawLfQldnHJV/opjb0FBytaGpEMWYsAj0xcoVe4Nj/x7myQ4qoD + Y8r8wEFriOwTk+0dICg40I2EUeq5qoJ7Q5bbdYPfe8EhJAkN4u7xJ6P6GDY6Zvoo + JpYSSAaKLZb9yd4SxAoDvyuEZL6YNX8vgfPEZqVi2lm5uDkeE+xqWhL2j0ECKXPN + PLQMFBCaVPO9RueiwV/P/l0DuChY7dSAHn9kqdS6PlSGe411OGTpxz5laD9Ho4a9 + UOAurbtu76wAPnsxszAxMAGqEXvZgcX+zUBm4uGPpLUu5vIiWgE/DpwmIpT5jwDu + EV0e7C43q3kT5ieqzxDb3gvUWdQZ4Qg6qa8js7KfKH7L0ToCtZACnpdVXjxE1Mp6 + aCKAPPo8AJm2YdS0Zyj1w8ZN6tDStZ6sfFyEkcRiLOF0pL0qJKw/aqgZd0cHCZed + z9p+zpuSbJgnEqax0G7fF5hGofUuCIz4F8CNiehjpZDrCHqPrbCsUveu4iP+cw2N + /DZsEJUr0qL+QsAll2L6Zm8z1bAGxomxfFqUAHPep+msFyKT6W2SXz3MzTClq1JK + CruKkw029sEv + -----END CERTIFICATE----- diff --git a/service-exposure/MutatingWebhookConfiguration.yaml b/service-exposure/MutatingWebhookConfiguration.yaml new file mode 100644 index 00000000..b0947aae --- /dev/null +++ b/service-exposure/MutatingWebhookConfiguration.yaml @@ -0,0 +1,44 @@ +# +# ============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: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: jwt-proxy-webhook + namespace: default +webhooks: + - name: rapps-webhook.default.svc.cluster.local + admissionReviewVersions: + - "v1beta1" + sideEffects: "None" + timeoutSeconds: 30 + objectSelector: + matchLabels: + app.kubernetes.io/name: rapp-helloworld-invoker1 + clientConfig: + service: + name: jwt-proxy-admission-controller + namespace: default + path: "/inject-sidecar" + caBundle: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURhakNDQWxLZ0F3SUJBZ0lVTlZFcUZwSUJMUEZUOGd2L3hQK245L2ZvTy80d0RRWUpLb1pJaHZjTkFRRUwKQlFBd1RURUxNQWtHQTFVRUJoTUNTVVV4RURBT0JnTlZCQWdUQjFkbFltaHZiMnN4RHpBTkJnTlZCQWNUQmtSMQpZbXhwYmpFTU1Bb0dBMVVFQ2hNRFJWTlVNUTB3Q3dZRFZRUUxFd1JQY21GdU1CNFhEVEl5TURreU1EQTRNRGN3Ck1Gb1hEVEkzTURreE9UQTRNRGN3TUZvd1RURUxNQWtHQTFVRUJoTUNTVVV4RURBT0JnTlZCQWdUQjFkbFltaHYKYjJzeER6QU5CZ05WQkFjVEJrUjFZbXhwYmpFTU1Bb0dBMVVFQ2hNRFJWTlVNUTB3Q3dZRFZRUUxFd1JQY21GdQpNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXArM3lhc2VHUVpOS1VCakVJcFF6CktUZFI5bEVFTDhGeitGRGIrM0YwalQ2cWtoMko5cmJYdUx0V0dWZm5RQWpXb1JpaHlUV3F3RlR6V2lMNHVtK0gKNzVYSm1ucHlqbkRJRStaUGZpcFR0SW40cHhSZ3Z3WXl5a0pjeGI4blN3a21IM2NVRkVLMHJnbEEvLzVMV0RuWgpIQkl2VkhFUzlGYktwVFBEZFlNRFJ2K3dNNGk1ZWVOM1djOCtnZ0hYZW1tc3pkRG9mc0dMTU1iNkpXQlY0MEs0ClhHRDdheEYwelBIT3RHblhlU21zL1lVTlB4R3Z5WWpmZHJqSW1kL2xKUCtDQysvMlhuaEZYYUYzSzJxbE9uQU4KUGVoOGRPNzdNZzVjU01JQkhwbll1RTVqUy95YmZ0RGRSWDcxRm9ZUmJ0MXZsOXNuVC9zNFhxTDB2bHBCRmVyMApSd0lEQVFBQm8wSXdRREFPQmdOVkhROEJBZjhFQkFNQ0FRWXdEd1lEVlIwVEFRSC9CQVV3QXdFQi96QWRCZ05WCkhRNEVGZ1FVcDQyM3B4NnUxbTYwZnhCNEJWYmFWR2gxaGwwd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFHaHYKc25jc1g0dUl2S1lZRUdCNitEYmNmdlViS1o2clQ1Ykx3OUV1aFpDSUJiS0xTOFRLMHFqV2dyM0JZWUsyRFA2UgpTcmhzOHRSbkQ2VCtPL1dMdWpPOXM4SUpBbGQvRkQzenJyZWs1YW16RndQb1JiWVZ6OXY4SG1HblRRY2JZWEFYCmlzcjg5Z1QzRFRLbkRxTHEyUTU2WnBiN2dLbFZWNXZKNjVaVFRzYUwxc2oxK1d0bDB0emcrektMNHdrckRqK0wKRzd4blYrNDY3eEUwSnora3JOaFYzaHJEYmhpOUVsRVNRTnVHeURsTUVuY2dvSEFqMmh4WnVINEVUTXJyYWxSWgphQTI3c1ZDNGlMYmJsQWZ0THRWb1YycGpVdTdDVWwrQ1pOZ2tFRGl1d05weWQvZzdlVmYvVk0vakt5TTFiODQ4Ck5nYlZmcjNhelFlOUIyc3kvQnc9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K" + rules: + - operations: [ "CREATE" ] + apiGroups: [""] + apiVersions: ["v1"] + resources: ["pods"] diff --git a/service-exposure/README.md b/service-exposure/README.md index 00da4d03..c6cdac35 100644 --- a/service-exposure/README.md +++ b/service-exposure/README.md @@ -1,6 +1,28 @@ +# +# ============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========================================================= +# This collection of files represent rapp service exposure prototyping in O-RAN. -Prerequisites: Istio should be installed on your cluster with the demo profile (istioctl install --set profile=demo). Please refer to the istio documentation for more information. +Prerequisites: Istio should be installed on your cluster with the demo profile. + istioctl install --set profile=demo +Please refer to the istio documentation for more information. 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. 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 @@ -12,30 +34,42 @@ rapps-keycloak-mgr.yaml: path: /var/rapps/certs or change them to match your own setup. +The certs directory contains 2 shell scripts for creating the server and client certs: server_certs.sh and client_certs.sh +Certs generated by the server_certs.sh script: rootCA.crt, tls.crt and tls.key go in the "/var/keycloak/certs" directory +Certs generated by the client_certs.sh script: client.crt, client.key, client_pub.key and rootCA.crt go in the "/var/rapps/certs" directory + Create the istio-nonrtric namespace and enable it for istio injection - kubectl create ns istio-nonrtric + kubectl create ns istio-nonrtric kubectl label namespace istio-nonrtric istio-injection=enabled All go programs need to be built prior to running the Dockerfiles - go build rapps-helm-installer.go - go build rapps-keycloak-mgr.go - go build rapps-istio-mgr.go + go build rapps-helm-installer.go + go build rapps-keycloak-mgr.go + go build rapps-istio-mgr.go go build rapps-rapp-provider.go go build rapps-rapp-invoker.go + go build rapps-webhook.go + go build rapps-jwt.go + go build rapps-rapp-helloworld-provider.go + go build rapps-rapp-helloworld-invoker1.go + go build rapps-rapp-helloworld-invoker2.go Once the go programs have been compile you then need to build a docker image for each of them. docker build -f Dockerfile_rim . -t /rapps-istio-mgr docker build -f Dockerfile_rkm . -t /rapps-keycloak-mgr docker build -f Dockerfile_rhi . -t /rapps-helm-installer - docker build -f Dockerfile_rri . -t /rapps-rapp-invoker - docker build -f Dockerfile_rrp . -t /rapps-rapp-provider + docker build -f Dockerfile_wh . -t /rapps-webhook + docker build -f Dockerfile_jwt . -t /rapps-jwt + docker build -f Dockerfile_rhwp . -t /rapps-rapp-helloworld-provider + docker build -f Dockerfile_rhwi1 . -t /rapps-rapp-helloworld-invoker1 + docker build -f Dockerfile_rhwi2 . -t /rapps-rapp-helloworld-invoker2 -Image references in the yaml files/helm charts should be changed to match your own tagged images. +Image references in the yaml files/helm charts should be changed to match your own tagged images. You will need to package your rapp charts and copy them to the /var/chartmuseum/charts directory before starting. @@ -51,20 +85,21 @@ Start keycloak and postgres in the default namespace with istio injection: istioctl kube-inject -f postgres.yaml | kubectl apply -f - istioctl kube-inject -f keycloak.yaml | kubectl apply -f - +or use the keycloak.sh deploy script -To start the management pods run: +To start the management pods run: start_pods.sh -Run: - kubectl get pods to ensure all managements pods are up and running - NAME READY STATUS RESTARTS AGE - chartmuseum-deployment-7b8cd4c9d4-tpmhl 1/1 Running 0 8s - keycloak-bc6f78f88-zmxlt 2/2 Running 0 2m20s - postgres-6fb4cc8db6-bbhg9 2/2 Running 0 2m34s - rapps-helm-installer-deployment-67476694-sxb2d 1/1 Running 0 6s - rapps-istio-mgr-deployment-67c67647b6-scmqc 1/1 Running 0 7s - rapps-keycloak-mgr-deployment-7464f87575-trvmx 1/1 Running 0 7s +Once all pods have been started a list of running pods is displayed at the end of the script: +NAME READY STATUS RESTARTS AGE +chartmuseum-deployment-7b8cd4c9d4-nd7dk 1/1 Running 0 9s +jwt-proxy-admission-controller-deployment-66797fb6df-mlk8t 1/1 Running 0 8s +keycloak-846ff979bc-ndvdf 2/2 Running 0 2m16s +postgres-78b4b9d95-nqjkj 2/2 Running 0 2m29s +rapps-helm-installer-deployment-67476694-n5r24 1/1 Running 0 8s +rapps-istio-mgr-deployment-67c67647b6-p5s2k 1/1 Running 0 8s +rapps-keycloak-mgr-deployment-7464f87575-54h9x 1/1 Running 0 8s Get the node port for the helm installer that corresponds to port 80 @@ -72,13 +107,17 @@ Get the node port for the helm installer that corresponds to port 80 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE rapps-helm-installer NodePort 10.96.58.211 80:31570/TCP 8m9s -Once these pods are up and running run: - curl http://:/install?chart= +Once these pods are up and running run: + curl http://:/install?chart= to install your rapp - e.g. curl http://192.168.49.2:31570/install?chart=rapp-provider + e.g. curl http://192.168.49.2:31570/install?chart=rapp-hello-world-provider Successfully installed release: rapp-provider + Alternativley use the deploy_rapp.sh script + e.g. ./deploy_rapp.sh rapp-helloworld-provider + Note: The line export host= should be changed to the appropaite ip for the host you are running on. + This will setup keycloak realm + client, istio policies and deploy your chart. You should install both the provider and the invoker to see the pods communicating. @@ -91,14 +130,19 @@ Check the invoker logs to see the test message: If you want to test using the rp_test.sh file, the client_secret field needs be changed to match the secret for you keycloak client. You can find this in the keycloak-mgr log. -To uninstall run: +To uninstall run: curl http://:/uninstall?chart= e.g. curl http://192.168.49.2:31570/uninstall?chart=rapp-invoker Successfully uninstalled release: rapp-invoker -To stop the management pods run: + Alternativley use the undeploy_rapp.sh script + e.g. ./undeploy_rapp.sh rapp-helloworld-provider + +To stop the management pods and provider/invoker pods at the same time run: stop_pods.sh Remove postgres and keycloak with the following commands: kubectl delete -f keycloak.yaml kubectl delete -f postgres.yaml + + or use ./keycloak.sh undeploy diff --git a/service-exposure/chartmuseum.yaml b/service-exposure/chartmuseum.yaml index 30aa4e60..987075d7 100644 --- a/service-exposure/chartmuseum.yaml +++ b/service-exposure/chartmuseum.yaml @@ -23,26 +23,26 @@ metadata: name: chartmuseum-deployment namespace: default labels: - app: chartmuseum + app: chartmuseum spec: selector: matchLabels: - app: chartmuseum + app: chartmuseum template: metadata: labels: - app: chartmuseum + app: chartmuseum version: v1 spec: containers: - - name: chartmuseum - image: chartmuseum/chartmuseum:latest + - name: chartmuseum + image: chartmuseum/chartmuseum:latest imagePullPolicy: IfNotPresent env: - - name: STORAGE - value: local - - name: STORAGE_LOCAL_ROOTDIR - value: /charts + - name: STORAGE + value: local + - name: STORAGE_LOCAL_ROOTDIR + value: /charts ports: - name: http containerPort: 8080 @@ -58,24 +58,24 @@ spec: mountPath: /charts readOnly: true volumes: - - name: chartdir + - name: chartdir hostPath: # Ensure the file directory is created. - path: /var/chartmuseum/charts + path: /var/chartmuseum/charts type: DirectoryOrCreate - replicas: 1 + replicas: 1 --- apiVersion: v1 kind: Service metadata: - name: chartmuseum + name: chartmuseum namespace: default spec: selector: - app: chartmuseum + app: chartmuseum ports: - name: http port: 8080 targetPort: 8080 - nodePort: 31580 - type: LoadBalancer + nodePort: 31581 + type: NodePort diff --git a/service-exposure/charts/rapp-helloworld-invoker1/.helmignore b/service-exposure/charts/rapp-helloworld-invoker1/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker1/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/service-exposure/charts/rapp-helloworld-invoker1/Chart.yaml b/service-exposure/charts/rapp-helloworld-invoker1/Chart.yaml new file mode 100644 index 00000000..400203d8 --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker1/Chart.yaml @@ -0,0 +1,43 @@ +# +# ============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: v2 +name: rapp-helloworld-invoker1 +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/service-exposure/charts/rapp-helloworld-invoker1/templates/NOTES.txt b/service-exposure/charts/rapp-helloworld-invoker1/templates/NOTES.txt new file mode 100644 index 00000000..939a0746 --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker1/templates/NOTES.txt @@ -0,0 +1,43 @@ +{{/* +# +# ============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========================================================= +# +*/}} +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "rapp-helloworld-invoker1.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "rapp-helloworld-invoker1.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "rapp-helloworld-invoker1.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "rapp-helloworld-invoker1.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/service-exposure/charts/rapp-helloworld-invoker1/templates/_helpers.tpl b/service-exposure/charts/rapp-helloworld-invoker1/templates/_helpers.tpl new file mode 100644 index 00000000..fbac7d30 --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker1/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "rapp-helloworld-invoker1.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "rapp-helloworld-invoker1.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "rapp-helloworld-invoker1.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "rapp-helloworld-invoker1.labels" -}} +helm.sh/chart: {{ include "rapp-helloworld-invoker1.chart" . }} +{{ include "rapp-helloworld-invoker1.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "rapp-helloworld-invoker1.selectorLabels" -}} +app.kubernetes.io/name: {{ include "rapp-helloworld-invoker1.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "rapp-helloworld-invoker1.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "rapp-helloworld-invoker1.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/service-exposure/charts/rapp-helloworld-invoker1/templates/clusterrole.yaml b/service-exposure/charts/rapp-helloworld-invoker1/templates/clusterrole.yaml new file mode 100644 index 00000000..f713ac9e --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker1/templates/clusterrole.yaml @@ -0,0 +1,41 @@ +{{/* +# +# ============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========================================================= +# +*/}} +{{- if .Values.rbac.create -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app: {{ template "rapp-helloworld-invoker1.name" .}} + chart: {{ .Chart.Name }}-{{ .Chart.Version }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + name: {{ template "rapp-helloworld-invoker1.fullname" . }} + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: +- kind: ServiceAccount + name: {{ template "rapp-helloworld-invoker1.fullname" . }} + namespace: {{ .Release.Namespace }} +{{- end -}} diff --git a/service-exposure/charts/rapp-helloworld-invoker1/templates/deployment.yaml b/service-exposure/charts/rapp-helloworld-invoker1/templates/deployment.yaml new file mode 100644 index 00000000..d989b0ec --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker1/templates/deployment.yaml @@ -0,0 +1,91 @@ +{{/* +# +# ============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: apps/v1 +kind: Deployment +metadata: + name: {{ include "rapp-helloworld-invoker1.fullname" . }} + labels: + {{- include "rapp-helloworld-invoker1.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "rapp-helloworld-invoker1.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "rapp-helloworld-invoker1.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "rapp-helloworld-invoker1.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: ["/app/rapps-rapp-helloworld-invoker1"] + args: [ + "-securityEnabled", "{{ .Values.rapp.securityEnabled }}", + "-rapp", "{{ with index .Values.rapp.apps 0 }}{{ .prefix }}{{ end }}", + "-methods", "{{- range .Values.rapp.apps }}{{ join "," .methods }}{{- end }}" + ] + ports: + - name: http + containerPort: 9000 + protocol: TCP + livenessProbe: + httpGet: + path: /health + port: 9000 + initialDelaySeconds: 5 + periodSeconds: 60 + readinessProbe: + initialDelaySeconds: 5 + periodSeconds: 10 + exec: + command: ["stat", "init.txt"] + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/service-exposure/charts/rapp-helloworld-invoker1/templates/hpa.yaml b/service-exposure/charts/rapp-helloworld-invoker1/templates/hpa.yaml new file mode 100644 index 00000000..5754224d --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker1/templates/hpa.yaml @@ -0,0 +1,49 @@ +{{/* +# +# ============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========================================================= +# +*/}} +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "rapp-helloworld-invoker1.fullname" . }} + labels: + {{- include "rapp-helloworld-invoker1.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "rapp-helloworld-invoker1.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/service-exposure/charts/rapp-helloworld-invoker1/templates/ingress.yaml b/service-exposure/charts/rapp-helloworld-invoker1/templates/ingress.yaml new file mode 100644 index 00000000..141e67b1 --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker1/templates/ingress.yaml @@ -0,0 +1,62 @@ +{{/* +# +# ============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========================================================= +# +*/}} +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "rapp-helloworld-invoker1.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "rapp-helloworld-invoker1.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + backend: + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} diff --git a/service-exposure/charts/rapp-helloworld-invoker1/templates/service.yaml b/service-exposure/charts/rapp-helloworld-invoker1/templates/service.yaml new file mode 100644 index 00000000..d2df6eb3 --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker1/templates/service.yaml @@ -0,0 +1,36 @@ +{{/* +# +# ============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: v1 +kind: Service +metadata: + name: {{ include "rapp-helloworld-invoker1.fullname" . }} + labels: + {{- include "rapp-helloworld-invoker1.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "rapp-helloworld-invoker1.selectorLabels" . | nindent 4 }} diff --git a/service-exposure/charts/rapp-helloworld-invoker1/templates/serviceaccount.yaml b/service-exposure/charts/rapp-helloworld-invoker1/templates/serviceaccount.yaml new file mode 100644 index 00000000..40d1cc4f --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker1/templates/serviceaccount.yaml @@ -0,0 +1,33 @@ +{{/* +# +# ============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========================================================= +# +*/}} +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "rapp-helloworld-invoker1.serviceAccountName" . }} + labels: + {{- include "rapp-helloworld-invoker1.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/service-exposure/charts/rapp-helloworld-invoker1/templates/tests/test-connection.yaml b/service-exposure/charts/rapp-helloworld-invoker1/templates/tests/test-connection.yaml new file mode 100644 index 00000000..ed38ea7b --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker1/templates/tests/test-connection.yaml @@ -0,0 +1,36 @@ +{{/* +# +# ============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: v1 +kind: Pod +metadata: + name: "{{ include "rapp-helloworld-invoker1.fullname" . }}-test-connection" + labels: + {{- include "rapp-helloworld-invoker1.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "rapp-helloworld-invoker1.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/service-exposure/charts/rapp-helloworld-invoker1/values.yaml b/service-exposure/charts/rapp-helloworld-invoker1/values.yaml new file mode 100644 index 00000000..8e89cff4 --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker1/values.yaml @@ -0,0 +1,123 @@ +# +# ============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========================================================= +# + +# Default values for rapp-helloworld-invoker1. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: ktimoney/rapps-rapp-helloworld-invoker1 + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "latest" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "rapp-helloworld-invoker1" + +rbac: + # Specifies whether rbac is enabled + create: true + +podAnnotations: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: false + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: rapp-helloworld-invoker1 + paths: + - path: / + backend: + serviceName: rapp-helloworld-invoker1 + servicePort: 80 + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +nodeSelector: {} + +tolerations: [] + +affinity: {} + + +rapp: + securityEnabled: true + type: invoker + realm: demo + client: demoprovider-cli + authenticator: client-jwt + roles: + - role : provider-viewer + grants: + - GET + apps: + - prefix: rapp-helloworld-provider + methods: + - GET diff --git a/service-exposure/charts/rapp-helloworld-invoker2/.helmignore b/service-exposure/charts/rapp-helloworld-invoker2/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker2/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/service-exposure/charts/rapp-helloworld-invoker2/Chart.yaml b/service-exposure/charts/rapp-helloworld-invoker2/Chart.yaml new file mode 100644 index 00000000..3a7db7a0 --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker2/Chart.yaml @@ -0,0 +1,44 @@ +# +# ============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: v2 +name: rapp-helloworld-invoker2 +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/service-exposure/charts/rapp-helloworld-invoker2/templates/NOTES.txt b/service-exposure/charts/rapp-helloworld-invoker2/templates/NOTES.txt new file mode 100644 index 00000000..9e38caae --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker2/templates/NOTES.txt @@ -0,0 +1,43 @@ +{{/* +# +# ============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========================================================= +# +*/}} +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "rapp-helloworld-invoker2.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "rapp-helloworld-invoker2.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "rapp-helloworld-invoker2.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "rapp-helloworld-invoker2.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/service-exposure/charts/rapp-helloworld-invoker2/templates/_helpers.tpl b/service-exposure/charts/rapp-helloworld-invoker2/templates/_helpers.tpl new file mode 100644 index 00000000..0999f450 --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker2/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "rapp-helloworld-invoker2.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "rapp-helloworld-invoker2.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "rapp-helloworld-invoker2.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "rapp-helloworld-invoker2.labels" -}} +helm.sh/chart: {{ include "rapp-helloworld-invoker2.chart" . }} +{{ include "rapp-helloworld-invoker2.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "rapp-helloworld-invoker2.selectorLabels" -}} +app.kubernetes.io/name: {{ include "rapp-helloworld-invoker2.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "rapp-helloworld-invoker2.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "rapp-helloworld-invoker2.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/service-exposure/charts/rapp-helloworld-invoker2/templates/clusterrole.yaml b/service-exposure/charts/rapp-helloworld-invoker2/templates/clusterrole.yaml new file mode 100644 index 00000000..55cb3992 --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker2/templates/clusterrole.yaml @@ -0,0 +1,41 @@ +{{/* +# +# ============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========================================================= +# +*/}} +{{- if .Values.rbac.create -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app: {{ template "rapp-helloworld-invoker2.name" .}} + chart: {{ .Chart.Name }}-{{ .Chart.Version }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + name: {{ template "rapp-helloworld-invoker2.fullname" . }} + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: +- kind: ServiceAccount + name: {{ template "rapp-helloworld-invoker2.fullname" . }} + namespace: {{ .Release.Namespace }} +{{- end -}} diff --git a/service-exposure/charts/rapp-helloworld-invoker2/templates/deployment.yaml b/service-exposure/charts/rapp-helloworld-invoker2/templates/deployment.yaml new file mode 100644 index 00000000..33337210 --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker2/templates/deployment.yaml @@ -0,0 +1,91 @@ +{{/* +# +# ============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: apps/v1 +kind: Deployment +metadata: + name: {{ include "rapp-helloworld-invoker2.fullname" . }} + labels: + {{- include "rapp-helloworld-invoker2.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "rapp-helloworld-invoker2.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "rapp-helloworld-invoker2.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "rapp-helloworld-invoker2.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: ["/app/rapps-rapp-helloworld-invoker2"] + args: [ + "-securityEnabled", "{{ .Values.rapp.securityEnabled }}", + "-rapp", "{{ with index .Values.rapp.apps 0 }}{{ .prefix }}{{ end }}", + "-methods", "{{- range .Values.rapp.apps }}{{ join "," .methods }}{{- end }}" + ] + ports: + - name: http + containerPort: 9000 + protocol: TCP + livenessProbe: + httpGet: + path: /health + port: 9000 + initialDelaySeconds: 5 + periodSeconds: 60 + readinessProbe: + initialDelaySeconds: 5 + periodSeconds: 10 + exec: + command: ["stat", "init.txt"] + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/service-exposure/charts/rapp-helloworld-invoker2/templates/hpa.yaml b/service-exposure/charts/rapp-helloworld-invoker2/templates/hpa.yaml new file mode 100644 index 00000000..2fcc1a81 --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker2/templates/hpa.yaml @@ -0,0 +1,49 @@ +{{/* +# +# ============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========================================================= +# +*/}} +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "rapp-helloworld-invoker2.fullname" . }} + labels: + {{- include "rapp-helloworld-invoker2.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "rapp-helloworld-invoker2.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/service-exposure/charts/rapp-helloworld-invoker2/templates/ingress.yaml b/service-exposure/charts/rapp-helloworld-invoker2/templates/ingress.yaml new file mode 100644 index 00000000..cfd3538b --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker2/templates/ingress.yaml @@ -0,0 +1,62 @@ +{{/* +# +# ============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========================================================= +# +*/}} +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "rapp-helloworld-invoker2.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "rapp-helloworld-invoker2.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + backend: + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} diff --git a/service-exposure/charts/rapp-helloworld-invoker2/templates/service.yaml b/service-exposure/charts/rapp-helloworld-invoker2/templates/service.yaml new file mode 100644 index 00000000..cadc2001 --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker2/templates/service.yaml @@ -0,0 +1,36 @@ +{{/* +# +# ============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: v1 +kind: Service +metadata: + name: {{ include "rapp-helloworld-invoker2.fullname" . }} + labels: + {{- include "rapp-helloworld-invoker2.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "rapp-helloworld-invoker2.selectorLabels" . | nindent 4 }} diff --git a/service-exposure/charts/rapp-helloworld-invoker2/templates/serviceaccount.yaml b/service-exposure/charts/rapp-helloworld-invoker2/templates/serviceaccount.yaml new file mode 100644 index 00000000..a92848e0 --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker2/templates/serviceaccount.yaml @@ -0,0 +1,33 @@ +{{/* +# +# ============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========================================================= +# +*/}} +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "rapp-helloworld-invoker2.serviceAccountName" . }} + labels: + {{- include "rapp-helloworld-invoker2.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/service-exposure/charts/rapp-helloworld-invoker2/templates/tests/test-connection.yaml b/service-exposure/charts/rapp-helloworld-invoker2/templates/tests/test-connection.yaml new file mode 100644 index 00000000..1f2e296b --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker2/templates/tests/test-connection.yaml @@ -0,0 +1,36 @@ +{{/* +# +# ============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: v1 +kind: Pod +metadata: + name: "{{ include "rapp-helloworld-invoker2.fullname" . }}-test-connection" + labels: + {{- include "rapp-helloworld-invoker2.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "rapp-helloworld-invoker2.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/service-exposure/charts/rapp-helloworld-invoker2/values.yaml b/service-exposure/charts/rapp-helloworld-invoker2/values.yaml new file mode 100644 index 00000000..bd6f5aba --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-invoker2/values.yaml @@ -0,0 +1,123 @@ +# +# ============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========================================================= +# + +# Default values for rapp-helloworld-invoker2. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: ktimoney/rapps-rapp-helloworld-invoker2 + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "latest" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "rapp-helloworld-invoker2" + +rbac: + # Specifies whether rbac is enabled + create: true + +podAnnotations: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: false + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: rapp-helloworld-invoker2 + paths: + - path: / + backend: + serviceName: rapp-helloworld-invoker2 + servicePort: 80 + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +nodeSelector: {} + +tolerations: [] + +affinity: {} + + +rapp: + securityEnabled: true + type: invoker + realm: demo + client: demoprovider-cli + authenticator: client-jwt + roles: + - role : provider-viewer + grants: + - GET + apps: + - prefix: rapp-helloworld-provider + methods: + - GET diff --git a/service-exposure/charts/rapp-helloworld-provider/.helmignore b/service-exposure/charts/rapp-helloworld-provider/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-provider/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/service-exposure/charts/rapp-helloworld-provider/Chart.yaml b/service-exposure/charts/rapp-helloworld-provider/Chart.yaml new file mode 100644 index 00000000..2978a4c3 --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-provider/Chart.yaml @@ -0,0 +1,44 @@ +# +# ============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: v2 +name: rapp-helloworld-provider +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/service-exposure/charts/rapp-helloworld-provider/templates/NOTES.txt b/service-exposure/charts/rapp-helloworld-provider/templates/NOTES.txt new file mode 100644 index 00000000..a4ad9132 --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-provider/templates/NOTES.txt @@ -0,0 +1,43 @@ +{{/* +# +# ============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========================================================= +# +*/}} +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "rapp-helloworld-provider.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "rapp-helloworld-provider.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "rapp-helloworld-provider.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "rapp-helloworld-provider.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/service-exposure/charts/rapp-helloworld-provider/templates/_helpers.tpl b/service-exposure/charts/rapp-helloworld-provider/templates/_helpers.tpl new file mode 100644 index 00000000..08fd72ba --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-provider/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "rapp-helloworld-provider.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "rapp-helloworld-provider.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "rapp-helloworld-provider.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "rapp-helloworld-provider.labels" -}} +helm.sh/chart: {{ include "rapp-helloworld-provider.chart" . }} +{{ include "rapp-helloworld-provider.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "rapp-helloworld-provider.selectorLabels" -}} +app.kubernetes.io/name: {{ include "rapp-helloworld-provider.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "rapp-helloworld-provider.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "rapp-helloworld-provider.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/service-exposure/charts/rapp-helloworld-provider/templates/clusterrole.yaml b/service-exposure/charts/rapp-helloworld-provider/templates/clusterrole.yaml new file mode 100644 index 00000000..7e78328e --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-provider/templates/clusterrole.yaml @@ -0,0 +1,41 @@ +{{/* +# +# ============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========================================================= +# +*/}} +{{- if .Values.rbac.create -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app: {{ template "rapp-helloworld-provider.name" .}} + chart: {{ .Chart.Name }}-{{ .Chart.Version }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + name: {{ template "rapp-helloworld-provider.fullname" . }} + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: +- kind: ServiceAccount + name: {{ template "rapp-helloworld-provider.fullname" . }} + namespace: {{ .Release.Namespace }} +{{- end -}} diff --git a/service-exposure/charts/rapp-helloworld-provider/templates/deployment.yaml b/service-exposure/charts/rapp-helloworld-provider/templates/deployment.yaml new file mode 100644 index 00000000..6ca73481 --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-provider/templates/deployment.yaml @@ -0,0 +1,82 @@ +{{/* +# +# ============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: apps/v1 +kind: Deployment +metadata: + name: {{ include "rapp-helloworld-provider.fullname" . }} + labels: + {{- include "rapp-helloworld-provider.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "rapp-helloworld-provider.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "rapp-helloworld-provider.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "rapp-helloworld-provider.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: 9000 + protocol: TCP + livenessProbe: + httpGet: + path: / + port: http + readinessProbe: + httpGet: + path: / + port: http + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/service-exposure/charts/rapp-helloworld-provider/templates/hpa.yaml b/service-exposure/charts/rapp-helloworld-provider/templates/hpa.yaml new file mode 100644 index 00000000..3a696089 --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-provider/templates/hpa.yaml @@ -0,0 +1,49 @@ +{{/* +# +# ============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========================================================= +# +*/}} +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "rapp-helloworld-provider.fullname" . }} + labels: + {{- include "rapp-helloworld-provider.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "rapp-helloworld-provider.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/service-exposure/charts/rapp-helloworld-provider/templates/ingress.yaml b/service-exposure/charts/rapp-helloworld-provider/templates/ingress.yaml new file mode 100644 index 00000000..80992c23 --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-provider/templates/ingress.yaml @@ -0,0 +1,62 @@ +{{/* +# +# ============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========================================================= +# +*/}} +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "rapp-helloworld-provider.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "rapp-helloworld-provider.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + backend: + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} diff --git a/service-exposure/charts/rapp-helloworld-provider/templates/service.yaml b/service-exposure/charts/rapp-helloworld-provider/templates/service.yaml new file mode 100644 index 00000000..1adda51d --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-provider/templates/service.yaml @@ -0,0 +1,36 @@ +{{/* +# +# ============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: v1 +kind: Service +metadata: + name: {{ include "rapp-helloworld-provider.fullname" . }} + labels: + {{- include "rapp-helloworld-provider.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "rapp-helloworld-provider.selectorLabels" . | nindent 4 }} diff --git a/service-exposure/charts/rapp-helloworld-provider/templates/serviceaccount.yaml b/service-exposure/charts/rapp-helloworld-provider/templates/serviceaccount.yaml new file mode 100644 index 00000000..23ca505a --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-provider/templates/serviceaccount.yaml @@ -0,0 +1,33 @@ +{{/* +# +# ============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========================================================= +# +*/}} +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "rapp-helloworld-provider.serviceAccountName" . }} + labels: + {{- include "rapp-helloworld-provider.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/service-exposure/charts/rapp-helloworld-provider/templates/tests/test-connection.yaml b/service-exposure/charts/rapp-helloworld-provider/templates/tests/test-connection.yaml new file mode 100644 index 00000000..7d295f60 --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-provider/templates/tests/test-connection.yaml @@ -0,0 +1,36 @@ +{{/* +# +# ============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: v1 +kind: Pod +metadata: + name: "{{ include "rapp-helloworld-provider.fullname" . }}-test-connection" + labels: + {{- include "rapp-helloworld-provider.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "rapp-helloworld-provider.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/service-exposure/charts/rapp-helloworld-provider/values.yaml b/service-exposure/charts/rapp-helloworld-provider/values.yaml new file mode 100644 index 00000000..bb6ef19d --- /dev/null +++ b/service-exposure/charts/rapp-helloworld-provider/values.yaml @@ -0,0 +1,125 @@ +# +# ============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========================================================= +# + +# Default values for rapp-helloworld-provider. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: ktimoney/rapps-rapp-helloworld-provider + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "latest" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "rapp-helloworld-provider" + +rbac: + # Specifies whether rbac should be enabled + create: true + +podAnnotations: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: false + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: rapp-helloworld-provider + paths: + - path: / + backend: + serviceName: rapp-helloworld-provider + servicePort: 80 + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +rapp: + securityEnabled: true + type: provider + realm: demo + client: demoprovider-cli + authenticator: client-jwt + roles: + - role : provider-viewer + grants: + - GET + - role : provider-admin + grants: + - GET + - POST + - PUT + - DELETE + diff --git a/service-exposure/deploy_rapp.sh b/service-exposure/deploy_rapp.sh new file mode 100644 index 00000000..4d77350c --- /dev/null +++ b/service-exposure/deploy_rapp.sh @@ -0,0 +1,58 @@ +#!/bin/sh +# +# ============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========================================================= +# +export host=$(minikube ip) + +if [ -z "$1" ] + then + echo "No argument supplied" + exit 1 +fi + +rapp=$1 + +echo "Deploying application..." +echo "------------------------" +curl http://$host:31570/install?chart=$rapp + +echo "\n" +echo "Waiting for pod to start..." +echo "---------------------------" +kubectl wait deployment -n istio-nonrtric $rapp --for=condition=available --timeout=90s + +echo "" +echo "Checking pod status..." +echo "----------------------" +kubectl get pods -n istio-nonrtric +#kubectl get pods --show-labels -n istio-nonrtric + +#if [ "$rapp" == "rapp-helloworld-invoker1" ] || [ "$rapp" == "rapp-helloworld-invoker2" ]; then +if [ "$rapp" != "rapp-helloworld-provider" ]; then + echo "" + echo "Inspect the log for $rapp..." + echo "-----------------------------------------------" + kubectl logs -l app.kubernetes.io/name=$rapp -n istio-nonrtric +fi +if [ "$rapp" = "rapp-helloworld-invoker1" ]; then + echo "" + echo "Inspect the log for $rapp jwt sidecar..." + echo "-----------------------------------------------------------" + kubectl logs -l app.kubernetes.io/name=$rapp -c jwt-proxy -n istio-nonrtric +fi diff --git a/service-exposure/keycloak.sh b/service-exposure/keycloak.sh new file mode 100644 index 00000000..8338b431 --- /dev/null +++ b/service-exposure/keycloak.sh @@ -0,0 +1,55 @@ +#!/bin/bash +# +# ============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========================================================= +# + +if [ -z "$1" ] + then + echo "No argument supplied" + exit 1 +fi + +OPERATION=$1 + +if [ "$OPERATION" == "deploy" ]; then + echo "Deploying applications..." + echo "-------------------------" + istioctl kube-inject -f postgres.yaml | kubectl apply -f - + sleep 10 + istioctl kube-inject -f keycloak.yaml | kubectl apply -f - + echo "" + echo "Waiting for pods to start..." + echo "----------------------------" + kubectl wait deployment -n default postgres --for=condition=available --timeout=90s + kubectl wait deployment -n default keycloak --for=condition=available --timeout=300s + echo "" + echo "Checking pod status..." + echo "----------------------" + kubectl get pods -n default +elif [ "$OPERATION" == "undeploy" ]; then + echo "Undeploying applications..." + echo "---------------------------" + kubectl delete -f keycloak.yaml + kubectl delete -f postgres.yaml +else + echo "Unrecogized operation ${OPERATION}" + exit 1 +fi + +exit 0 diff --git a/service-exposure/keycloak.yaml b/service-exposure/keycloak.yaml index d611c6d2..c8c4a630 100644 --- a/service-exposure/keycloak.yaml +++ b/service-exposure/keycloak.yaml @@ -20,7 +20,7 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: keycloak + name: keycloak namespace: default --- apiVersion: v1 @@ -31,12 +31,12 @@ metadata: app: 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 @@ -65,20 +65,20 @@ 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 + image: quay.io/keycloak/keycloak:16.1.1 + imagePullPolicy: IfNotPresent env: - name: KEYCLOAK_USER value: "admin" - name: KEYCLOAK_PASSWORD value: "admin" - name: KEYCLOAK_HTTPS_PORT - value: "8443" + value: "8443" - name: PROXY_ADDRESS_FORWARDING value: "true" - name: MANAGEMENT_USER @@ -89,18 +89,18 @@ spec: value: "false" - name: DB_VENDOR value: "postgres" - - name: DB_ADDR + - name: DB_ADDR value: "postgres" - - name: DB_PORT + - name: DB_PORT value: "5432" - name: DB_DATABASE value: "keycloak" - name: DB_USER - value: "keycloak" + value: "keycloak" - name : DB_PASSWORD - value: "keycloak" - - name : X509_CA_BUNDLE - value: /etc/x509/https/rootCA.crt + value: "keycloak" + - name : X509_CA_BUNDLE + value: /etc/x509/https/rootCA.crt ports: - name: http containerPort: 8080 @@ -111,12 +111,12 @@ spec: path: /auth/realms/master port: 8080 volumeMounts: - - name: keycloak-certs - mountPath: /etc/x509/https + - name: keycloak-certs + mountPath: /etc/x509/https volumes: - - name: keycloak-certs + - name: keycloak-certs hostPath: - path: /var/keycloak/certs + path: /var/keycloak/certs type: Directory --- apiVersion: networking.istio.io/v1alpha3 @@ -134,7 +134,7 @@ spec: tls: mode: PASSTHROUGH hosts: - - keycloak.est.tech + - keycloak.oran.org - port: number: 80 name: http @@ -148,14 +148,14 @@ metadata: name: keycloak-tls-vs spec: hosts: - - keycloak.est.tech + - keycloak.oran.org gateways: - kcgateway tls: - match: - port: 443 sniHosts: - - keycloak.est.tech + - keycloak.oran.org route: - destination: host: keycloak.default.svc.cluster.local @@ -170,7 +170,7 @@ spec: hosts: - "*" gateways: - - kcgateway + - kcgateway http: - name: "keycloak-routes" match: diff --git a/service-exposure/postgres.yaml b/service-exposure/postgres.yaml index 588dd8e1..d0e71c59 100644 --- a/service-exposure/postgres.yaml +++ b/service-exposure/postgres.yaml @@ -60,7 +60,7 @@ data: DO $$ BEGIN IF NOT EXISTS (SELECT FROM pg_user WHERE usename = 'capif') THEN - CREATE USER capif WITH PASSWORD 'capif'; + CREATE USER capif WITH PASSWORD 'capif'; GRANT ALL PRIVILEGES ON DATABASE capif TO capif; END IF; END @@ -77,11 +77,11 @@ spec: app: postgres ports: - protocol: TCP - port: 5432 + port: 5432 nodePort: 30032 - targetPort: 5432 + targetPort: 5432 --- -apiVersion: apps/v1 +apiVersion: apps/v1 kind: Deployment metadata: name: postgres @@ -101,17 +101,17 @@ spec: containers: - image: nexus3.onap.org:10001/postgres name: postgres - imagePullPolicy: IfNotPresent + imagePullPolicy: IfNotPresent env: - name: POSTGRES_DB - value: keycloak + value: keycloak - name: POSTGRES_USER - value: keycloak + value: keycloak - name: POSTGRES_PASSWORD - value: keycloak + value: keycloak - name: PGDATA - value: /var/lib/pgsql/data - lifecycle: + value: /var/lib/pgsql/data + lifecycle: postStart: exec: command: [ "/bin/sh", "-c", "sleep 10 && psql -U $POSTGRES_USER -f /init.sql" ] @@ -130,16 +130,16 @@ spec: initialDelaySeconds: 15 timeoutSeconds: 2 ports: - - containerPort: 5432 + - containerPort: 5432 name: postgres volumeMounts: - name: postgres-persistent-storage - mountPath: /var/lib/pgsql/data - - name : tmp-dir + mountPath: /var/lib/pgsql/data + - name : tmp-dir mountPath: /tmp - name: db-init - mountPath: /init.sql - subPath: init.sql + mountPath: /init.sql + subPath: init.sql volumes: - name: postgres-persistent-storage persistentVolumeClaim: @@ -147,7 +147,7 @@ spec: - name: tmp-dir hostPath: path: /tmp - type: Directory + type: Directory - name: db-init configMap: name: db-init diff --git a/service-exposure/rapps-helm-installer.go b/service-exposure/rapps-helm-installer.go index dc92fc79..0dc93419 100644 --- a/service-exposure/rapps-helm-installer.go +++ b/service-exposure/rapps-helm-installer.go @@ -17,7 +17,6 @@ // limitations under the License. // ========================LICENSE_END=================================== // - package main import ( @@ -70,6 +69,7 @@ type Rapp struct { SecurityEnabled bool Realm string Client string + Authenticator string Roles []struct { Role string Grants []string @@ -113,30 +113,10 @@ func runInstall(res http.ResponseWriter, req *http.Request) { if err != nil { msg = err.Error() } else { - if rapp.SecurityEnabled && rapp.Type == "provider" { - // keycloak client setup - fmt.Println("Setting up keycloak") - _, err = http.Get("http://rapps-keycloak-mgr.default/create?realm=" + rapp.Realm + "&name=" + rapp.Client + "&role=" + rapp.Roles[0].Role) - if err != nil { - msg = err.Error() - } else { - fmt.Println("Setting up istio") - _, err := http.Get("http://rapps-istio-mgr.default/create?name=" + chartName + "&realm=" + rapp.Realm + "&role=" + rapp.Roles[0].Role + "&method=" + rapp.Roles[0].Grants[0]) - if err != nil { - msg = err.Error() - } else { - // Install chart - fmt.Printf("Installing chart %s to %s namespace\n", chartName, namespace) - chart, err = installHelmChart(install) - if err != nil { - msg = "Error occurred during installation " + err.Error() - } else { - msg = "Successfully installed release: " + chart - } - } - } + err := installSecurity(rapp) + if err != nil { + msg = err.Error() } else { - // Install chart fmt.Printf("Installing chart %s to %s namespace\n", chartName, namespace) chart, err = installHelmChart(install) if err != nil { @@ -144,8 +124,8 @@ func runInstall(res http.ResponseWriter, req *http.Request) { } else { msg = "Successfully installed release: " + chart } - } + } } } registrerRapp(chartName, rapp.Type) @@ -159,6 +139,54 @@ func runInstall(res http.ResponseWriter, req *http.Request) { res.Write(data) } +func installSecurity(rapp Rapp) error { + var url string + var params string + role := rapp.Roles[0].Role + grants := rapp.Roles[0].Grants[0] + realm := rapp.Realm + client := rapp.Client + authenticator := rapp.Authenticator + + if !rapp.SecurityEnabled { + return nil + } + // Different security requirements depending on the rapp type + 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) + if err != nil { + return err + } else { + fmt.Println("Setting up istio") + url = "http://rapps-istio-mgr.default/create-policy?" + params = "name=" + chartName + "&realm=" + realm + "&role=" + role + "&method=" + grants + url += params + + _, err := http.Get(url) + if err != nil { + return err + } + } + } else { + fmt.Println("Setting up istio") + url = "http://rapps-istio-mgr.default/create-filter?" + params = "name=" + chartName + "&realm=" + realm + "&client=" + client + "&authType=" + authenticator + url += params + _, err := http.Get(url) + if err != nil { + return err + } + } + + return nil + +} + func runUninstall(res http.ResponseWriter, req *http.Request) { query := req.URL.Query() chartName = query.Get("chart") @@ -178,19 +206,9 @@ func runUninstall(res http.ResponseWriter, req *http.Request) { } else { msg = "Successfully uninstalled release: " + chart } - if rapp.SecurityEnabled && rapp.Type == "provider" { - // Remove istio objects for rapp - fmt.Println("Removing istio services") - _, err := http.Get("http://rapps-istio-mgr.default/remove?name=" + chartName) - if err != nil { - msg = err.Error() - } - // remove keycloak client - fmt.Println("Removing keycloak client") - _, err = http.Get("http://rapps-keycloak-mgr.default/remove?realm=" + rapp.Realm + "&name=" + rapp.Client + "&role=" + rapp.Roles[0].Role) - if err != nil { - msg = err.Error() - } + err := uninstallSecurity(rapp, chartName) + if err != nil { + msg = err.Error() } } unregistrerRapp(chartName, rapp.Type) @@ -204,6 +222,45 @@ func runUninstall(res http.ResponseWriter, req *http.Request) { res.Write(data) } +func uninstallSecurity(rapp Rapp, chartName string) error { + var url string + var params string + role := rapp.Roles[0].Role + realm := rapp.Realm + client := rapp.Client + authenticator := rapp.Authenticator + + if !rapp.SecurityEnabled { + return nil + } + if rapp.Type == "provider" { + // Remove istio objects for rapp + fmt.Println("Removing istio services") + _, err := http.Get("http://rapps-istio-mgr.default/remove-policy?name=" + chartName) + if err != nil { + return err + } + // remove keycloak client + fmt.Println("Removing keycloak client") + url = "http://rapps-keycloak-mgr.default/remove?" + params = "name=" + client + "&realm=" + realm + "&role=" + role + "&authType=" + authenticator + url += params + _, err = http.Get(url) + if err != nil { + return err + } + } + if rapp.Type == "invoker" { + // Remove istio objects for rapp + fmt.Println("Removing istio services") + _, err := http.Get("http://rapps-istio-mgr.default/remove-filter?name=" + chartName) + if err != nil { + return err + } + } + return nil +} + func runList(res http.ResponseWriter, req *http.Request) { chartInfo := list() // create response binary data @@ -480,7 +537,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 { diff --git a/service-exposure/rapps-helm-installer.yaml b/service-exposure/rapps-helm-installer.yaml index 19969dbd..e5cb1ead 100644 --- a/service-exposure/rapps-helm-installer.yaml +++ b/service-exposure/rapps-helm-installer.yaml @@ -50,7 +50,7 @@ spec: template: metadata: labels: - app: rapps-helm-installer + app: rapps-helm-installer version: v1 spec: containers: @@ -58,7 +58,7 @@ spec: image: ktimoney/rapps-helm-installer imagePullPolicy: IfNotPresent ports: - - containerPort: 9000 + - containerPort: 9000 resources: limits: memory: 256Mi @@ -67,7 +67,7 @@ spec: memory: 128Mi cpu: "80m" serviceAccountName: helm-app - replicas: 1 + replicas: 1 --- apiVersion: v1 kind: Service @@ -79,7 +79,7 @@ spec: app: rapps-helm-installer ports: - protocol: TCP - port: 80 - targetPort: 9000 - nodePort: 31570 + port: 80 + targetPort: 9000 + nodePort: 31570 type: NodePort diff --git a/service-exposure/rapps-istio-mgr.go b/service-exposure/rapps-istio-mgr.go index 5a0761b6..fb584bd7 100644 --- a/service-exposure/rapps-istio-mgr.go +++ b/service-exposure/rapps-istio-mgr.go @@ -17,7 +17,6 @@ // limitations under the License. // ========================LICENSE_END=================================== // - package main import ( @@ -25,6 +24,7 @@ import ( "context" "fmt" netv1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1" + netv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3" 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" @@ -35,102 +35,27 @@ import ( "net/http" "os" "path/filepath" - "strings" + "text/template" ) const ( NAMESPACE = "istio-nonrtric" ) -var gatewayManifest = ` -apiVersion: networking.istio.io/v1beta1 -kind: Gateway -metadata: - name: nonrtric-istio-RAPP-NAME-gateway - namespace: RAPP-NS -spec: - selector: - istio: ingressgateway # use Istio gateway implementation - servers: - - port: - number: 80 - name: http - protocol: HTTP - hosts: - - "*" -` - -var virtualServiceManifest = ` -apiVersion: networking.istio.io/v1beta1 -kind: VirtualService -metadata: - name: nonrtric-istio-RAPP-NAME-vs - namespace: RAPP-NS -spec: - hosts: - - "*" - gateways: - - nonrtric-istio-RAPP-NAME-gateway - http: - - name: "RAPP-NAME-routes" - match: - - uri: - prefix: "/RAPP-NAME" - route: - - destination: - port: - number: 80 - host: RAPP-NAME.RAPP-NS.svc.cluster.local -` - -var requestAuthenticationManifest = ` -apiVersion: security.istio.io/v1beta1 -kind: RequestAuthentication -metadata: - name: "jwt-RAPP-NAME" - namespace: RAPP-NS -spec: - selector: - matchLabels: - app.kubernetes.io/instance: RAPP-NAME - jwtRules: - - issuer: "http://192.168.49.2:31560/auth/realms/REALM-NAME" - jwksUri: "http://192.168.49.2:31560/auth/realms/REALM-NAME/protocol/openid-connect/certs" - - issuer: "http://keycloak.default:8080/auth/realms/REALM-NAME" - jwksUri: "http://keycloak.default:8080/auth/realms/REALM-NAME/protocol/openid-connect/certs" - - issuer: "https://192.168.49.2:31561/auth/realms/REALM-NAME" - jwksUri: "https://192.168.49.2:31561/auth/realms/REALM-NAME/protocol/openid-connect/certs" - - issuer: "https://keycloak.default:8443/auth/realms/REALM-NAME" - jwksUri: "https://keycloak.default:8443/auth/realms/REALM-NAME/protocol/openid-connect/certs" - - issuer: "https://keycloak.est.tech:443/auth/realms/REALM-NAME" - jwksUri: "https://keycloak.default:8443/auth/realms/REALM-NAME/protocol/openid-connect/certs" - - issuer: "http://istio-ingressgateway.istio-system:80/auth/realms/REALM-NAME" - jwksUri: "http://keycloak.default:8080/auth/realms/REALM-NAME/protocol/openid-connect/certs" -` - -var authorizationPolicyManifest = ` -apiVersion: "security.istio.io/v1beta1" -kind: "AuthorizationPolicy" -metadata: - name: "RAPP-NAME-policy" - namespace: RAPP-NS -spec: - selector: - matchLabels: - app.kubernetes.io/instance: RAPP-NAME - action: ALLOW - rules: - - from: - - source: - requestPrincipals: ["http://192.168.49.2:31560/auth/realms/REALM-NAME/", "http://keycloak.default:8080/auth/realms/REALM-NAME/", "https://192.168.49.2:31561/auth/realms/REALM-NAME/", "https://keycloak.default:8443/auth/realms/REALM-NAME/", "https://keycloak.est.tech:443/auth/realms/REALM-NAME/", "http://istio-ingressgateway.istio-system:80/auth/realms/REALM-NAME/"] - - to: - - operation: - methods: ["METHOD-NAME"] - paths: ["/RAPP-NAME"] - when: - - key: request.auth.claims[clientRole] - values: ["ROLE-NAME"] -` +type TemplateConfig struct { + Name string + Namespace string + Realm string + Client string + Authenticator string + Role string + Method string +} + +var inputs TemplateConfig +var appName string + +var config *template.Template func connectToK8s() *versioned.Clientset { config, err := rest.InClusterConfig() @@ -159,13 +84,17 @@ func connectToK8s() *versioned.Clientset { return ic } -func createGateway(clientset *versioned.Clientset, appName string) (string, error) { +func createGateway(clientset *versioned.Clientset) (string, error) { gtClient := clientset.NetworkingV1beta1().Gateways(NAMESPACE) - manifest := strings.Replace(gatewayManifest, "RAPP-NAME", appName, -1) - manifest = strings.Replace(manifest, "RAPP-NS", NAMESPACE, -1) + 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)), 1000) + dec := k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(manifest.String())), 1000) if err := dec.Decode(>); err != nil { return "", err @@ -181,13 +110,17 @@ func createGateway(clientset *versioned.Clientset, appName string) (string, erro return result.GetName(), nil } -func createVirtualService(clientset *versioned.Clientset, appName string) (string, error) { +func createVirtualService(clientset *versioned.Clientset) (string, error) { vsClient := clientset.NetworkingV1beta1().VirtualServices(NAMESPACE) - manifest := strings.Replace(virtualServiceManifest, "RAPP-NAME", appName, -1) - manifest = strings.Replace(manifest, "RAPP-NS", NAMESPACE, -1) + 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)), 1000) + dec := k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(manifest.String())), 1000) if err := dec.Decode(&vs); err != nil { return "", err @@ -203,14 +136,17 @@ func createVirtualService(clientset *versioned.Clientset, appName string) (strin return result.GetName(), nil } -func createRequestAuthentication(clientset *versioned.Clientset, appName, realmName string) (string, error) { +func createRequestAuthentication(clientset *versioned.Clientset) (string, error) { raClient := clientset.SecurityV1beta1().RequestAuthentications(NAMESPACE) - manifest := strings.Replace(requestAuthenticationManifest, "RAPP-NAME", appName, -1) - manifest = strings.Replace(manifest, "REALM-NAME", realmName, -1) - manifest = strings.Replace(manifest, "RAPP-NS", NAMESPACE, -1) + 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)), 1000) + dec := k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(manifest.String())), 1000) if err := dec.Decode(&ra); err != nil { return "", err @@ -226,16 +162,17 @@ func createRequestAuthentication(clientset *versioned.Clientset, appName, realmN return result.GetName(), nil } -func createAuthorizationPolicy(clientset *versioned.Clientset, appName, realmName, roleName, methodName string) (string, error) { +func createAuthorizationPolicy(clientset *versioned.Clientset) (string, error) { apClient := clientset.SecurityV1beta1().AuthorizationPolicies(NAMESPACE) - manifest := strings.Replace(authorizationPolicyManifest, "RAPP-NAME", appName, -1) - manifest = strings.Replace(manifest, "REALM-NAME", realmName, -1) - manifest = strings.Replace(manifest, "ROLE-NAME", roleName, -1) - manifest = strings.Replace(manifest, "METHOD-NAME", methodName, -1) - manifest = strings.Replace(manifest, "RAPP-NS", NAMESPACE, -1) + 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)), 1000) + dec := k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(manifest.String())), 1000) if err := dec.Decode(&ap); err != nil { return "", err @@ -251,7 +188,33 @@ func createAuthorizationPolicy(clientset *versioned.Clientset, appName, realmNam return result.GetName(), nil } -func removeGateway(clientset *versioned.Clientset, appName string) { +func createEnvoyFilter(clientset *versioned.Clientset) (string, error) { + 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 + } + + ef := &netv1alpha3.EnvoyFilter{} + dec := k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(manifest.String())), 1000) + + if err = dec.Decode(&ef); err != nil { + return "", err + } + + result, err := efClient.Create(context.TODO(), ef, metav1.CreateOptions{}) + + if err != nil { + return "", err + } + + fmt.Printf("Create Envoy Filter %s \n", result.GetName()) + return result.GetName(), nil +} + +func removeGateway(clientset *versioned.Clientset) { gtClient := clientset.NetworkingV1beta1().Gateways(NAMESPACE) err := gtClient.Delete(context.TODO(), "nonrtric-istio-"+appName+"-gateway", metav1.DeleteOptions{}) if err != nil { @@ -261,7 +224,7 @@ func removeGateway(clientset *versioned.Clientset, appName string) { } } -func removeVirtualService(clientset *versioned.Clientset, appName string) { +func removeVirtualService(clientset *versioned.Clientset) { vsClient := clientset.NetworkingV1beta1().VirtualServices(NAMESPACE) err := vsClient.Delete(context.TODO(), "nonrtric-istio-"+appName+"-vs", metav1.DeleteOptions{}) if err != nil { @@ -271,7 +234,7 @@ func removeVirtualService(clientset *versioned.Clientset, appName string) { } } -func removeRequestAuthentication(clientset *versioned.Clientset, appName string) { +func removeRequestAuthentication(clientset *versioned.Clientset) { raClient := clientset.SecurityV1beta1().RequestAuthentications(NAMESPACE) err := raClient.Delete(context.TODO(), "jwt-"+appName, metav1.DeleteOptions{}) if err != nil { @@ -281,7 +244,7 @@ func removeRequestAuthentication(clientset *versioned.Clientset, appName string) } } -func removeAuthorizationPolicy(clientset *versioned.Clientset, appName string) { +func removeAuthorizationPolicy(clientset *versioned.Clientset) { apClient := clientset.SecurityV1beta1().AuthorizationPolicies(NAMESPACE) err := apClient.Delete(context.TODO(), appName+"-policy", metav1.DeleteOptions{}) if err != nil { @@ -291,30 +254,41 @@ func removeAuthorizationPolicy(clientset *versioned.Clientset, appName string) { } } +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") + } +} + func createIstioPolicy(res http.ResponseWriter, req *http.Request) { query := req.URL.Query() realmName := query.Get("realm") appName := query.Get("name") roleName := query.Get("role") methodName := query.Get("method") + inputs = TemplateConfig{Name: appName, Namespace: NAMESPACE, Realm: realmName, Role: roleName, Method: methodName } var msg string clientset := connectToK8s() - _, err := createGateway(clientset, appName) + _, err := createGateway(clientset) if err != nil { msg = err.Error() fmt.Println(err.Error()) } else { - _, err := createVirtualService(clientset, appName) + _, err := createVirtualService(clientset) if err != nil { msg = err.Error() fmt.Println(err.Error()) } else { - _, err := createRequestAuthentication(clientset, appName, realmName) + _, err := createRequestAuthentication(clientset) if err != nil { msg = err.Error() fmt.Println(err.Error()) } else { - _, err := createAuthorizationPolicy(clientset, appName, realmName, roleName, methodName) + _, err := createAuthorizationPolicy(clientset) if err != nil { msg = err.Error() fmt.Println(err.Error()) @@ -331,20 +305,51 @@ func createIstioPolicy(res http.ResponseWriter, req *http.Request) { res.Write(data) } +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) +} + func removeIstioPolicy(res http.ResponseWriter, req *http.Request) { query := req.URL.Query() - appName := query.Get("name") + appName = query.Get("name") clientset := connectToK8s() - removeAuthorizationPolicy(clientset, appName) - removeRequestAuthentication(clientset, appName) - removeVirtualService(clientset, appName) - removeGateway(clientset, appName) + removeAuthorizationPolicy(clientset) + removeRequestAuthentication(clientset) + removeVirtualService(clientset) + removeGateway(clientset) +} + +func removeIstioFilter(res http.ResponseWriter, req *http.Request) { + query := req.URL.Query() + appName = query.Get("name") + clientset := connectToK8s() + removeEnvoyFilter(clientset) } func main() { - createIstioHandler := http.HandlerFunc(createIstioPolicy) - http.Handle("/create", createIstioHandler) - removeIstioHandler := http.HandlerFunc(removeIstioPolicy) - http.Handle("/remove", removeIstioHandler) + createIstioPolicyHandler := http.HandlerFunc(createIstioPolicy) + http.Handle("/create-policy", createIstioPolicyHandler) + removeIstioPolicyHandler := http.HandlerFunc(removeIstioPolicy) + http.Handle("/remove-policy", removeIstioPolicyHandler) + createIstioFilterHandler := http.HandlerFunc(createIstioFilter) + http.Handle("/create-filter", createIstioFilterHandler) + removeIstioFilterHandler := http.HandlerFunc(removeIstioFilter) + http.Handle("/remove-filter", removeIstioFilterHandler) http.ListenAndServe(":9000", nil) } diff --git a/service-exposure/rapps-istio-mgr.yaml b/service-exposure/rapps-istio-mgr.yaml index 6003bb55..92d3f3a7 100644 --- a/service-exposure/rapps-istio-mgr.yaml +++ b/service-exposure/rapps-istio-mgr.yaml @@ -31,7 +31,7 @@ spec: template: metadata: labels: - app: rapps-istio-mgr + app: rapps-istio-mgr version: v1 spec: containers: @@ -39,7 +39,7 @@ spec: image: ktimoney/rapps-istio-mgr imagePullPolicy: IfNotPresent ports: - - containerPort: 9000 + - containerPort: 9000 resources: limits: memory: 256Mi @@ -48,7 +48,7 @@ spec: memory: 128Mi cpu: "80m" serviceAccountName: helm-app - replicas: 1 + replicas: 1 --- apiVersion: v1 kind: Service @@ -60,7 +60,7 @@ spec: app: rapps-istio-mgr ports: - protocol: TCP - port: 80 - targetPort: 9000 - nodePort: 31590 + port: 80 + targetPort: 9000 + nodePort: 31551 type: NodePort diff --git a/service-exposure/rapps-jwt.go b/service-exposure/rapps-jwt.go new file mode 100644 index 00000000..d220458b --- /dev/null +++ b/service-exposure/rapps-jwt.go @@ -0,0 +1,227 @@ +// - +// ========================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 +// +// 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 ( + "crypto/tls" + "crypto/x509" + "encoding/json" + "flag" + "fmt" + "io/ioutil" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + kubernetes "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "net/http" + "net/url" + "rapps/utils/generatejwt" + "context" + "net" + "time" +) + +type Jwttoken struct { + Access_token string + Expires_in int + Refresh_expires_in int + Refresh_token string + Token_type string + Not_before_policy int + Session_state string + Scope string +} + +var keycloakHost string +var keycloakPort string +var keycloakAlias string +var realmName string +var clientId string +var namespace string +var authenticator string +var healthy bool = true +var jwt Jwttoken + +const ( + scope = "email" + client_assertion_type = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" +) + +func getToken(res http.ResponseWriter, req *http.Request) { + var resp = &http.Response{} + var err error + authenticator = req.Header.Get("authenticator") + clientId = req.Header.Get("client") + realmName = req.Header.Get("realm") + namespace = req.Header.Get("ns") + keycloakUrl := "http://" + keycloakHost + ":" + keycloakPort + "/auth/realms/" + realmName + "/protocol/openid-connect/token" + fmt.Printf("Making token request to %s\n", keycloakUrl) + res.Header().Set("Content-type", "application/json") + res.Header().Set("Authorization", "") + + if authenticator == "client-jwt" { + resp, err = getJwtToken(keycloakUrl, clientId) + } else if authenticator == "client-x509" { + resp, err = getx509Token(keycloakUrl, clientId) + } else { + resp, err = getSecretToken(keycloakUrl, clientId) + } + + if err != nil { + fmt.Println(err) + res.WriteHeader(http.StatusInternalServerError) + res.Write([]byte(err.Error())) + panic("Something wrong with the credentials or url ") + } + + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + json.Unmarshal([]byte(body), &jwt) + fmt.Printf("Token: %s\n", jwt.Access_token) + + res.Header().Set("Authorization", "Bearer "+jwt.Access_token) + res.WriteHeader(http.StatusOK) + res.Write([]byte("Successfully retrieved JWT access token")) +} + +func getJwtToken(keycloakUrl, clientId string) (*http.Response, error) { + var resp = &http.Response{} + var err error + client_assertion := getClientAssertion() + + if jwt.Refresh_token != "" { + resp, err = http.PostForm(keycloakUrl, url.Values{"client_assertion_type": {client_assertion_type}, + "client_assertion": {client_assertion}, "grant_type": {"refresh_token"}, + "refresh_token": {jwt.Refresh_token}, "client_id": {clientId}, "scope": {scope}}) + } else { + resp, err = http.PostForm(keycloakUrl, url.Values{"client_assertion_type": {client_assertion_type}, + "client_assertion": {client_assertion}, "grant_type": {"client_credentials"}, + "client_id": {clientId}, "scope": {scope}}) + } + + return resp, err +} + +func getClientAssertion() string { + realm := "http://" + keycloakHost + ":" + keycloakPort + "/auth/realms/" + realmName + clientAssertion := generatejwt.CreateJWT("/certs/client.key", "", clientId, realm) + return clientAssertion +} + +func getx509Token(keycloakUrl, clientId string) (*http.Response, error) { + var resp = &http.Response{} + var err error + + client := getClient() + resp, err = client.PostForm(keycloakUrl, url.Values{"username": {""}, "password": {""}, "grant_type": {"password"}, "client_id": {clientId}, "scope": {scope}}) + + return resp, err +} + +func getClient() *http.Client { + caCert, _ := ioutil.ReadFile("/certs/rootCA.crt") + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + + cert, _ := tls.LoadX509KeyPair("/certs/client.crt", "/certs/client.key") + + dialer := &net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + DualStack: true, + } + + client := &http.Client{ + Transport: &http.Transport{ + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + fmt.Println("address original =", addr) + if addr == keycloakAlias+":"+keycloakPort { + addr = keycloakHost + ":" + keycloakPort + fmt.Println("address modified =", addr) + } + return dialer.DialContext(ctx, network, addr) + }, + TLSClientConfig: &tls.Config{ + RootCAs: caCertPool, + Certificates: []tls.Certificate{cert}, + }, + }, + } + return client +} + +func getSecretToken(keycloakUrl, clientId string) (*http.Response, error) { + var resp = &http.Response{} + var err error + + secretName := clientId + "-secret" + clientSecret := getSecret(secretName) + resp, err = http.PostForm(keycloakUrl, + url.Values{"client_secret": {clientSecret}, "grant_type": {"client_credentials"}, "client_id": {clientId}}) + + return resp, err +} + +func getSecret(secretName string) string { + clientset := connectToK8s() + res, err := clientset.CoreV1().Secrets(namespace).Get(context.TODO(), secretName, metav1.GetOptions{}) + if err != nil { + fmt.Println(err.Error()) + } + return string(res.Data["client_secret"]) +} + +func connectToK8s() *kubernetes.Clientset { + config, err := rest.InClusterConfig() + if err != nil { + fmt.Println("failed to create K8s config") + } + + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + fmt.Println("Failed to create K8s clientset") + } + + return clientset +} + +func health(res http.ResponseWriter, req *http.Request) { + if healthy { + res.WriteHeader(http.StatusOK) + res.Write([]byte("healthy")) + } else { + res.WriteHeader(http.StatusInternalServerError) + res.Write([]byte("unhealthy")) + } +} + +func main() { + flag.StringVar(&keycloakHost, "keycloakHost", "istio-ingressgateway.istio-system", "Keycloak Host") + flag.StringVar(&keycloakPort, "keycloakPort", "80", "Keycloak Port") + flag.StringVar(&keycloakAlias, "keycloakAlias", "keycloak.oran.org", "Keycloak URL Alias") + flag.Parse() + + healthHandler := http.HandlerFunc(health) + http.Handle("/health", healthHandler) + tokenHandler := http.HandlerFunc(getToken) + http.Handle("/token", tokenHandler) + http.ListenAndServe(":8888", nil) + + ioutil.WriteFile("init.txt", []byte("Initialization done."), 0644) +} diff --git a/service-exposure/rapps-keycloak-mgr.go b/service-exposure/rapps-keycloak-mgr.go index 5fc92f45..35e503dc 100644 --- a/service-exposure/rapps-keycloak-mgr.go +++ b/service-exposure/rapps-keycloak-mgr.go @@ -17,7 +17,6 @@ // limitations under the License. // ========================LICENSE_END=================================== // - package main import ( @@ -29,7 +28,6 @@ import ( kubernetes "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "net/http" - "strings" "rapps/utils/pemtojwks" ) @@ -42,12 +40,13 @@ func createClient(res http.ResponseWriter, req *http.Request) { realmName := query.Get("realm") clientName := query.Get("name") role := query.Get("role") + authType := query.Get("authType") var msg string - msg, err := create(realmName, clientName, role) + msg, err := create(realmName, clientName, role, authType) if err != nil { msg = err.Error() } - if realmName != "x509" && realmName != "jwt" { + if authType == "client-secret" { createSecret(msg, clientName, realmName, role, namespace) } // create response binary data @@ -61,12 +60,13 @@ func removeClient(res http.ResponseWriter, req *http.Request) { realmName := query.Get("realm") clientName := query.Get("name") role := query.Get("role") + authType := query.Get("authType") var msg string = "Removed keycloak " + clientName + " from " + realmName + " realm" remove(realmName, clientName) - if realmName != "x509" && realmName != "jwt" { - removeSecret(namespace, role) - } + if authType == "client-secret" { + removeSecret(namespace, role) + } // create response binary data data := []byte(msg) // slice of bytes // write `data` to response @@ -81,7 +81,7 @@ func main() { http.ListenAndServe(":9000", nil) } -func create(realmName, clientName, clientRoleName string) (string, error) { +func create(realmName, clientName, clientRoleName, authType string) (string, error) { client := gocloak.NewClient("http://keycloak.default:8080") ctx := context.Background() token, err := client.LoginAdmin(ctx, "admin", "admin", "master") @@ -122,7 +122,7 @@ func create(realmName, clientName, clientRoleName string) (string, error) { fmt.Println("Retrieved AuthenticationFlow id", flowId) } - newClient1 := gocloak.Client{ + secretClient := gocloak.Client{ ClientID: gocloak.StringP(clientName), Enabled: gocloak.BoolP(true), DirectAccessGrantsEnabled: gocloak.BoolP(true), @@ -135,7 +135,7 @@ func create(realmName, clientName, clientRoleName string) (string, error) { "client_credentials.use_refresh_token": "true"}, } - newClient2 := gocloak.Client{ + x509Client := gocloak.Client{ ClientID: gocloak.StringP(clientName), Enabled: gocloak.BoolP(true), DirectAccessGrantsEnabled: gocloak.BoolP(true), @@ -151,32 +151,32 @@ func create(realmName, clientName, clientRoleName string) (string, error) { AuthenticationFlowBindingOverrides: &map[string]string{"direct_grant": flowId}, } - jwksString := pemtojwks.CreateJWKS("/certs/client_pub.key", "public", "/certs/client.crt") - newClient3 := gocloak.Client{ - ClientID: gocloak.StringP(clientName), - Enabled: gocloak.BoolP(true), - DirectAccessGrantsEnabled: gocloak.BoolP(true), - BearerOnly: gocloak.BoolP(false), - PublicClient: gocloak.BoolP(false), - ServiceAccountsEnabled: gocloak.BoolP(true), - ClientAuthenticatorType: gocloak.StringP("client-jwt"), - DefaultClientScopes: &[]string{"email"}, - Attributes: &map[string]string{"token.endpoint.auth.signing.alg": "RS256", - "use.jwks.string": "true", - "jwks.string": jwksString, - "use.refresh.tokens": "true", - "client_credentials.use_refresh_token": "true", + jwksString := pemtojwks.CreateJWKS("/certs/client.crt") + jwtClient := gocloak.Client{ + ClientID: gocloak.StringP(clientName), + Enabled: gocloak.BoolP(true), + DirectAccessGrantsEnabled: gocloak.BoolP(true), + BearerOnly: gocloak.BoolP(false), + PublicClient: gocloak.BoolP(false), + ServiceAccountsEnabled: gocloak.BoolP(true), + ClientAuthenticatorType: gocloak.StringP("client-jwt"), + DefaultClientScopes: &[]string{"email"}, + Attributes: &map[string]string{"token.endpoint.auth.signing.alg": "RS256", + "use.jwks.string": "true", + "jwks.string": jwksString, + "use.refresh.tokens": "true", + "client_credentials.use_refresh_token": "true", }, - } + } var newClient gocloak.Client - if strings.HasPrefix(clientName, "x509") { - newClient = newClient2 - } else if strings.HasPrefix(clientName, "jwt") { - newClient = newClient3 + if authType == "client-x509" { + newClient = x509Client + } else if authType == "client-jwt" { + newClient = jwtClient } else { - newClient = newClient1 - } + newClient = secretClient + } clientId, err := client.CreateClient(ctx, token.AccessToken, realmName, newClient) if err != nil { @@ -204,7 +204,7 @@ func create(realmName, clientName, clientRoleName string) (string, error) { fmt.Println("Service Account user", *user.Username) } - if strings.HasPrefix(clientName, "x509") { + if authType == "client-x509" { newUser := gocloak.User{ ID: gocloak.StringP(realmName + "user"), Username: gocloak.StringP(realmName + "user"), @@ -262,7 +262,7 @@ func create(realmName, clientName, clientRoleName string) (string, error) { fmt.Println("Client rolemapper added to client") } - if strings.HasPrefix(clientName, "x509") { + if authType == "client-x509" { clientRole := *newClient.ClientID + "." + clientRoleName clientroleMapper := gocloak.ProtocolMapperRepresentation{ @@ -336,7 +336,7 @@ func createSecret(clientSecret, clientName, realmName, role, namespace string) { } func remove(realmName, clientName string) { - adminClient := gocloak.NewClient("http://192.168.49.2:31560") + adminClient := gocloak.NewClient("http://keycloak.default:8080") ctx := context.Background() token, err := adminClient.LoginAdmin(ctx, "admin", "admin", "master") if err != nil { diff --git a/service-exposure/rapps-keycloak-mgr.yaml b/service-exposure/rapps-keycloak-mgr.yaml index c2f14da0..67747469 100644 --- a/service-exposure/rapps-keycloak-mgr.yaml +++ b/service-exposure/rapps-keycloak-mgr.yaml @@ -31,7 +31,7 @@ spec: template: metadata: labels: - app: rapps-keycloak-mgr + app: rapps-keycloak-mgr version: v1 spec: containers: @@ -39,7 +39,7 @@ spec: image: ktimoney/rapps-keycloak-mgr imagePullPolicy: IfNotPresent ports: - - containerPort: 9000 + - containerPort: 9000 resources: limits: memory: 256Mi @@ -58,7 +58,7 @@ spec: path: /var/rapps/certs type: DirectoryOrCreate serviceAccountName: helm-app - replicas: 1 + replicas: 1 --- apiVersion: v1 kind: Service @@ -70,7 +70,7 @@ spec: app: rapps-keycloak-mgr ports: - protocol: TCP - port: 80 - targetPort: 9000 + port: 80 + targetPort: 9000 nodePort: 31600 type: NodePort diff --git a/service-exposure/rapps-rapp-helloworld-invoker1.go b/service-exposure/rapps-rapp-helloworld-invoker1.go new file mode 100644 index 00000000..bfa5afab --- /dev/null +++ b/service-exposure/rapps-rapp-helloworld-invoker1.go @@ -0,0 +1,163 @@ +// - +// ========================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 +// +// 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" + "flag" + "fmt" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + "io/ioutil" + "net/http" + "strings" + "time" +) + + +var gatewayHost string +var gatewayPort string +var securityEnabled string +var useGateway string +var rapp string +var methods string +var healthy bool = true +var ttime time.Time + +const ( + namespace = "istio-nonrtric" + scope = "email" + client_assertion_type = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" +) + +var ( + reqDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Name: "rapp_http_request_duration_seconds", + Help: "Duration of the last request call.", + Buckets: []float64{0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10}, + }, []string{"app", "func", "handler", "method", "code"}) + reqBytes = prometheus.NewSummaryVec(prometheus.SummaryOpts{ + Name: "rapp_bytes_summary", + Help: "Summary of bytes transferred over http", + }, []string{"app", "func", "handler", "method", "code"}) +) + + +func MakeRequest(client *http.Client, prefix string, method string, ch chan string) { + var service = strings.Split(prefix, "/")[1] + var gatewayUrl = "http://" + gatewayHost + ":" + gatewayPort + var jsonValue []byte = []byte{} + var restUrl string = "" + + if securityEnabled != "true" { + gatewayUrl = "http://" + service + "." + namespace + ":80" + prefix = "" + } + + restUrl = gatewayUrl + prefix + resp := &http.Response{} + + timer := prometheus.NewTimer(prometheus.ObserverFunc(func(v float64) { + reqDuration.WithLabelValues("rapp-helloworld-invoker1", "MakeRequest", resp.Request.URL.Path, resp.Request.Method, + resp.Status).Observe(v) + })) + defer timer.ObserveDuration() + fmt.Printf("Making get request to %s\n", restUrl) + req, err := http.NewRequest(method, restUrl, bytes.NewBuffer(jsonValue)) + if err != nil { + fmt.Printf("Got error %s", err.Error()) + } + req.Header.Set("Content-type", "application/json") + + resp, err = client.Do(req) + if err != nil { + fmt.Printf("Got error %s", err.Error()) + } + + defer resp.Body.Close() + body, _ := ioutil.ReadAll(resp.Body) + reqBytes.WithLabelValues("rapp-helloworld-invoker1", "MakeRequest", req.URL.Path, req.Method, + resp.Status).Observe(float64(resp.ContentLength)) + + respString := string(body[:]) + if respString == "RBAC: access denied" { + respString += " for " + service + " " + strings.ToLower(method) + " request" + } + fmt.Printf("Received response for %s %s request - %s\n", service, strings.ToLower(method), respString) + ch <- prefix + "," + method +} + +func health(res http.ResponseWriter, req *http.Request) { + if healthy { + res.WriteHeader(http.StatusOK) + res.Write([]byte("healthy")) + } else { + res.WriteHeader(http.StatusInternalServerError) + res.Write([]byte("unhealthy")) + } +} + +func main() { + ttime = time.Now() + time.Sleep(3 * time.Second) + prometheus.Register(reqDuration) + prometheus.Register(reqBytes) + + flag.StringVar(&gatewayHost, "gatewayHost", "istio-ingressgateway.istio-system", "Gateway Host") + flag.StringVar(&gatewayPort, "gatewayPort", "80", "Gateway Port") + flag.StringVar(&useGateway, "useGateway", "Y", "Connect to services through API gateway") + flag.StringVar(&securityEnabled, "securityEnabled", "true", "Security is required to use this application") + flag.StringVar(&rapp, "rapp", "rapp-helloworld-provider", "Name of rapp to invoke") + flag.StringVar(&methods, "methods", "GET", "Methods to access application") + flag.Parse() + + healthHandler := http.HandlerFunc(health) + http.Handle("/health", healthHandler) + http.Handle("/metrics", promhttp.Handler()) + go func() { + http.ListenAndServe(":9000", nil) + }() + + ioutil.WriteFile("init.txt", []byte("Initialization done."), 0644) + + client := &http.Client{ + Timeout: time.Second * 10, + } + + ch := make(chan string) + var prefixArray []string = []string{"/" + rapp} + var methodArray []string = []string{methods} + for _, prefix := range prefixArray { + for _, method := range methodArray { + go MakeRequest(client, prefix, method, ch) + } + } + + + for r := range ch { + go func(resp string) { + time.Sleep(10 * time.Second) + elements := strings.Split(resp, ",") + prefix := elements[0] + method := elements[1] + MakeRequest(client, prefix, method, ch) + }(r) + } +} diff --git a/service-exposure/rapps-rapp-helloworld-invoker2.go b/service-exposure/rapps-rapp-helloworld-invoker2.go new file mode 100644 index 00000000..93c6981f --- /dev/null +++ b/service-exposure/rapps-rapp-helloworld-invoker2.go @@ -0,0 +1,163 @@ +// - +// ========================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 +// +// 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" + "flag" + "fmt" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + "io/ioutil" + "net/http" + "strings" + "time" +) + + +var gatewayHost string +var gatewayPort string +var securityEnabled string +var useGateway string +var rapp string +var methods string +var healthy bool = true +var ttime time.Time + +const ( + namespace = "istio-nonrtric" + scope = "email" + client_assertion_type = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" +) + +var ( + reqDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Name: "rapp_http_request_duration_seconds", + Help: "Duration of the last request call.", + Buckets: []float64{0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10}, + }, []string{"app", "func", "handler", "method", "code"}) + reqBytes = prometheus.NewSummaryVec(prometheus.SummaryOpts{ + Name: "rapp_bytes_summary", + Help: "Summary of bytes transferred over http", + }, []string{"app", "func", "handler", "method", "code"}) +) + + +func MakeRequest(client *http.Client, prefix string, method string, ch chan string) { + var service = strings.Split(prefix, "/")[1] + var gatewayUrl = "http://" + gatewayHost + ":" + gatewayPort + var jsonValue []byte = []byte{} + var restUrl string = "" + + if securityEnabled != "true" { + gatewayUrl = "http://" + service + "." + namespace + ":80" + prefix = "" + } + + restUrl = gatewayUrl + prefix + resp := &http.Response{} + + timer := prometheus.NewTimer(prometheus.ObserverFunc(func(v float64) { + reqDuration.WithLabelValues("rapp-helloworld-invoker2", "MakeRequest", resp.Request.URL.Path, resp.Request.Method, + resp.Status).Observe(v) + })) + defer timer.ObserveDuration() + fmt.Printf("Making get request to %s\n", restUrl) + req, err := http.NewRequest(method, restUrl, bytes.NewBuffer(jsonValue)) + if err != nil { + fmt.Printf("Got error %s", err.Error()) + } + req.Header.Set("Content-type", "application/json") + + resp, err = client.Do(req) + if err != nil { + fmt.Printf("Got error %s", err.Error()) + } + + defer resp.Body.Close() + body, _ := ioutil.ReadAll(resp.Body) + reqBytes.WithLabelValues("rapp-helloworld-invoker2", "MakeRequest", req.URL.Path, req.Method, + resp.Status).Observe(float64(resp.ContentLength)) + + respString := string(body[:]) + if respString == "RBAC: access denied" { + respString += " for " + service + " " + strings.ToLower(method) + " request" + } + fmt.Printf("Received response for %s %s request - %s\n", service, strings.ToLower(method), respString) + ch <- prefix + "," + method +} + +func health(res http.ResponseWriter, req *http.Request) { + if healthy { + res.WriteHeader(http.StatusOK) + res.Write([]byte("healthy")) + } else { + res.WriteHeader(http.StatusInternalServerError) + res.Write([]byte("unhealthy")) + } +} + +func main() { + ttime = time.Now() + time.Sleep(4 * time.Second) + prometheus.Register(reqDuration) + prometheus.Register(reqBytes) + + flag.StringVar(&gatewayHost, "gatewayHost", "istio-ingressgateway.istio-system", "Gateway Host") + flag.StringVar(&gatewayPort, "gatewayPort", "80", "Gateway Port") + flag.StringVar(&useGateway, "useGateway", "Y", "Connect to services through API gateway") + flag.StringVar(&securityEnabled, "securityEnabled", "true", "Security is required to use this application") + flag.StringVar(&rapp, "rapp", "rapp-helloworld-provider", "Name of rapp to invoke") + flag.StringVar(&methods, "methods", "GET", "Methods to access application") + flag.Parse() + + healthHandler := http.HandlerFunc(health) + http.Handle("/health", healthHandler) + http.Handle("/metrics", promhttp.Handler()) + go func() { + http.ListenAndServe(":9000", nil) + }() + + ioutil.WriteFile("init.txt", []byte("Initialization done."), 0644) + + client := &http.Client{ + Timeout: time.Second * 10, + } + + ch := make(chan string) + var prefixArray []string = []string{"/" + rapp} + var methodArray []string = []string{methods} + for _, prefix := range prefixArray { + for _, method := range methodArray { + go MakeRequest(client, prefix, method, ch) + } + } + + + for r := range ch { + go func(resp string) { + time.Sleep(10 * time.Second) + elements := strings.Split(resp, ",") + prefix := elements[0] + method := elements[1] + MakeRequest(client, prefix, method, ch) + }(r) + } +} diff --git a/service-exposure/rapps-rapp-helloworld-provider.go b/service-exposure/rapps-rapp-helloworld-provider.go new file mode 100644 index 00000000..63d6493a --- /dev/null +++ b/service-exposure/rapps-rapp-helloworld-provider.go @@ -0,0 +1,42 @@ +// - +// ========================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 +// +// 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 ( + "net/http" +) + +// create a handler struct +type HttpHandler struct{} + +// implement `ServeHTTP` method on `HttpHandler` struct +func (h HttpHandler) ServeHTTP(res http.ResponseWriter, req *http.Request) { + // create response binary data + data := []byte("Hello World Demo!") // slice of bytes + // write `data` to response + res.Write(data) +} + +func main() { + // create a new handler + handler := HttpHandler{} + // listen and serve + http.ListenAndServe(":9000", handler) +} diff --git a/service-exposure/rapps-rapp-invoker.go b/service-exposure/rapps-rapp-invoker.go index 4dc73591..21c72c04 100644 --- a/service-exposure/rapps-rapp-invoker.go +++ b/service-exposure/rapps-rapp-invoker.go @@ -17,7 +17,6 @@ // limitations under the License. // ========================LICENSE_END=================================== // - package main import ( @@ -57,7 +56,7 @@ var role string var rapp string var methods string var healthy bool = true -var ttime time.Time +var ttime time.Time var jwt Jwttoken const ( @@ -80,7 +79,7 @@ func getToken(secretName string) string { ttime = time.Now() ttime = ttime.Add(time.Second * time.Duration(jwt.Expires_in)) } - return jwt.Access_token + return jwt.Access_token } func getSecret(secretName string) (string, string, string) { diff --git a/service-exposure/rapps-rapp-provider.go b/service-exposure/rapps-rapp-provider.go index 227f85ea..4b5efd77 100644 --- a/service-exposure/rapps-rapp-provider.go +++ b/service-exposure/rapps-rapp-provider.go @@ -17,7 +17,6 @@ // limitations under the License. // ========================LICENSE_END=================================== // - package main import ( diff --git a/service-exposure/rapps-webhook.go b/service-exposure/rapps-webhook.go new file mode 100644 index 00000000..242d622e --- /dev/null +++ b/service-exposure/rapps-webhook.go @@ -0,0 +1,185 @@ +// - +// ========================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 +// +// 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 ( + "encoding/json" + "errors" + "flag" + "fmt" + "io/ioutil" + "k8s.io/api/admission/v1beta1" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/serializer" + "log" + "net/http" +) + +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 +} + +type patchOperation struct { + Op string `json:"op"` + Path string `json:"path"` + Value interface{} `json:"value,omitempty"` +} + +var parameters ServerParameters + +var ( + universalDeserializer = serializer.NewCodecFactory(runtime.NewScheme()).UniversalDeserializer() +) + +func main() { + flag.StringVar(¶meters.port, "port", "8443", "Webhook server port.") + flag.StringVar(¶meters.certFile, "tlsCertFile", "/certs/tls.crt", "File containing the x509 certificate") + flag.StringVar(¶meters.keyFile, "tlsKeyFile", "/certs/tls.key", "File containing the x509 private key") + flag.StringVar(¶meters.hostPath, "hostPath", "/var/rapps/certs", "Host Path containing rapp cert files") + flag.Parse() + + http.HandleFunc("/inject-sidecar", HandleSideCarInjection) + log.Fatal(http.ListenAndServeTLS(":"+parameters.port, parameters.certFile, parameters.keyFile, nil)) +} + +func HandleSideCarInjection(w http.ResponseWriter, r *http.Request) { + + body, err := ioutil.ReadAll(r.Body) + err = ioutil.WriteFile("/tmp/request", body, 0644) + if err != nil { + panic(err.Error()) + } + + var admissionReviewReq v1beta1.AdmissionReview + + if _, _, err := universalDeserializer.Decode(body, nil, &admissionReviewReq); err != nil { + w.WriteHeader(http.StatusBadRequest) + fmt.Errorf("Could not deserialize request: %v", err) + } else if admissionReviewReq.Request == nil { + w.WriteHeader(http.StatusBadRequest) + errors.New("Malformed admission review - request is empty") + } + + fmt.Printf("Received Admission Review Request - Type: %v \t Event: %v \t Name: %v \n", + admissionReviewReq.Request.Kind, + admissionReviewReq.Request.Operation, + admissionReviewReq.Request.Name, + ) + + var pod v1.Pod + + err = json.Unmarshal(admissionReviewReq.Request.Object.Raw, &pod) + + if err != nil { + fmt.Errorf("Could not unmarshal pod from admission request: %v", err) + } + + var patches []patchOperation + + labels := pod.ObjectMeta.Labels + labels["sidecar-injection-webhook"] = "jwt-proxy" + + patches = append(patches, patchOperation{ + Op: "add", + Path: "/metadata/labels", + Value: labels, + }) + + var containers []v1.Container + containers = append(containers, pod.Spec.Containers...) + container := v1.Container{ + Name: "jwt-proxy", + Image: "ktimoney/rapps-jwt", + ImagePullPolicy: v1.PullIfNotPresent, + Ports: []v1.ContainerPort{ + { + Name: "http", + Protocol: v1.ProtocolTCP, + ContainerPort: 8888, + }, + }, + VolumeMounts: []v1.VolumeMount{ + { + Name: "certsdir", + MountPath: "/certs", + ReadOnly: true, + }, + }, + } + + containers = append(containers, container) + fmt.Println(containers) + + patches = append(patches, patchOperation{ + Op: "add", + Path: "/spec/containers", + 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, + }, + }, + } + volumes = append(volumes, volume) + fmt.Println(volumes) + + patches = append(patches, patchOperation{ + Op: "add", + Path: "/spec/volumes", + Value: volumes, + }) + fmt.Println(patches) + + patchBytes, err := json.Marshal(patches) + + if err != nil { + fmt.Errorf("Error occurred when trying to marshal JSON patch: %v", err) + } + + admissionReviewResponse := v1beta1.AdmissionReview{ + Response: &v1beta1.AdmissionResponse{ + UID: admissionReviewReq.Request.UID, + Allowed: true, + }, + } + + admissionReviewResponse.Response.Patch = patchBytes + + bytes, err := json.Marshal(&admissionReviewResponse) + if err != nil { + fmt.Errorf("Error occurred when trying to marshal Aadmission Review response: %v", err) + } + + w.Write(bytes) + +} diff --git a/service-exposure/rapps-webhook.yaml b/service-exposure/rapps-webhook.yaml new file mode 100644 index 00000000..1b513176 --- /dev/null +++ b/service-exposure/rapps-webhook.yaml @@ -0,0 +1,115 @@ +# +# ============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========================================================= +# +--- +############################################################ +# TLS certificate for OPA admission controller. +############################################################ +apiVersion: v1 +kind: Secret +metadata: + name: webhook-cert + namespace: default +type: Opaque +data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVMekNDQXhlZ0F3SUJBZ0lVUTZFV1pIK3RQdTk0WmEzeWp6STFFbGNBcTdrd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1RURUxNQWtHQTFVRUJoTUNTVVV4RURBT0JnTlZCQWdUQjFkbFltaHZiMnN4RHpBTkJnTlZCQWNUQmtSMQpZbXhwYmpFTU1Bb0dBMVVFQ2hNRFJWTlVNUTB3Q3dZRFZRUUxFd1JQY21GdU1CNFhEVEl5TURreU1EQTRNRGt3Ck1Gb1hEVFF5TURreE5UQTRNRGt3TUZvd1RURUxNQWtHQTFVRUJoTUNTVVV4RURBT0JnTlZCQWdUQjFkbFltaHYKYjJzeER6QU5CZ05WQkFjVEJrUjFZbXhwYmpFTU1Bb0dBMVVFQ2hNRFJWTlVNUTB3Q3dZRFZRUUxFd1JQY21GdQpNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQW0zdUdEYnVwQkhjV3lockZ1TjdrCkFRN3hZeW05NGVjcjJZOE5mbHFtTDU2NG9YTzlic09uaG85czVtLzdiNUFTUlZ4aTJlUFltTGg4ZmlxL3hZaDgKZEo5M3ErWm8vZU9zUElER3BFTlBCQjhqL3AwNHkyckJwN2IwZk5PdTQ2dFB1YUNMRzR6ZEZoSkgyWU5FbGxnbQpqYWtvTThPcTBCZGYrejJlSWlBYWYxQTN1bTY0czJaR1pnR2hSdnhkYW1zVFhLNm1hNFd4OXBhMHlkdC93dnJtCm1SUThWOUlDNDhOS2QveTBpUmh3M2pRNzZUb05NWWFiUk9LTE1jNjMwVXZtL2NuV0dZNjZNeFVoeEhLUWY4SE4KTGp3U1VGRzlWYmVBYXQwcktoSkw2L21Eb3dpbzFRaXhoK25lazRXdm41cGFiZzlLakRWUnRja0NXS0toeUhFTAovUUlEQVFBQm80SUJCVENDQVFFd0RnWURWUjBQQVFIL0JBUURBZ1dnTUIwR0ExVWRKUVFXTUJRR0NDc0dBUVVGCkJ3TUJCZ2dyQmdFRkJRY0RBakFNQmdOVkhSTUJBZjhFQWpBQU1CMEdBMVVkRGdRV0JCVGFTU1luVDA3L3ZDMmsKTDk0NmhmRVFzemN6b1RDQm9nWURWUjBSQklHYU1JR1hnaDVxZDNRdGNISnZlSGt0WVdSdGFYTnphVzl1TFdOdgpiblJ5YjJ4c1pYS0NPR3AzZEMxd2NtOTRlUzFoWkcxcGMzTnBiMjR0WTI5dWRISnZiR3hsY2k1a1pXWmhkV3gwCkxuTjJZeTVqYkhWemRHVnlMbXh2WTJGc2dpcHFkM1F0Y0hKdmVIa3RZV1J0YVhOemFXOXVMV052Ym5SeWIyeHMKWlhJdVpHVm1ZWFZzZEM1emRtT0NDV3h2WTJGc2FHOXpkSWNFZndBQUFUQU5CZ2txaGtpRzl3MEJBUXNGQUFPQwpBUUVBU0EzV1VmbG9Ia0NIaU1TZ2VQSkVaUXdHN0tZSnJUQjByS25tYmFFSzloZnNveWlXcWs2WTl5RjFsMFVFCjBvQTdiRUZBRWh6S0VkcGxXOHRrQ1ZwaGxQZ0FBbktPU0dhYlVUam9KYURsWDBDRS9oNE5RaTlmOHVKT0ZkaVEKc3JRcXZCZWthUzNOd2ZIVHBTdFRnSEs1bEovMjUxenJ1VVZ0VTBERWpIQ1BYUks2S09uUmlNYktCSFBDNmoybQpmV29zdC9NYWJhbUlvOHlIdjZXaHNTbXhDODlEZ1BXa3RXM01QOXFEUjlTVWQzWk5BdWZKU0hEeDkxWEN6L2JpClRFZjRrRE1GRnVsMW5UUzBXTlhVUEV1MnR0SWxmMDBsZS91VHVNVUxOZ3V3Umlmc2tPSmFJcUVBc2NxWmtxdEMKQW5EMGdnV01QMEpCL1k1eGZTM1ZPLzBPVVE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== + tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBbTN1R0RidXBCSGNXeWhyRnVON2tBUTd4WXltOTRlY3IyWThOZmxxbUw1NjRvWE85CmJzT25obzlzNW0vN2I1QVNSVnhpMmVQWW1MaDhmaXEveFloOGRKOTNxK1pvL2VPc1BJREdwRU5QQkI4ai9wMDQKeTJyQnA3YjBmTk91NDZ0UHVhQ0xHNHpkRmhKSDJZTkVsbGdtamFrb004T3EwQmRmK3oyZUlpQWFmMUEzdW02NApzMlpHWmdHaFJ2eGRhbXNUWEs2bWE0V3g5cGEweWR0L3d2cm1tUlE4VjlJQzQ4TktkL3kwaVJodzNqUTc2VG9OCk1ZYWJST0tMTWM2MzBVdm0vY25XR1k2Nk14VWh4SEtRZjhITkxqd1NVRkc5VmJlQWF0MHJLaEpMNi9tRG93aW8KMVFpeGgrbmVrNFd2bjVwYWJnOUtqRFZSdGNrQ1dLS2h5SEVML1FJREFRQUJBb0lCQUFhWm9jRW5mQzlDVnVkUgpaNTlIWnVwY2xnYWRtUC9qN2txWDlmeXRJR3paRWdGWWhtd1RSaU5DSjE5STFhV1F1aFhUckNhUHMzd1lLTUM2ClU5V3d5NGV2MVVhb3kwQXJ6LzNwZ1lVcmpra2dnVWlucCtlS3FwblIvR0xvSVg1c29UL0IvdVcyZnhRV3hwSUgKTG53clZjZWhyS0UxNXlSYU9hclNuTW5hRHdYa2N1ZlF4enFLR1E2aFZZUGFGSk9JUXJQWmZXei84b2ZpWThBbwprSlZjSG1uOTZNLzZxRWhTbVZHcmc5M1BtYjJkUmNyc2hDblRCY2g5dDNuNnV4WGxKcmx0dG9lR1piTDB3Y3M0Ck9Wc2NDY043NEx3OGozSkdXeDg2QmVLT3ZORlJKajh3ajdxcGhsTW5hNjUxTmd6UE41eXlIR1IzWVhXU1lxSFcKMW9BRFFva0NnWUVBekN5ZlJjRTdMMEtsVGY4VmQ5cjZpRjhwcktBV0xSeXRZQTJjUUdSYWZHZlZYenJ4TnNBRwpGVlZlL2RabzVRa0I5NjlkKzRudDV5OUQzMWhYUGRNa0w0UWlWaHBuSE5zSzg4RVpuVXkxTkRBVCtJdEorY0VmCi9UaHRKa2Z4VGVXb1p6cmhIU1kxb2theFpzMWJ6bFpUNzFMTnBRTUs3bDBzc2hCVUdreklCME1DZ1lFQXd2TGcKcHhlWEtMMHVxNExWQVdMVEtyb2ttdmdIOG80QWpiekZLUnVTUTdlL3pVdXNVbVlZdFdMMEVqcGZXSTdNdXhrUwpjZUJiRmduakl5S3lGbDZTQ1BPaCs0N09EdlRNMDBpTGdoTnpOalVJbkE5RGJCV2ZURFVYd3ZHSDdhUGJ1OEVuCjFFUW1Wc2l5bC80SG1nK055WWJ5Q29WU1ZPZEJrQit1d0VYeFM3OENnWUFGOE9wMWlpamh1Q3U5T0VYMHBkK1MKWmtwOUptOWV3cTNjMUtpT1N4MUM3M2FLL2RrVkFjTnJqWDlsSFg4UjR4QTJsOWpCUUFNM0xlM29xdFpuQ3lUTAphU25pbllRUWwrTWFzcXkvSWdOSDBIcFVTaUZON2l1ekg1ZzFlL1J1a3RjeW9jajVJeXArWFZZK0tvMllWSFMrCnl3Y0czUzdOUHRMVkg1cUM1V2NRcHdLQmdBYUp5c3NQMlh2K1RGQm9ST2lVL2V3UzdpTmNhamZTVjJacGpGdEMKbDNjNTlHN1lPT0ZTbDBXT0dnMTZjN1F1cGVNb2hodlhvSFp1d25WdE5uZlZtQ1JBdDVBT1RBN29XdTVESXBxcwpPRkw3R0Z6VGpqbFR5Rkh2L2VvRjI3ODJuYW9BWW11V0ZZc1hsQlhRNlVSYmZTL2pITDhKbGFkUFVqMlpNbTAwCmExRlZBb0dBVlRReHNXNDJXWkRIQVExZC8vSzNXYTBQS0dWWmhHbkYxNFQzUlB3VDVDcGVKbmhhQ0QvVzh1VFQKSHBUUi8ySTY2RFQ5UjI2SlZReERoemNaa3dmR3krQVNoQ3BSb2FJZERGdjI1TS9EVGoxT2dUMUZpNm91ZjlzMgpVdFdkSEo1NlJ3cUhuRlptcnB2T3V4bGgwUWdQUHo3ZTg3dzBJMlpJQi9tRzY0Y2NHMlk9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg== +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: webhook-app + namespace: default +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: webhook-app +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + name: webhook-app + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: jwt-proxy-admission-controller-deployment + namespace: default + labels: + app: jwt-proxy-admission-controller +spec: + selector: + matchLabels: + app: jwt-proxy-admission-controller + template: + metadata: + labels: + app: jwt-proxy-admission-controller + version: v1 + spec: + serviceAccountName: webhook-app + containers: + - name: jwt-proxy-admission-controller + image: ktimoney/rapps-webhook + imagePullPolicy: IfNotPresent + command: ["/app/rapps-webhook"] + args: [ + "-port", "8443", + "-tlsCertFile", "/certs/tls.crt", + "-tlsKeyFile", "/certs/tls.key", + "-hostPath", "/var/rapps/certs" + ] + ports: + - containerPort: 8443 + resources: + limits: + memory: 256Mi + cpu: "250m" + requests: + memory: 128Mi + cpu: "80m" + volumeMounts: + - readOnly: true + mountPath: /certs + name: webhook-cert + volumes: + - name: webhook-cert + secret: + secretName: webhook-cert + replicas: 1 +--- +apiVersion: v1 +kind: Service +metadata: + name: jwt-proxy-admission-controller + namespace: default +spec: + selector: + app: jwt-proxy-admission-controller + ports: + - protocol: TCP + port: 443 + targetPort: 8443 + nodePort: 30570 + type: NodePort +--- diff --git a/service-exposure/rp_test.sh b/service-exposure/rp_test.sh index c1d07a28..c26f9aef 100644 --- a/service-exposure/rp_test.sh +++ b/service-exposure/rp_test.sh @@ -30,23 +30,14 @@ REFRESH_TOKEN="" function get_token { - local prefix="${1}" + local prefix="${1}" url="http://192.168.49.2:31560/auth/realms" - # echo $url TOKEN=$(curl -s -X POST $url/provider/protocol/openid-connect/token -H \ "Content-Type: application/x-www-form-urlencoded" -d client_secret=OwTCeahULA21G5TfEVMLG1iMloGiyH3i \ - -d 'grant_type=client_credentials' -d client_id=provider-cli) + -d 'grant_type=client_credentials' -d client_id=provider-cli) echo "TOKEN: $TOKEN" ACCESS_TOKEN=$(echo $TOKEN | jq -r '.access_token') - #echo "ACCESS_TOKEN: $ACCESS_TOKEN" REFRESH_TOKEN=$(echo $TOKEN | jq -r '.refresh_token') - #echo "REFRESH_TOKEN: $REFRESH_TOKEN" - # TOKEN2=$(curl -s -X POST $url/provider/protocol/openid-connect/token -H \ - # "Content-Type: application/x-www-form-urlencoded" -d client_secret= \ - # -d refresh_token=$REFRESH_TOKEN \ - # -d 'grant_type=refresh_token' -d client_id=provider-cli) - #echo "TOKEN2 = $TOKEN2" - #ACCESS_TOKEN="" echo $ACCESS_TOKEN } diff --git a/service-exposure/start_pods.sh b/service-exposure/start_pods.sh index f4281c98..f1dc5155 100644 --- a/service-exposure/start_pods.sh +++ b/service-exposure/start_pods.sh @@ -18,8 +18,31 @@ # SPDX-License-Identifier: Apache-2.0 # ============LICENSE_END========================================================= # +export host=$(minikube ip) -kubectl create -f chartmuseum.yaml +echo "Deploying applications..." +echo "-------------------------" +kubectl create -f chartmuseum.yaml kubectl create -f rapps-keycloak-mgr.yaml kubectl create -f rapps-istio-mgr.yaml kubectl create -f rapps-helm-installer.yaml +kubectl create -f rapps-webhook.yaml + +echo "" +echo "Waiting for pods to start..." +echo "----------------------------" +kubectl wait deployment -n default chartmuseum-deployment --for=condition=available --timeout=90s +kubectl wait deployment -n default rapps-keycloak-mgr-deployment --for=condition=available --timeout=90s +kubectl wait deployment -n default rapps-istio-mgr-deployment --for=condition=available --timeout=90s +kubectl wait deployment -n default rapps-helm-installer-deployment --for=condition=available --timeout=90s +kubectl wait deployment -n default jwt-proxy-admission-controller-deployment --for=condition=available --timeout=90s + +echo "" +echo "Configure sidecar injection..." +echo "----------------------------" +kubectl create -f MutatingWebhookConfiguration.yaml + +echo "" +echo "Checking pod status..." +echo "----------------------" +kubectl get pods -n default diff --git a/service-exposure/stop_pods.sh b/service-exposure/stop_pods.sh index 86f22efd..fa67b13f 100644 --- a/service-exposure/stop_pods.sh +++ b/service-exposure/stop_pods.sh @@ -19,7 +19,22 @@ # ============LICENSE_END========================================================= # +export host=$(minikube ip) + +echo "Undeploying applications..." +echo "---------------------------" +curl http://$host:31570/uninstall?chart=rapp-helloworld-invoker1 +echo "" +sleep 2 +curl http://$host:31570/uninstall?chart=rapp-helloworld-invoker2 +echo "" +sleep 2 +curl http://$host:31570/uninstall?chart=rapp-helloworld-provider +echo "" + kubectl delete -f rapps-helm-installer.yaml kubectl delete -f rapps-istio-mgr.yaml kubectl delete -f rapps-keycloak-mgr.yaml kubectl delete -f chartmuseum.yaml +kubectl delete -f rapps-webhook.yaml +kubectl delete -f MutatingWebhookConfiguration.yaml diff --git a/service-exposure/templates/AuthorizationPolicy-template.txt b/service-exposure/templates/AuthorizationPolicy-template.txt new file mode 100644 index 00000000..0899475c --- /dev/null +++ b/service-exposure/templates/AuthorizationPolicy-template.txt @@ -0,0 +1,40 @@ +# +# ============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: "security.istio.io/v1beta1" +kind: "AuthorizationPolicy" +metadata: + name: "{{.Name}}-policy" + namespace: {{.Namespace}} +spec: + selector: + matchLabels: + app.kubernetes.io/instance: {{.Name}} + action: ALLOW + rules: + - from: + - source: + requestPrincipals: ["http://istio-ingressgateway.istio-system:80/auth/realms/{{.Realm}}/"] + - to: + - operation: + methods: ["{{.Method}}"] + paths: ["/{{.Name}}"] + when: + - key: request.auth.claims[clientRole] + values: ["{{.Role}}"] diff --git a/service-exposure/templates/EnvoyFilter-template.txt b/service-exposure/templates/EnvoyFilter-template.txt new file mode 100644 index 00000000..f023fa63 --- /dev/null +++ b/service-exposure/templates/EnvoyFilter-template.txt @@ -0,0 +1,90 @@ +# +# ============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}} +spec: + workloadSelector: + labels: + app.kubernetes.io/name: {{.Name}} + configPatches: + # The first patch adds the lua filter to the listener/http connection manager + - applyTo: HTTP_FILTER + match: + context: SIDECAR_OUTBOUND + listener: + filterChain: + filter: + name: "envoy.filters.network.http_connection_manager" + subFilter: + name: "envoy.filters.http.router" + patch: + operation: INSERT_BEFORE + value: # lua filter specification + name: envoy.lua + typed_config: + "@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua" + inlineCode: | + function envoy_on_request(request_handle) + local uri = request_handle:headers():get(":path") + local method = request_handle:headers():get(":method") + if (method ~= "POST" and uri ~= "/auth/realms/{{.Realm}}/protocol/openid-connect/token") + then + -- Make an HTTP call to an upstream host with the following headers, body, and timeout. + local headers, body = request_handle:httpCall( + "jwt_cluster", + { + [":method"] = "GET", + [":path"] = "/token", + [":authority"] = "jwt-proxy", + ["realm"] = "{{.Realm}}", + ["client"] = "{{.Client}}", + ["authenticator"] = "{{.Authenticator}}", + ["ns"] = "{{.Namespace}}" + }, + "jwt call", + 5000) + if (headers["authorization"] ~= nil) + then + request_handle:headers():add("authorization", headers["authorization"]) + end + end + end + - applyTo: CLUSTER + match: + context: SIDECAR_OUTBOUND + patch: + operation: ADD + value: # cluster specification + name: jwt_cluster + type: STRICT_DNS + connect_timeout: 60s + lb_policy: ROUND_ROBIN + load_assignment: + cluster_name: jwt_cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 0.0.0.0 + port_value: 8888 diff --git a/service-exposure/templates/Gateway-template.txt b/service-exposure/templates/Gateway-template.txt new file mode 100644 index 00000000..594a6272 --- /dev/null +++ b/service-exposure/templates/Gateway-template.txt @@ -0,0 +1,34 @@ +# +# ============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/v1beta1 +kind: Gateway +metadata: + name: nonrtric-istio-{{.Name}}-gateway + namespace: {{.Namespace}} +spec: + selector: + istio: ingressgateway # use Istio gateway implementation + servers: + - port: + number: 80 + name: http + protocol: HTTP + hosts: + - "*" diff --git a/service-exposure/templates/RequestAuthentication-template.txt b/service-exposure/templates/RequestAuthentication-template.txt new file mode 100644 index 00000000..5fbdbbbc --- /dev/null +++ b/service-exposure/templates/RequestAuthentication-template.txt @@ -0,0 +1,31 @@ +# +# ============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: security.istio.io/v1beta1 +kind: RequestAuthentication +metadata: + name: "jwt-{{.Name}}" + namespace: {{.Namespace}} +spec: + selector: + matchLabels: + app.kubernetes.io/instance: {{.Name}} + jwtRules: + - issuer: "http://istio-ingressgateway.istio-system:80/auth/realms/{{.Realm}}" + jwksUri: "http://keycloak.default:8080/auth/realms/{{.Realm}}/protocol/openid-connect/certs" diff --git a/service-exposure/templates/VirtualService-template.txt b/service-exposure/templates/VirtualService-template.txt new file mode 100644 index 00000000..93be9cdc --- /dev/null +++ b/service-exposure/templates/VirtualService-template.txt @@ -0,0 +1,39 @@ +# +# ============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/v1beta1 +kind: VirtualService +metadata: + name: nonrtric-istio-{{.Name}}-vs + namespace: {{.Namespace}} +spec: + hosts: + - "*" + gateways: + - nonrtric-istio-{{.Name}}-gateway + http: + - name: "{{.Name}}-routes" + match: + - uri: + prefix: "/{{.Name}}" + route: + - destination: + port: + number: 80 + host: {{.Name}}.{{.Namespace}}.svc.cluster.local diff --git a/service-exposure/undeploy_rapp.sh b/service-exposure/undeploy_rapp.sh new file mode 100644 index 00000000..5e37e178 --- /dev/null +++ b/service-exposure/undeploy_rapp.sh @@ -0,0 +1,36 @@ +#!/bin/sh +# +# ============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========================================================= +# +export host=$(minikube ip) + +if [ -z "$1" ] + then + echo "No argument supplied" + exit 1 +fi + +rapp=$1 + +curl http://$host:31570/uninstall?chart=$rapp +echo "" + +kubectl wait deployment -n istio-nonrtric $rapp --for=delete --timeout=90s + +kubectl get pods -n istio-nonrtric diff --git a/service-exposure/utils/generatejwt/generatejwt.go b/service-exposure/utils/generatejwt/generatejwt.go new file mode 100644 index 00000000..7157f82d --- /dev/null +++ b/service-exposure/utils/generatejwt/generatejwt.go @@ -0,0 +1,184 @@ +// - +// ========================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 +// +// 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 generatejwt + +import ( + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "fmt" + "github.com/dgrijalva/jwt-go" + "io/ioutil" + "log" + "time" +) + +type JWT struct { + privateKey []byte + publicKey []byte +} + +func NewJWT(privateKey []byte, publicKey []byte) JWT { + return JWT{ + privateKey: privateKey, + publicKey: publicKey, + } +} + +func readFile(file string) []byte { + key, err := ioutil.ReadFile(file) + if err != nil { + log.Fatalln(err) + } + return key +} + +func (j JWT) createWithKey(ttl time.Duration, content interface{}, client, realm string) (string, error) { + key, err := jwt.ParseRSAPrivateKeyFromPEM(j.privateKey) + if err != nil { + return "", fmt.Errorf("create: parse key: %w", err) + } + + now := time.Now().UTC() + + claims := make(jwt.MapClaims) + claims["dat"] = content // Our custom data. + claims["exp"] = now.Add(ttl).Unix() // The expiration time after which the token must be disregarded. + claims["iat"] = now.Unix() // The time at which the token was issued. + claims["nbf"] = now.Unix() // The time before which the token must be disregarded. + claims["jti"] = "myJWTId" + fmt.Sprint(now.UnixNano()) + claims["sub"] = client + claims["iss"] = client + claims["aud"] = realm + + token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims) + tokenString, err := token.SignedString(key) + if err != nil { + return "", fmt.Errorf("create: sign token: %w", err) + } + + return tokenString, nil +} + +func createWithSecret(ttl time.Duration, content interface{}, client, realm, secret string) (string, error) { + now := time.Now().UTC() + + claims := make(jwt.MapClaims) + claims["dat"] = content // Our custom data. + claims["exp"] = now.Add(ttl).Unix() // The expiration time after which the token must be disregarded. + claims["iat"] = now.Unix() // The time at which the token was issued. + claims["nbf"] = now.Unix() // The time before which the token must be disregarded. + claims["jti"] = "myJWTId" + fmt.Sprint(now.UnixNano()) + claims["sub"] = client + claims["iss"] = client + claims["aud"] = realm + + token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(secret)) + if err != nil { + return "", fmt.Errorf("create: sign token: %w", err) + } + + return token, nil +} + +func (j JWT) Validate(token string) (interface{}, error) { + key, err := jwt.ParseRSAPublicKeyFromPEM(j.publicKey) + if err != nil { + return "", fmt.Errorf("validate: parse key: %w", err) + } + + tok, err := jwt.Parse(token, func(jwtToken *jwt.Token) (interface{}, error) { + if _, ok := jwtToken.Method.(*jwt.SigningMethodRSA); !ok { + return nil, fmt.Errorf("unexpected method: %s", jwtToken.Header["alg"]) + } + + return key, nil + }) + if err != nil { + return nil, fmt.Errorf("validate: %w", err) + } + + claims, ok := tok.Claims.(jwt.MapClaims) + if !ok || !tok.Valid { + return nil, fmt.Errorf("validate: invalid") + } + + return claims["dat"], nil +} + +func createPublicKeyFromPrivateKey(privkey_bytes []byte) []byte { + block, _ := pem.Decode([]byte(privkey_bytes)) + var privateKey *rsa.PrivateKey + pkcs1, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + pkcs8, err := x509.ParsePKCS8PrivateKey(block.Bytes) + privateKey = pkcs8.(*rsa.PrivateKey) + if err != nil { + log.Fatal(err) + } + } else { + privateKey = pkcs1 + } + + publicKey := &privateKey.PublicKey + + pubkey_bytes, err := x509.MarshalPKIXPublicKey(publicKey) + if err != nil { + log.Fatal(err) + } + + pubkey_pem := pem.EncodeToMemory( + &pem.Block{ + Type: "PUBLIC KEY", + Bytes: pubkey_bytes, + }, + ) + return pubkey_pem +} + +func CreateJWT(privateKeyFile, secret, client, realm string) string { + if secret == "" { + prvKey := readFile(privateKeyFile) + pubKey := createPublicKeyFromPrivateKey(prvKey) + + jwtToken := NewJWT(prvKey, pubKey) + + // 1. Create a new JWT token. + tok, err := jwtToken.createWithKey(time.Hour, "Can be anything", client, realm) + if err != nil { + log.Fatalln(err) + } + + // 2. Validate an existing JWT token. + _, err = jwtToken.Validate(tok) + if err != nil { + log.Fatalln(err) + } + return tok + } else { + // 1. Create a new JWT token. + tok, err := createWithSecret(time.Hour, "Can be anything", client, realm, secret) + if err != nil { + log.Fatalln(err) + } + return tok + } + +} diff --git a/service-exposure/utils/pemtojwks/pemtojwks.go b/service-exposure/utils/pemtojwks/pemtojwks.go new file mode 100644 index 00000000..6317843d --- /dev/null +++ b/service-exposure/utils/pemtojwks/pemtojwks.go @@ -0,0 +1,133 @@ +// - +// ========================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 +// +// 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 pemtojwks + +import ( + "crypto/rsa" + "crypto/sha1" + "crypto/x509" + "encoding/base64" + "encoding/json" + "encoding/pem" + "fmt" + "golang.org/x/crypto/ssh" + "io/ioutil" + "math/big" +) + +type Jwks struct { + Keys []Key `json:"keys"` +} +type Key struct { + Kid string `json:"kid,omitempty"` + Kty string `json:"kty"` + Use string `json:"use"` + N string `json:"n"` + E string `json:"e"` + X5c []string `json:"x5c"` + X5t string `json:"x5t"` +} + +func getKeyFromPrivate(key []byte) *rsa.PublicKey { + parsed, err := ssh.ParseRawPrivateKey(key) + if err != nil { + fmt.Println(err) + } + + // Convert back to an *rsa.PrivateKey + privateKey := parsed.(*rsa.PrivateKey) + + publicKey := &privateKey.PublicKey + return publicKey +} + +func getKeyFromPublic(key []byte) *rsa.PublicKey { + pubPem, _ := pem.Decode(key) + + parsed, err := x509.ParsePKIXPublicKey(pubPem.Bytes) + if err != nil { + fmt.Println("Unable to parse RSA public key", err) + } + + // Convert back to an *rsa.PublicKey + publicKey := parsed.(*rsa.PublicKey) + + return publicKey +} + +func getCert(cert []byte) *x509.Certificate { + certPem, _ := pem.Decode(cert) + if certPem == nil { + panic("Failed to parse pem file") + } + + // pass cert bytes + certificate, err := x509.ParseCertificate(certPem.Bytes) + if err != nil { + fmt.Println("Unable to parse Certificate", err) + } + + return certificate +} + +func getPublicKeyFromCert(cert_bytes []byte) *rsa.PublicKey { + block, _ := pem.Decode([]byte(cert_bytes)) + var cert *x509.Certificate + cert, _ = x509.ParseCertificate(block.Bytes) + rsaPublicKey := cert.PublicKey.(*rsa.PublicKey) + + return rsaPublicKey +} + +func CreateJWKS(certFile string) string { + var publicKey *rsa.PublicKey + + cert, err := ioutil.ReadFile(certFile) + if err != nil { + fmt.Println(err) + } + publicKey = getPublicKeyFromCert(cert) + + certificate := getCert(cert) + // generate fingerprint with sha1 + // you can also use md5, sha256, etc. + fingerprint := sha1.Sum(certificate.Raw) + + + jwksKey := Key{ + Kid: "SIGNING_KEY", + Kty: "RSA", + Use: "sig", + N: base64.RawStdEncoding.EncodeToString(publicKey.N.Bytes()), + E: base64.RawStdEncoding.EncodeToString(big.NewInt(int64(publicKey.E)).Bytes()), + X5c: []string{base64.RawStdEncoding.EncodeToString(certificate.Raw)}, + X5t: base64.RawStdEncoding.EncodeToString(fingerprint[:]), + } + jwksKeys := []Key{jwksKey} + jwks := Jwks{jwksKeys} + + jwksJson, err := json.Marshal(jwks) + if err != nil { + fmt.Println(err) + return err.Error() + } + return string(jwksJson) + +}