Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Your bundle is now protected with basic authentication.


JWT Injection

To enable automatic include a jwt token in our rapp request we need to enable some k8s objects:

1) MutatingWebhookConfiguration to inject jwt retrieval pod as a sidecar. The MutatingWebhookConfiguration uses a pod mutating serive to alter our pod and add the new sidecar.


Code Block
languagetext
titleMutating Webhook
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"
        "strconv"
)

type ServerParameters struct {
        port     int    // webhook server port
        certFile string // path to the x509 cert
        keyFile  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.IntVar(¶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.Parse()

        http.HandleFunc("/inject-sidecar", HandleSideCarInjection)
        log.Fatal(http.ListenAndServeTLS(":"+strconv.Itoa(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: "/var/rapps/certs",
                                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)

}


rapps-webhook.yaml

2) Envoyfilter to update request header with jwt token.


Code Block
languagetext
titleEnvoy Filter
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: RAPP-NAME-outbound-filter
  namespace: RAPP-NS
spec:
  workloadSelector:
    labels:
      app.kubernetes.io/name: RAPP-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 path ~= "/auth/realms/REALM-NAME/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-NAME",
                 ["client"] = "CLIENT-NAME"
                },
               "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