Prerequisites:
kubectl(obviously)just: alternative tomake(cargo install just)kindcloud-provider-kindhelmfilekyvernoCLI
Create kind cluster:
just kube-cluster-createInstall Istio (ambient mode) with Kyverno and Falco:
just kube-cluster-initNote
You will need to start a new shell where you will run cloud-provider-kind so that the LoadBalancer, the one created by the Gateway, gets an external IP.
just helm-demo-app-installTo get the external IP of our service:
export INGRESS_HOST=$(kubectl get -n my-sentences-demo-app gateway sentences-demo-app-gw -o jsonpath='{.status.addresses[0].value}')
echo $INGRESS_HOSTAnd then you try the app with:
curl http://$INGRESS_HOST/You can check in Kiali, that the mTLS is enabled in our namespace:
kubectl port-forward -n istio-system svc/kiali 20001:20001Go to http://localhost:20001/, and see the app in Kiali:
And the traffic graph (you need to send some requests to the app to see it):
If you want to test all the Kyverno policies more easily, you can use:
just kyverno-valid-tests kyverno-invalid-tests kyverno-mutate-testsYou can access the Kyverno Policy Reporter with:
kubectl port-forward -n kyverno svc/policy-reporter-ui 8082:8080And go to http://localhost:8082/.
- ClusterPolicy
check-image: check that images, coming fromghcr.io/mfernd/k8s-security*, has a signature (bysigstore/cosign)
# invalid pod image (does not have a signature)
kyverno apply charts/kubernetes_yaml/kyverno/check-images.cpol.yaml --resource charts/kubernetes_yaml/kyverno/tests/invalid-pod-image.yaml
# valid pod image (latest has a signature)
kyverno apply charts/kubernetes_yaml/kyverno/check-images.cpol.yaml --resource charts/kubernetes_yaml/kyverno/tests/valid-pod.yaml- ClusterPolicy
require-requests-limits: check that all pods, in namespaces beginning bymy*orapp*, haverequestsandlimitsresources defined
# invalid pod (does not have requests and limits)
kyverno apply charts/kubernetes_yaml/kyverno/require-requests-limits.cpol.yaml --resource charts/kubernetes_yaml/kyverno/tests/invalid-pod-requests-limits.yaml
# valid pod (has requests and limits)
kyverno apply charts/kubernetes_yaml/kyverno/require-requests-limits.cpol.yaml --resource charts/kubernetes_yaml/kyverno/tests/valid-pod.yaml- ClusterPolicy
restrict-nodeport: prevent the use ofNodePortservices
# invalid service (has NodePort)
kyverno apply charts/kubernetes_yaml/kyverno/restrict-nodeport.cpol.yaml --resource charts/kubernetes_yaml/kyverno/tests/invalid-svc-nodeport.yaml
# valid service (to test that ClusterIP, at least, works)
kyverno apply charts/kubernetes_yaml/kyverno/restrict-nodeport.cpol.yaml --resource charts/kubernetes_yaml/kyverno/tests/valid-svc-clusterip.yaml- ClusterPolicy
add-default-securitycontext: add a defaultsecurityContextto all pods
kyverno apply charts/kubernetes_yaml/kyverno/add-default-securitycontext.cpol.yaml --resource charts/kubernetes_yaml/kyverno/tests/mutate-pod-security-context.yamlWe see that the pod has been patched with a securityContext.
Applying 3 policy rule(s) to 1 resource(s)...
mutate policy add-default-securitycontext applied to default/Pod/nginx:
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: default
spec:
containers:
- image: nginx:1.27.3
name: nginx
ports:
- containerPort: 80
securityContext:
fsGroup: 2000
runAsGroup: 3000
runAsNonRoot: true
runAsUser: 1000
---
pass: 1, fail: 0, warn: 0, error: 0, skip: 0
- ClusterPolicy
add-istio-mesh-namespace: add theistio.io/dataplane-mode: ambientlabel to all namespace beginning bymy*orapp*
kyverno apply charts/kubernetes_yaml/kyverno/add-istio-mesh-ns.cpol.yaml --resource charts/kubernetes_yaml/kyverno/tests/mutate-ns-istio-mesh.yamlWe see that the namespace has our new istio label istio.io/dataplane-mode: ambient.
Applying 1 policy rule(s) to 1 resource(s)...
mutate policy add-istio-mesh-namespace applied to default/Namespace/my-namespace:
apiVersion: v1
kind: Namespace
metadata:
labels:
istio.io/dataplane-mode: ambient
name: my-namespace
namespace: default
---
pass: 1, fail: 0, warn: 0, error: 0, skip: 0
We will try to trigger the rule Read sensitive file untrusted:
just falco-trigger-ruleExpected result:
{
"hostname":"mfernd-k8s-security-control-plane",
"output":"17:55:52.489810605: Warning Sensitive file opened for reading by non-trusted program (file=/etc/shadow gparent=<NA> ggparent=<NA> gggparent=<NA> evt_type=openat user=root user_uid=0 user_loginuid=-1 process=cat proc_exepath=/usr/bin/cat parent=containerd-shim command=cat /etc/shadow terminal=34816 container_id=edabb60361fc container_image=docker.io/library/nginx container_image_tag=1.27 container_name=bayrou k8s_ns=trigger-falco-rule k8s_pod_name=bayrou)",
"output_fields":{
"container.id":"edabb60361fc",
"container.image.repository":"docker.io/library/nginx",
"container.image.tag":"1.27",
"container.name":"bayrou",
"evt.time":1734890152489810605,
"evt.type":"openat",
"fd.name":"/etc/shadow",
"k8s.ns.name":"trigger-falco-rule",
"k8s.pod.name":"bayrou",
"proc.aname[2]":"<NA>",
"proc.aname[3]":null,
"proc.aname[4]":null,
"proc.cmdline":"cat /etc/shadow",
"proc.exepath":"/usr/bin/cat",
"proc.name":"cat",
"proc.pname":"containerd-shim",
"proc.tty":34816,
"user.loginuid":-1,
"user.name":"root",
"user.uid":0
},
"priority":"Warning",
"rule":"Read sensitive file untrusted",
"source":"syscall",
"tags":[
"T1555",
"container",
"filesystem",
"host",
"maturity_stable",
"mitre_credential_access"
],
"time":"2024-12-22T17:55:52.489810605Z"
}Tip
You can see the rule in the default falco_rules.yaml.
kubectl port-forward -n falco svc/falco-falcosidekick-ui 2802:2802Go to http://localhost:2802/ (credentials are admin/admin).
just upjust devWith mprocs to execute services in parallel.
Source: crates/common/
| Env Var | Description | Default |
|---|---|---|
APP_HOST |
Application host | "0.0.0.0" |
APP_PORT |
Application port | 3000 (or 80 in Docker) |
Used by all other modules.
Source: crates/aggregator-svc/
| Env Var | Description | Default |
|---|---|---|
APP_WORKERS_CONFIG |
Used to know where to get words (see workers_config.example.toml) |
N/A |
Source: crates/provider-svc/
| Env Var | Description | Default |
|---|---|---|
APP_PROVIDER_KIND |
Define the provider type of the instance (see struct WordKind for possible values) |
N/A |


