First version of service exposure
Issue-ID: NONRTRIC-759
Signed-off-by: ktimoney <kevin.timoney@est.tech>
Change-Id: I45635153d3bd84d8c548654e4a27d10ed0d5e7aa
diff --git a/service-exposure/rapps-istio-mgr.go b/service-exposure/rapps-istio-mgr.go
new file mode 100644
index 0000000..5a0761b
--- /dev/null
+++ b/service-exposure/rapps-istio-mgr.go
@@ -0,0 +1,350 @@
+// -
+// ========================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"
+ "context"
+ "fmt"
+ netv1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1"
+ secv1beta1 "istio.io/client-go/pkg/apis/security/v1beta1"
+ versioned "istio.io/client-go/pkg/clientset/versioned"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ k8Yaml "k8s.io/apimachinery/pkg/util/yaml"
+ "k8s.io/client-go/rest"
+ clientcmd "k8s.io/client-go/tools/clientcmd"
+ "log"
+ "net/http"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+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"]
+`
+
+func connectToK8s() *versioned.Clientset {
+ config, err := rest.InClusterConfig()
+ if err != nil {
+ // fallback to kubeconfig
+ home, exists := os.LookupEnv("HOME")
+ if !exists {
+ home = "/root"
+ }
+
+ kubeconfig := filepath.Join(home, ".kube", "config")
+ if envvar := os.Getenv("KUBECONFIG"); len(envvar) > 0 {
+ kubeconfig = envvar
+ }
+ config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
+ if err != nil {
+ log.Fatalln("failed to create K8s config")
+ }
+ }
+
+ ic, err := versioned.NewForConfig(config)
+ if err != nil {
+ log.Fatalf("Failed to create istio client: %s", err)
+ }
+
+ return ic
+}
+
+func createGateway(clientset *versioned.Clientset, appName string) (string, error) {
+ gtClient := clientset.NetworkingV1beta1().Gateways(NAMESPACE)
+ manifest := strings.Replace(gatewayManifest, "RAPP-NAME", appName, -1)
+ manifest = strings.Replace(manifest, "RAPP-NS", NAMESPACE, -1)
+
+ gt := &netv1beta1.Gateway{}
+ dec := k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(manifest)), 1000)
+
+ if err := dec.Decode(>); err != nil {
+ return "", err
+ }
+
+ result, err := gtClient.Create(context.TODO(), gt, metav1.CreateOptions{})
+
+ if err != nil {
+ return "", err
+ }
+
+ fmt.Printf("Create Gateway %s \n", result.GetName())
+ return result.GetName(), nil
+}
+
+func createVirtualService(clientset *versioned.Clientset, appName string) (string, error) {
+ vsClient := clientset.NetworkingV1beta1().VirtualServices(NAMESPACE)
+ manifest := strings.Replace(virtualServiceManifest, "RAPP-NAME", appName, -1)
+ manifest = strings.Replace(manifest, "RAPP-NS", NAMESPACE, -1)
+
+ vs := &netv1beta1.VirtualService{}
+ dec := k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(manifest)), 1000)
+
+ if err := dec.Decode(&vs); err != nil {
+ return "", err
+ }
+
+ result, err := vsClient.Create(context.TODO(), vs, metav1.CreateOptions{})
+
+ if err != nil {
+ return "", err
+ }
+
+ fmt.Printf("Create Virtual Service %s \n", result.GetName())
+ return result.GetName(), nil
+}
+
+func createRequestAuthentication(clientset *versioned.Clientset, appName, realmName string) (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)
+
+ ra := &secv1beta1.RequestAuthentication{}
+ dec := k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(manifest)), 1000)
+
+ if err := dec.Decode(&ra); err != nil {
+ return "", err
+ }
+
+ result, err := raClient.Create(context.TODO(), ra, metav1.CreateOptions{})
+
+ if err != nil {
+ return "", err
+ }
+
+ fmt.Printf("Create Request Authentication %s \n", result.GetName())
+ return result.GetName(), nil
+}
+
+func createAuthorizationPolicy(clientset *versioned.Clientset, appName, realmName, roleName, methodName string) (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)
+
+ ap := &secv1beta1.AuthorizationPolicy{}
+ dec := k8Yaml.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(manifest)), 1000)
+
+ if err := dec.Decode(&ap); err != nil {
+ return "", err
+ }
+
+ result, err := apClient.Create(context.TODO(), ap, metav1.CreateOptions{})
+
+ if err != nil {
+ return "", err
+ }
+
+ fmt.Printf("Create Authorization Policy %s \n", result.GetName())
+ return result.GetName(), nil
+}
+
+func removeGateway(clientset *versioned.Clientset, appName string) {
+ gtClient := clientset.NetworkingV1beta1().Gateways(NAMESPACE)
+ err := gtClient.Delete(context.TODO(), "nonrtric-istio-"+appName+"-gateway", metav1.DeleteOptions{})
+ if err != nil {
+ fmt.Println(err)
+ } else {
+ fmt.Println("Deleted Gateway nonrtric-istio-" + appName + "-gateway")
+ }
+}
+
+func removeVirtualService(clientset *versioned.Clientset, appName string) {
+ vsClient := clientset.NetworkingV1beta1().VirtualServices(NAMESPACE)
+ err := vsClient.Delete(context.TODO(), "nonrtric-istio-"+appName+"-vs", metav1.DeleteOptions{})
+ if err != nil {
+ fmt.Println(err)
+ } else {
+ fmt.Println("Deleted VirtualServices nonrtric-istio-" + appName + "-vs")
+ }
+}
+
+func removeRequestAuthentication(clientset *versioned.Clientset, appName string) {
+ raClient := clientset.SecurityV1beta1().RequestAuthentications(NAMESPACE)
+ err := raClient.Delete(context.TODO(), "jwt-"+appName, metav1.DeleteOptions{})
+ if err != nil {
+ fmt.Println(err)
+ } else {
+ fmt.Println("Deleted RequestAuthentication jwt-" + appName)
+ }
+}
+
+func removeAuthorizationPolicy(clientset *versioned.Clientset, appName string) {
+ apClient := clientset.SecurityV1beta1().AuthorizationPolicies(NAMESPACE)
+ err := apClient.Delete(context.TODO(), appName+"-policy", metav1.DeleteOptions{})
+ if err != nil {
+ fmt.Println(err)
+ } else {
+ fmt.Println("Deleted AuthorizationPolicy " + appName + "-policy")
+ }
+}
+
+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")
+ var msg string
+ clientset := connectToK8s()
+ _, err := createGateway(clientset, appName)
+ if err != nil {
+ msg = err.Error()
+ fmt.Println(err.Error())
+ } else {
+ _, err := createVirtualService(clientset, appName)
+ if err != nil {
+ msg = err.Error()
+ fmt.Println(err.Error())
+ } else {
+ _, err := createRequestAuthentication(clientset, appName, realmName)
+ if err != nil {
+ msg = err.Error()
+ fmt.Println(err.Error())
+ } else {
+ _, err := createAuthorizationPolicy(clientset, appName, realmName, roleName, methodName)
+ if err != nil {
+ msg = err.Error()
+ fmt.Println(err.Error())
+ } else {
+ msg = "Istio rapp security setup successfully"
+ }
+ }
+ }
+ }
+
+ // 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")
+ clientset := connectToK8s()
+ removeAuthorizationPolicy(clientset, appName)
+ removeRequestAuthentication(clientset, appName)
+ removeVirtualService(clientset, appName)
+ removeGateway(clientset, appName)
+}
+
+func main() {
+ createIstioHandler := http.HandlerFunc(createIstioPolicy)
+ http.Handle("/create", createIstioHandler)
+ removeIstioHandler := http.HandlerFunc(removeIstioPolicy)
+ http.Handle("/remove", removeIstioHandler)
+ http.ListenAndServe(":9000", nil)
+}