...
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 | ||||
---|---|---|---|---|
| ||||
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)
} |
2) Envoyfilter to update request header with jwt token.
Code Block | ||||
---|---|---|---|---|
| ||||
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 |