+// -
+// ========================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)
+}