diff --git a/security/keylime-poc/Makefile b/security/keylime-poc/Makefile
new file mode 100644
index 00000000..0fe14743
--- /dev/null
+++ b/security/keylime-poc/Makefile
@@ -0,0 +1,28 @@
+# simple makefile to make life easy
+
+.PHONY: e2e run docker kind verify clean realclean
+.PHONY: compose
+
+e2e: run verify
+ "e2e done!"
+
+run: docker kind
+ echo "Done!"
+
+docker:
+ cd k8s; ./run_docker.sh
+
+kind:
+ cd k8s; ./run_kind.sh
+
+verify:
+ #cd k8s; ./verify.sh
+
+clean:
+ cd k8s; ./clean.sh
+
+realclean:
+ cd k8s; ./clean.sh realclean
+
+compose:
+ sudo apt install -y docker-compose-plugin
diff --git a/security/keylime-poc/README.md b/security/keylime-poc/README.md
new file mode 100644
index 00000000..d602c48e
--- /dev/null
+++ b/security/keylime-poc/README.md
@@ -0,0 +1,15 @@
+# Keylime POC
+
+
+## Docker Compose
+
+First part of POC is deploying Keylime services in
+[Docker Compose](compose/README.md). Setup is verified by a single Agent, backed
+by software TPM, also in Docker.
+
+## Kubernetes
+
+Second part of POC is deploying [Agent in k8s](k8s/README.md), fronted by
+Ingress/LoadBalancer, while the Verifier and Registrar sit in Docker Compose.
+This simulates the use-case of using existing non-K8s Keylime installation to
+measure nodes in K8s cluster.
diff --git a/security/keylime-poc/agent-with-swtpm/Dockerfile b/security/keylime-poc/agent-with-swtpm/Dockerfile
new file mode 100644
index 00000000..8a5e2616
--- /dev/null
+++ b/security/keylime-poc/agent-with-swtpm/Dockerfile
@@ -0,0 +1,85 @@
+##############################################################################
+# keylime TPM 2.0 Dockerfile
+#
+# This file is for automatic test running of Keylime and rust-keylime.
+# It is not recommended for use beyond testing scenarios.
+##############################################################################
+
+FROM quay.io/fedora/fedora
+
+# environment variables
+ARG BRANCH=master
+ENV container docker
+ENV HOME /root
+ENV KEYLIME_HOME ${HOME}/keylime
+ENV TPM_HOME ${HOME}/swtpm2
+COPY dbus-policy.conf /etc/dbus-1/system.d/
+
+# Packaged dependencies
+ENV PKGS_DEPS "automake \
+clang clang-devel \
+createrepo_c \
+czmq-devel \
+dbus \
+dbus-daemon \
+dbus-devel \
+dnf-plugins-core \
+efivar-devel \
+gcc \
+git \
+glib2-devel \
+glib2-static \
+gnulib \
+iproute \
+kmod \
+libarchive-devel \
+libselinux-python3 \
+libtool \
+libtpms \
+llvm llvm-devel \
+make \
+openssl-devel \
+pkg-config \
+procps \
+python3-cryptography \
+python3-dbus \
+python3-devel \
+python3-gpg \
+python3-pip \
+python3-requests \
+python3-setuptools \
+python3-sqlalchemy \
+python3-tornado \
+python3-virtualenv \
+python3-yaml \
+python3-zmq \
+redhat-rpm-config \
+rpm-build \
+rpm-sign \
+rust clippy cargo \
+swtpm \
+swtpm-tools \
+tpm2-abrmd \
+tpm2-tools \
+tpm2-tss \
+tpm2-tss-devel \
+uthash-devel \
+wget \
+which"
+
+ENV DEV_DEPS "strace openssl"
+
+RUN dnf makecache && \
+ dnf -y install $PKGS_DEPS $DEV_DEPS && \
+ dnf clean all && \
+ rm -rf /var/cache/dnf/*
+
+RUN git clone https://github.com/keylime/rust-keylime \
+ && cd rust-keylime \
+ && make all \
+ && make install \
+ && cd .. \
+ && rm -rf rust-keylime
+
+COPY start.sh /
+CMD ["/start.sh"]
diff --git a/security/keylime-poc/agent-with-swtpm/dbus-policy.conf b/security/keylime-poc/agent-with-swtpm/dbus-policy.conf
new file mode 100644
index 00000000..c8b0b903
--- /dev/null
+++ b/security/keylime-poc/agent-with-swtpm/dbus-policy.conf
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/security/keylime-poc/agent-with-swtpm/start.sh b/security/keylime-poc/agent-with-swtpm/start.sh
new file mode 100755
index 00000000..e48e0f39
--- /dev/null
+++ b/security/keylime-poc/agent-with-swtpm/start.sh
@@ -0,0 +1,57 @@
+#!/usr/bin/env bash
+
+set -eux
+
+KEYLIME=/var/lib/keylime
+TPMDIR=/tmp/tpmdir
+
+# configure swtpm2 and start it
+mkdir -p "${TPMDIR}"
+chown tss:tss "${TPMDIR}"
+chmod 750 "${TPMDIR}"
+
+swtpm_setup --tpm2 \
+ --tpmstate "${TPMDIR}" \
+ --createek --decryption --create-ek-cert \
+ --create-platform-cert \
+ --display || true
+swtpm socket --tpm2 \
+ --tpmstate dir="${TPMDIR}" \
+ --flags startup-clear \
+ --ctrl type=tcp,port=2322 \
+ --server type=tcp,port=2321 \
+ --daemon
+sleep 2
+
+# configure dbus for abmrd
+sudo rm -rf /var/run/dbus
+sudo mkdir /var/run/dbus
+sudo dbus-daemon --system
+
+# run abmrd
+tpm2-abrmd \
+ --logger=stdout \
+ --tcti=swtpm: \
+ --allow-root \
+ --flush-all \
+ &
+sleep 2
+
+# prep image for running agent as non-root
+useradd -s /sbin/nologin -g tss keylime || true
+
+chown keylime:tss "${KEYLIME}" "${KEYLIME}"/secure
+chmod 770 "${KEYLIME}" "${KEYLIME}"/secure
+cp "${KEYLIME}"/cv_ca/cacert.crt "${KEYLIME}"/secure/
+chown keylime:tss "${KEYLIME}"/secure/cacert.crt
+
+# make swtpm CA accessible to tenant to validate EK cert
+# and verify it to be sure we have it right to avoid issues down the road
+cat /var/lib/swtpm-localca/{issuercert,swtpm-localca-rootca-cert}.pem > "${KEYLIME}"/tpm_cert_store/swtpm_localca.pem
+tpm2_getekcertificate > "${KEYLIME}"/ek.bin
+openssl x509 -inform DER -in "${KEYLIME}"/ek.bin -out "${KEYLIME}"/ek.pem
+openssl verify -CAfile "${KEYLIME}"/tpm_cert_store/swtpm_localca.pem "${KEYLIME}"/ek.pem
+sleep 2
+
+# run agent
+keylime_agent
diff --git a/security/keylime-poc/compose/.gitignore b/security/keylime-poc/compose/.gitignore
new file mode 100644
index 00000000..92b72c46
--- /dev/null
+++ b/security/keylime-poc/compose/.gitignore
@@ -0,0 +1 @@
+allowlist.txt
diff --git a/security/keylime-poc/compose/README.md b/security/keylime-poc/compose/README.md
new file mode 100644
index 00000000..c41bb580
--- /dev/null
+++ b/security/keylime-poc/compose/README.md
@@ -0,0 +1,21 @@
+# Keylime in Docker Compose POC
+
+
+Make a Proof of Concept of [Keylime in Docker Compose](compose/README.md).
+
+This will also work as part of [Keylime Agent in k8s](../k8s/README.md) POC,
+as Keylime Verifier, Registrar and Tenant will be outside k8s, but Agent inside.
+This creates interesting problems to be solved as Agent traffic will need to
+flow via Ingress/LoadBalancer and it cannot be reached via IP.
+
+## Steps
+
+1. docker compose installed
+1. Keylime images built locally via upstream
+ [build_locally.sh](https://github.com/keylime/keylime/blob/master/docker/release/build_locally.sh)
+ (we need unreleased fixes from `master`).
+1. `docker compose up --build` to launch it
+1. `./tenant.sh -c add` to verify stack works and agent gets added to verifier,
+ with EK certificate from software TPM.
+
+After this, feel free to play with `./tenant.sh`.
diff --git a/security/keylime-poc/compose/agent.conf b/security/keylime-poc/compose/agent.conf
new file mode 100644
index 00000000..db9ce8ed
--- /dev/null
+++ b/security/keylime-poc/compose/agent.conf
@@ -0,0 +1,87 @@
+[agent]
+# The configuration file version
+version = "2.3"
+
+# The agent's UUID.
+uuid = "c47b9ea2-2bc2-461b-957b-e77dbcf35e5e"
+# uuid = "hash_ek"
+# uuid = "generate"
+
+# The keylime working directory. The default value is /var/lib/keylime
+keylime_dir = "/var/lib/keylime"
+
+# The size of the memory-backed tmpfs partition where Keylime stores crypto keys.
+# Use syntax that the 'mount' command would accept as a size parameter for tmpfs.
+# The default below sets it to 1 megabyte.
+secure_size = "1m"
+# run_as = "keylime:tss"
+
+# Enable mTLS communication between agent, verifier and tenant.
+# Details on why setting it to "false" is generally considered insecure can be found
+# on https://github.com/keylime/keylime/security/advisories/GHSA-2m39-75g9-ff5r
+agent_enable_mtls = "true"
+
+# The name of the file containing the Keylime agent TLS server private key.
+# This private key is used to serve the Keylime agent REST API
+# A new private key is generated in case it is not found.
+# If set as "default", the "server-private.pem" value is used.
+# If a relative path is set, it will be considered relative from the keylime_dir.
+# If an absolute path is set, it is used without change
+server_key = "secure/agent.key"
+
+# The name of the file containing the X509 certificate used as the Keylime agent
+# server TLS certificate.
+# This certificate must be self signed.
+# If set as "default", the "server-cert.crt" value is used
+# If a relative path is set, it will be considered relative from the keylime_dir.
+# If an absolute path is set, it is used without change.
+server_cert = "secure/agent.crt"
+
+# The CA that signs the client certificates of the tenant and verifier.
+# If set as "default" the "cv_ca/cacert.crt" value, relative from the
+# keylime_dir is used.
+# If a relative path is set, it will be considered relative from the keylime_dir.
+# If an absolute path is set, it is used without change.
+trusted_client_ca = "secure/cacert.crt"
+
+# The address and port of registrar server which agent communicate with
+registrar_ip = "127.0.0.2"
+registrar_port = 8890
+
+# tbd
+# The binding IP address and port for the agent server
+ip = "0.0.0.0"
+port = 9002
+
+# Address and port where the verifier and tenant can connect to reach the agent.
+# These keys are optional.
+contact_ip = "127.0.0.3"
+contact_port = 9002
+
+# Use this option to state the existing TPM ownerpassword.
+# This option should be set only when a password is set for the Endorsement
+# Hierarchy (e.g. via "tpm2_changeauth -c e").
+# In order to use a hex value for the password, use the prefix "hex:"
+# For example if tpm2_changeauth -c e "hex:00a1b2c3e4" has run, the config option
+# would be 'tpm_ownerpassword = "hex:00a1b2c3e4"'
+# If no password was set, keep the empty string "".
+tpm_ownerpassword = ""
+tpm_version = "2"
+
+# enc_keyname = "derived_tci_key"
+# dec_payload_file = "decrypted_payload"
+# extract_payload_zip = true
+# enable_revocation_notifications = false
+# revocation_actions_dir = "/usr/libexec/keylime"
+# revocation_cert = "default"
+# revocation_actions = ""
+# payload_script = "autorun.sh"
+# allow_payload_revocation_actions = true
+# tpm_hash_alg = "sha256"
+# tpm_encryption_alg = "rsa"
+# tpm_signing_alg = "rsassa"
+# ek_handle = "generate"
+# enable_iak_idevid = false
+# agent_data_path = ""
+# ima_ml_path = "default"
+# measuredboot_ml_path = "default"
diff --git a/security/keylime-poc/compose/compose.yml b/security/keylime-poc/compose/compose.yml
new file mode 100644
index 00000000..aadc20ed
--- /dev/null
+++ b/security/keylime-poc/compose/compose.yml
@@ -0,0 +1,70 @@
+services:
+ keylime-verifier:
+ image: keylime_verifier:latest # locally built
+ hostname: keylime-verifier
+ volumes:
+ - ./verifier.conf:/etc/keylime/verifier.conf:ro
+ - ./logging.conf:/etc/keylime/logging.conf:ro
+ - secure_volume:/var/lib/keylime
+ ports:
+ - "8881:8881"
+ user: root
+
+ keylime-registrar:
+ image: keylime_registrar:latest # locally built
+ hostname: keylime-registrar
+ depends_on:
+ - keylime-verifier
+ volumes:
+ - ./registrar.conf:/etc/keylime/registrar.conf:ro
+ - ./logging.conf:/etc/keylime/logging.conf:ro
+ - secure_volume:/var/lib/keylime
+ ports:
+ - "8891:8891"
+ - "8890:8890"
+ user: root
+ entrypoint: ["bash", "-c", "sleep 5; keylime_registrar"]
+
+ keylime-tenant:
+ image: keylime_tenant:latest # locally built
+ hostname: keylime-tenant
+ network_mode: host
+ depends_on:
+ - keylime-verifier
+ - keylime-registrar
+ volumes:
+ - ./tenant.conf:/etc/keylime/tenant.conf:ro
+ - ./logging.conf:/etc/keylime/logging.conf:ro
+ - secure_volume:/var/lib/keylime
+ - ./allowlist.txt:/tmp/allowlist.txt:ro
+ user: root
+ entrypoint: ["bash", "-c", "tail -f /dev/null"]
+
+ keylime-agent:
+ build:
+ # image: quay.io/keylime/keylime_agent:master + swtpm config
+ context: ../agent-with-swtpm
+ dockerfile: ../agent-with-swtpm/Dockerfile
+ hostname: keylime-agent
+ network_mode: host
+ user: root
+ depends_on:
+ - keylime-verifier
+ - keylime-registrar
+ environment:
+ - TPM2TOOLS_TCTI=tabrmd:bus_type=system
+ - TCTI=tabrmd:bus_type=system
+ - RUST_LOG=keylime_agent=debug
+ volumes:
+ - /sys/kernel/security:/sys/kernel/security:ro
+ - ./agent.conf:/etc/keylime/agent.conf:ro
+ - ./target/debug/:/rust-keylime
+ - secure_volume:/var/lib/keylime
+ - agent_volume:/var/lib/keylime/secure
+
+volumes:
+ secure_volume:
+ agent_volume:
+ driver_opts:
+ type: tmpfs
+ device: tmpfs
diff --git a/security/keylime-poc/compose/logging.conf b/security/keylime-poc/compose/logging.conf
new file mode 100644
index 00000000..9470c7f6
--- /dev/null
+++ b/security/keylime-poc/compose/logging.conf
@@ -0,0 +1,32 @@
+# Keylime logging configuration
+
+[logging]
+version = 2.3
+
+[loggers]
+keys = root,keylime
+
+[handlers]
+keys = consoleHandler
+
+[formatters]
+keys = formatter
+
+[formatter_formatter]
+format = %(asctime)s.%(msecs)03d - %(name)s - %(levelname)s - %(message)s
+datefmt = %Y-%m-%d %H:%M:%S
+
+[logger_root]
+level = DEBUG
+handlers = consoleHandler
+
+[handler_consoleHandler]
+class = StreamHandler
+level = DEBUG
+formatter = formatter
+args = (sys.stdout,)
+
+[logger_keylime]
+level = DEBUG
+qualname = keylime
+handlers =
diff --git a/security/keylime-poc/compose/registrar.conf b/security/keylime-poc/compose/registrar.conf
new file mode 100644
index 00000000..37cbc1dd
--- /dev/null
+++ b/security/keylime-poc/compose/registrar.conf
@@ -0,0 +1,120 @@
+[jenkins@bm08b-jenkins keylime_config]$ cat registrar.conf
+# Keylime registrar configuration
+[registrar]
+
+# The configuration file version number
+version = 2.3
+
+# The binding address and port for the registrar server
+ip = "0.0.0.0"
+port = 8890
+tls_port = 8891
+
+# The 'tls_dir' option define the directory where the keys and certificates are
+# stored.
+#
+# If set as 'generate', automatically generate a CA, keys, and certificates for
+# the registrar server in the /var/lib/keylime/reg_ca directory, if not present.
+#
+# The 'server_key', 'server_cert', and 'trusted_client_ca' options should all be
+# set with the 'default' keyword when 'generate' keyword is set for 'tls_dir'.
+#
+# If set as 'default', share the files with the verifier by using the
+# 'var/lib/keylime/cv_ca' directory, which should contain the files indicated by
+# the 'server_key', 'server_cert', and 'trusted_client_ca' options.
+tls_dir = default
+
+# The name of the file containing the Keylime registrar server private key.
+# The file should be stored in the directory set in the 'tls_dir' option.
+# This private key is used to serve the Keylime registrar REST API
+#
+# If set as 'default', the 'server-private.pem' value is used.
+server_key = default
+
+# Set the password used to decrypt the private key file.
+# If 'tls_dir = generate', this password will also be used to protect the
+# generated server private key.
+# If left empty, the private key will not be encrypted.
+server_key_password =
+
+# The name of the file containing the Keylime registrar server certificate.
+# The file should be stored in the directory set in the 'tls_dir' option.
+#
+# If set as 'default', the 'server-cert.crt' value is used.
+server_cert = default
+
+# The list of trusted client CA certificates.
+# The files in the list should be stored in the directory set in the 'tls_dir'
+# option.
+#
+# If set as 'default', the value is set as '[cacert.crt]'
+trusted_client_ca = default
+
+# Database URL Configuration
+# See this document https://keylime.readthedocs.io/en/latest/installation.html#database-support
+# for instructions on using different database configurations.
+#
+# An example of database_url value for using sqlite:
+# sqlite:////var/lib/keylime/reg_data.sqlite
+# An example of database_url value for using mysql:
+# mysql+pymysql://keylime:keylime@keylime_db:[port]/registrar?charset=utf8
+#
+# If set as 'sqlite' keyword, will use the configuration set by the file located
+# at "/var/lib/keylime/reg_data.sqlite".
+database_url = sqlite
+
+# Limits for DB connection pool size in sqlalchemy
+# (https://docs.sqlalchemy.org/en/14/core/pooling.html#api-documentation-available-pool-implementations)
+database_pool_sz_ovfl = 5,10
+
+# Whether to automatically update the DB schema using alembic
+auto_migrate_db = True
+
+# Durable Attestation is currently marked as an experimental feature
+# In order to enable Durable Attestation, an "adapter" for a Persistent data Store
+# (time-series like database) needs to be specified. Some example adapters can be
+# found under "da/examples" so, for instance
+# "durable_attestation_import = keylime.da.examples.redis.py"
+# could be used to interact with a Redis (Persistent data Store)
+durable_attestation_import =
+
+# If an adapter for Durable Attestation was specified, then the URL for a Persistent Store
+# needs to be specified here. A second optional URL could be specified, for a
+# Rekor Transparency Log. A third additional URL could be specified, pointing to a
+# Time Stamp Authority (TSA), compatible with RFC3161. Additionally, one might need to
+# specify a path containing certificates required by the stores or TSA. Continuing with
+# the above example, the following values could be assigned to the parameters:
+# "persistent_store_url=redis://127.0.0.1:6379?db=10&password=/root/redis.auth&prefix=myda"
+# "transparency_log_url=http://127.0.0.1:3000"
+# "time_stamp_authority_url=http://127.0.0.1:2020"
+# "time_stamp_authority_certs_path=~/mycerts/tsa_cert1.pem"
+persistent_store_url =
+transparency_log_url =
+time_stamp_authority_url =
+time_stamp_authority_certs_path =
+
+# If Durable Attestation was enabled, which requires a Persistent Store URL
+# to be specified, the two following parameters control the format and enconding
+# of the stored attestation artifacts (defaults "json" for format and "" for encoding)
+persistent_store_format = json
+persistent_store_encoding =
+
+# If Durable Attestation was enabled with a Transparency Log URL was specified,
+# the digest algorithm for signatures is controlled by this parameter (default "sha256")
+transparency_log_sign_algo = sha256
+
+# If Durable Attestation was enabled with a Transparency Log URL was specified,
+# a keylime administrator can specify some agent attributes (including attestation
+# artifacts, such as quotes and logs) to be signed by the registrar. The use of "all"
+# will result in the whole "package" (agent + artifacts) being signed and leaving it empty
+# will mean no signing should be done.
+signed_attributes = ek_tpm,aik_tpm,ekcert
+
+# What TPM-based identity is allowed to be used to register agents.
+# The options "default" and "iak_idevid" will only allow registration with IAK and IDevID if python cryptography is version 38.0.0 or higher.
+# The following options are accepted:
+# "default": either an EK or IAK and IDevID may be used. In the case that cryptography version is <38.0.0 only EK will be used
+# "ek_cert_or_iak_idevid": this is equivalent to default
+# "ek_cert": only allow agents to use an EK to register
+# "iak_idevid": only allow agents with an IAK and IDevID to register
+tpm_identity = default
diff --git a/security/keylime-poc/compose/tenant.conf b/security/keylime-poc/compose/tenant.conf
new file mode 100644
index 00000000..1c4857f3
--- /dev/null
+++ b/security/keylime-poc/compose/tenant.conf
@@ -0,0 +1,130 @@
+# Keylime tenant configuration
+[tenant]
+
+# The configuration file version number
+version = 2.3
+
+# The verifier IP address and port
+verifier_ip = 127.0.0.1
+verifier_port = 8881
+
+# The registrar IP address and port
+registrar_ip = 127.0.0.2
+registrar_port = 8891
+
+# The 'tls_dir' option define the directory where the keys and certificates are
+# stored.
+#
+# If set as 'default', share the files with the verifier by using the
+# 'var/lib/keylime/cv_ca', which should contain the files indicated by the
+# 'client_key', 'client_cert', and 'trusted_server_ca' options.
+tls_dir = default
+
+# Enable mTLS communication between agent, verifier and tenant.
+# Details on why setting it to "False" is generally considered insecure can be found
+# on https://github.com/keylime/keylime/security/advisories/GHSA-2m39-75g9-ff5r
+enable_agent_mtls = True
+
+# The name of the file containing the Keylime tenant client private key.
+# The file should be stored in the directory set in the 'tls_dir' option.
+# This private key is used by the Keylime tenant to connect to the other
+# services using TLS.
+#
+# If set as 'default', the 'client-private.pem' value is used.
+client_key = default
+
+# Set the password used to encrypt the private key file.
+# If client_key is set as 'default', should match the password set in the
+# 'client_key_password' option in the verifier configuration file
+client_key_password =
+
+# The name of the file containing the Keylime tenant client certificate.
+# The file should be stored in the directory set in the 'tls_dir' option.
+# This certificate is used by the Keylime tenant to connect to the other
+# services using TLS.
+#
+# If set as 'default', the 'client-cert.crt' value is used.
+client_cert = default
+
+# The list of trusted server CA certificates.
+# The files in the list should be stored in the directory set in the 'tls_dir'
+# option.
+#
+# If set as 'default', the value is set as '[cacert.crt]'
+trusted_server_ca = default
+
+# Directory containing the EK CA certificates.
+# The EK certificate provided by the agent will be validated against the CAs
+# located in this directory.
+tpm_cert_store = /var/lib/keylime/tpm_cert_store
+
+# Maximum size of the payload in bytes. The value should match the 'secure_size'
+# option in the agent configuration
+max_payload_size = 1048576
+
+# List of hash algorithms used for PCRs
+# Accepted values: sha512, sha384, sha256, sha1
+accept_tpm_hash_algs = ['sha512', 'sha384', 'sha256']
+
+# List of encryption algorithms to use with the TPM
+# Accepted values: ecc, rsa
+accept_tpm_encryption_algs = ['ecc', 'rsa']
+
+# List of signature algorithms to use
+# Accepted values: rsassa, rsapss, ecdsa, ecdaa, ecschnorr
+accept_tpm_signing_algs = ['ecschnorr', 'rsassa']
+
+# Wether or not to use an exponantial backoff algorithm for retries.
+exponential_backoff = True
+
+# Either how long to wait between failed attempts to communicate with the TPM
+# in seconds, or the base for the exponential backoff algorithm if enabled via
+# "exponential_backoff" option.
+# Floating point values are accepted.
+retry_interval = 2
+
+# Integer number of retries to communicate with the TPM before giving up.
+max_retries = 5
+
+# Request timeout in seconds.
+request_timeout = 60
+
+# Tell the tenant whether to require an EK certificate from the TPM.
+# If set to False the tenant will ignore EK certificates entirely.
+#
+# WARNING: SETTING THIS OPTION TO FALSE IS VERY DANGEROUS!!!
+#
+# If you disable this check, then you may not be talking to a real TPM.
+# All the security guarantees of Keylime rely upon the security of the EK
+# and the assumption that you are talking to a spec-compliant and honest TPM.
+
+# Some physical TPMs do not have EK certificates, so you may need to set
+# this to "False" for some deployments. If you do set it to "False", you
+# MUST use the 'ek_check_script' option below to specify a script that will
+# check the provided EK against a allowlist for the environment that has
+# been collected in a trustworthy way. For example, the cloud provider
+# might provide a signed list of EK public key hashes. Then you could write
+# an ek_check_script that checks the signature of the allowlist and then
+# compares the hash of the given EK with the allowlist.
+require_ek_cert = True
+
+# Optional script to execute to check the EK and/or EK certificate against a
+# allowlist or any other additional EK processing you want to do. Runs in
+# /var/lib/keylime. You call also specify an absolute path to the script.
+# Script should return 0 if the EK or EK certificate are valid. Any other
+# return value will invalidate the tenant quote check and prevent
+# bootstrapping a key.
+#
+# The various keys are passed to the script via environment variables:
+# EK - contains a PEM encoded version of the public EK
+# EK_CERT - contains a DER encoded EK certificate if one is available.
+# PROVKEYS - contains a json document containing EK, EKcert, and AIK from the
+# provider. EK and AIK are in PEM format. The EKcert is in base64 encoded
+# DER format.
+#
+# Set to blank to disable this check. See warning above if require_ek_cert
+# is "False".
+ek_check_script =
+
+# Path to file containing the measured boot reference state
+mb_refstate =
diff --git a/security/keylime-poc/compose/tenant.sh b/security/keylime-poc/compose/tenant.sh
new file mode 100755
index 00000000..7dece8fc
--- /dev/null
+++ b/security/keylime-poc/compose/tenant.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+# Run docker-compose up -d first to have infra in place
+
+set -eu
+
+# test with args "-c add" as that triggers basically the whole keylime/tpm chain
+docker exec \
+ -it \
+ --user root \
+ compose-keylime-tenant-1 \
+ keylime_tenant \
+ --uuid c47b9ea2-2bc2-461b-957b-e77dbcf35e5e \
+ "$@"
diff --git a/security/keylime-poc/compose/verifier.conf b/security/keylime-poc/compose/verifier.conf
new file mode 100644
index 00000000..d6daba7f
--- /dev/null
+++ b/security/keylime-poc/compose/verifier.conf
@@ -0,0 +1,252 @@
+# Keylime verifier configuration
+[verifier]
+
+# The configuration file version number
+version = 2.3
+
+# Unique identifier for the each verifier instances.
+uuid = default
+
+# The binding address and port for the verifier server
+ip = "0.0.0.0"
+port = 8881
+
+# The address and port of registrar server that the verifier communicates with
+registrar_ip = "127.0.0.2"
+registrar_port = 8891
+
+# Enable mTLS communication between agent, verifier and tenant.
+# Details on why setting it to "False" is generally considered insecure can be found
+# on https://github.com/keylime/keylime/security/advisories/GHSA-2m39-75g9-ff5r
+enable_agent_mtls = True
+
+# The 'tls_dir' option define the directory where the keys and certificates are
+# stored.
+#
+# If set as 'generate', automatically generate a CA, keys, and certificates for
+# the client and the server in the /var/lib/keylime/cv_ca directory, if not
+# present.
+#
+# The 'server_key', 'server_cert', 'client_key', 'client_cert',
+# 'trusted_client_ca', and 'trusted_server_ca' options should all be set with
+# the 'default' keyword when 'generate' keyword is set for 'tls_dir'.
+#
+# If set as 'default', the 'var/lib/keylime/cv_ca' directory is used, which
+# should contain the files indicated by the 'server_key', 'server_cert',
+# 'client_key', 'client_cert', 'trusted_client_ca', and 'trusted_server_ca'
+# options.
+tls_dir = generate
+
+# The name of the file containing the Keylime verifier server private key.
+# The file should be stored in the directory set in the 'tls_dir' option.
+# This private key is used to serve the Keylime verifier REST API
+#
+# If set as 'default', the 'server-private.pem' value is used.
+server_key = default
+
+# Set the password used to decrypt the server private key file.
+# If 'tls_dir = generate', this password will also be used to protect the
+# generated server private key.
+# If left empty, the private key will not be encrypted.
+server_key_password =
+
+# The name of the file containing the Keylime verifier server certificate.
+# The file should be stored in the directory set in the 'tls_dir' option.
+#
+# If set as 'default', the 'server-cert.crt' value is used.
+server_cert = default
+
+# The list of trusted client CA certificates.
+# The files in the list should be stored in the directory set in the 'tls_dir'
+# option.
+#
+# If set as 'default', the value is set as '[cacert.crt]'
+trusted_client_ca = default
+
+# The name of the file containing the Keylime verifier client private key.
+# The file should be stored in the directory set in the 'tls_dir' option.
+# This private key is used by the Keylime verifier to connect to the other
+# services using TLS.
+#
+# If set as 'default', the 'client-private.pem' value is used.
+client_key = default
+
+# Set the password used to decrypt the client private key file.
+# If 'tls_dir = generate', this password will also be used to protect the
+# generated client private key.
+# If left empty, the private key will not be encrypted.
+client_key_password =
+
+# The name of the file containing the Keylime verifier client certificate.
+# The file should be stored in the directory set in the 'tls_dir' option.
+# This certificate is used by the Keylime verifier to connect to the other
+# services using TLS.
+#
+# If set as 'default', the 'client-cert.crt' value is used.
+client_cert = default
+
+# The list of trusted server CA certificates.
+# The files in the list should be stored in the directory set in the 'tls_dir'
+# option.
+#
+# If set as 'default', the value is set as '[cacert.crt]'
+trusted_server_ca = default
+
+# Database URL Configuration
+# See this document https://keylime.readthedocs.io/en/latest/installation.html#database-support
+# for instructions on using different database configurations.
+#
+# An example of database_url value for using sqlite:
+# sqlite:////var/lib/keylime/cv_data.sqlite
+# An example of database_url value for using mysql:
+# mysql+pymysql://keylime:keylime@keylime_db:[port]/verifier?charset=utf8
+#
+# If set as 'sqlite' keyword, will use the configuration set by the file located
+# at "/var/lib/keylime/cv_data.sqlite".
+database_url = sqlite
+
+# Limits for DB connection pool size in sqlalchemy
+# (https://docs.sqlalchemy.org/en/14/core/pooling.html#api-documentation-available-pool-implementations)
+database_pool_sz_ovfl = 5,10
+
+# Whether to automatically update the DB schema using alembic
+auto_migrate_db = True
+
+# The number of worker processes to use for the cloud verifier.
+# Set to "0" to create one worker per processor.
+num_workers = 2
+
+# Wether or not to use an exponantial backoff algorithm for retries.
+exponential_backoff = True
+
+# Either how long to wait between failed attempts to connect to a cloud agent
+# in seconds, or the base for the exponential backoff algorithm.
+# Floating point values accepted here.
+retry_interval = 2
+
+# Number of retries to connect to an agent before giving up. Must be an integer.
+max_retries = 5
+
+# Time between integrity measurement checks, in seconds. If set to "0", checks
+# will done as fast as possible. Floating point values accepted here.
+quote_interval = 2
+
+# The verifier limits the size of upload payloads (allowlists) which defaults to
+# 100MB (104857600 bytes). This setting can be raised (or lowered) based on the
+# size of the actual payloads
+max_upload_size = 104857600
+
+# Timeout in seconds for requests made to agents
+request_timeout = 60.0
+
+# The name of the boot attestation policy to use in comparing a measured boot event log
+# with a measured boot reference state.
+# A policy is a Python object that `isinstance` of `keylime.elchecking.policies.Policy`
+# and was registered by calling `keylime.elchecking.policies.register`.
+# The keylime agent extracts the measured boot event log.
+# The verifier client specifies the measured boot reference state to use;
+# this is specified independently for each agent.
+# Depending on the policy, the same reference state may be usable with multiple agents.
+# The `accept-all` policy ignores the reference state and approves every log.
+measured_boot_policy_name = accept-all
+
+# This is a list of Python modules to dynamically load, for example to register
+# additional boot attestation policies.
+# Empty strings in the list are ignored.
+# A module here may be relative, in which case it is interpreted
+# relative to the keylime.elchecking package.
+# The default value for this config item is the empty list.
+measured_boot_imports = []
+
+# This is used to manage the number of times measure boot attestation
+# is done. In other words, it controls the number of times the call
+# to the measure boot policy engine is made to evaluate the boot log
+# against the policy specified.
+# Here are its possible values and number of bootlog evaluations.
+# once (default) : Bootlog evaluation will be done for only one time.
+# always : Bootlog evaluation will always be done (i.e. for unlimited times).
+measured_boot_evaluate = once
+
+# Severity labels for revocation events strictly ordered from least severity to
+# highest severtiy.
+severity_labels = ["info", "notice", "warning", "error", "critical", "alert", "emergency"]
+
+# Severity policy that matches different event_ids to the severity label.
+# The rules are evaluated from the beginning of the list and the first match is
+# used. The event_id can also be a regex. Default policy assigns the highest
+# severity to all events.
+severity_policy = [{"event_id": ".*", "severity_label" : "emergency"}]
+
+# If files are already opened when IMA tries to measure them this causes
+# a time of measure, time of use (ToMToU) error entry.
+# By default we ignore those entries and only print a warning.
+# Set to False to treat ToMToU entries as errors.
+ignore_tomtou_errors = False
+
+# Durable Attestation is currently marked as an experimental feature
+# In order to enable Durable Attestation, an "adapter" for a Persistent data Store
+# (time-series like database) needs to be specified. Some example adapters can be
+# found under "da/examples" so, for instance
+# "durable_attestation_import = keylime.da.examples.redis.py"
+# could be used to interact with a Redis (Persistent data Store)
+durable_attestation_import =
+
+# If an adapter for Durable Attestation was specified, then the URL for a Persistent Store
+# needs to be specified here. A second optional URL could be specified, for a
+# Rekor Transparency Log. A third additional URL could be specified, pointing to a
+# Time Stamp Authority (TSA), compatible with RFC3161. Additionally, one might need to
+# specify a path containing certificates required by the stores or TSA. Continuing with
+# the above example, the following values could be assigned to the parameters:
+# "persistent_store_url=redis://127.0.0.1:6379?db=10&password=/root/redis.auth&prefix=myda"
+# "transparency_log_url=http://127.0.0.1:3000"
+# "time_stamp_authority_url=http://127.0.0.1:2020"
+# "time_stamp_authority_certs_path=~/mycerts/tsa_cert1.pem"
+persistent_store_url =
+transparency_log_url =
+time_stamp_authority_url =
+time_stamp_authority_certs_path =
+
+# If Durable Attestation was enabled, which requires a Persistent Store URL
+# to be specified, the two following parameters control the format and enconding
+# of the stored attestation artifacts (defaults "json" for format and "" for encoding)
+persistent_store_format = json
+persistent_store_encoding =
+
+# If Durable Attestation was enabled with a Transparency Log URL was specified,
+# the digest algorithm for signatures is controlled by this parameter (default "sha256")
+transparency_log_sign_algo = sha256
+
+# If Durable Attestation was enabled with a Transparency Log URL was specified,
+# a keylime administrator can specify some agent attributes (including attestation
+# artifacts, such as quotes and logs) to be signed by the verifier. The use of "all"
+# will result in the whole "package" (agent + artifacts) being signed and leaving it empty
+# will mean no signing should be done.
+signed_attributes =
+
+# Require that allowlists are signed with a key passed via the tenant tool
+require_allow_list_signatures = False
+
+[revocations]
+
+# List of revocation notification methods to enable.
+#
+# Available methods are:
+#
+# "agent": Deliver notification directly to the agent via the REST
+# protocol.
+#
+# "zeromq": Enable the ZeroMQ based revocation notification method;
+# zmq_ip and zmq_port options must be set. Currently this only works if you are
+# using keylime-CA.
+#
+# "webhook": Send notification via webhook. The endpoint URL must be
+# configured with 'webhook_url' option. This can be used to notify other
+# systems that do not have a Keylime agent running.
+enabled_revocation_notifications = ['agent']
+
+# The binding address and port of the revocation notifier service via ZeroMQ.
+zmq_ip = 127.0.0.1
+zmq_port = 8992
+
+# Webhook url for revocation notifications.
+webhook_url =
diff --git a/security/keylime-poc/k8s/README.md b/security/keylime-poc/k8s/README.md
new file mode 100644
index 00000000..6fa57999
--- /dev/null
+++ b/security/keylime-poc/k8s/README.md
@@ -0,0 +1,217 @@
+# Keylime POC
+
+
+WIP: NOTHING IS EXPECTED WORK HERE YET!
+
+## Kubernetes
+
+Make a Proof of Concept of Keylime in k8s.
+
+This POC is needed as the concept of having Keylime Tenant/Verifier/Registrar
+outside K8s cluster, but Keylime Agent in K8s cluster and being accessed via
+Ingress/LoadBalancer IP, is something Keylime maintainers did not think
+originally as a use-case. This has several issues with the current design, and
+while there is a proposal/ study for changing from "pull model" to "push
+model", it is miles away and this POC tries to find out the minimal changes
+needed to make the current model work for this use case.
+
+## POC requirements
+
+Since Keylime is about using TPM and being secure, we have some requirements:
+
+1. A machine/VM where we can setup a K8s cluster, and the pods can have access
+ to TPM.
+1. We need the TPM public certificates for said machine, EK CA etc.
+1. Machine where we can run Keylime services/commands, Registrar, Verifier and
+ Tenant. Preferably a second machine, so we can't "cheat".
+1. There needs to be connectivity between these. Preferably different networks,
+ again we don't want to "cheat".
+1. Agents have unique UUID that are pre-set, generated or tied to TPM EK.
+ There should be more than one machine in Cluster, so we have multiple Agents.
+
+For test/reproducibility purposes, we must consider some shortcuts:
+
+1. Use of single laptop, where the separation is happening either via
+ - VM (does it expose TPM properly)
+ - Containers (eventually all services, directly on Docker or in K8s cluster
+ will run in the same container runtime)
+1. Using TPM simulation software (might be good for reproducibility due
+ different manufacturers having different certs etc)
+1. Using only one machine in K8s cluster, and hence only one Agent (this may
+ lead of incorrect configs as there is only one Agent as is always "correct"
+ target)
+
+We have some availability for infra:
+
+1. Xerces (OpenStack) does not expose TPM to the VMs, so it is ruled out
+1. BML (Bare Metal Lab) has real machines that certainly would work, but those
+ are not for development (jump hosts) or run test payloads (actual servers).
+ Also access to those is not readily available to persons outside the ESJ
+ team.
+
+## POC setup
+
+As we're talking about POC, it is good to start with MVP, which here is:
+
+1. Use laptop as hardware for TPM access
+1. Use containers to make it reproducible
+1. Make all hardware related things configurable (EK certs, networks, ...)
+
+This leads to test setup looking like this.
+
+### Registrar in Docker
+
+1. Make Docker networking such it can be accessed by the K8s services (use
+ host network if needed)
+1. Run Keylime Registrar image in local Docker
+1. Run Keylime Verifier image in local Docker
+1. Run Keylime Tenant CLI in local Docker
+
+### Agent in K8s
+
+1. Run K8s cluster with Kind
+1. Install Keylime Agent in Kind
+1. Expose the Agent with LoadBalancer IP (if Kind supports that?)
+1. Make Ingress rule for a fake hostname (uuid0-31.uuid32-64.cluster.local)
+
+## POC plan
+
+With POC setup in place, we aim to achieve following steps.
+
+1. Agent registration to Registrar must work.
+
+ - Connectivity from Agent -> Registrar
+ - Agent must be configured with valid data (for later stages)
+ - `contact_ip`
+ - ...
+ - See Keylime Rust Agent
+ [contact_ip issue](https://github.com/keylime/rust-keylime/issues/848)
+
+1. Tenant will add Agent to Verifier with data from Registrar/command line.
+
+ - With use of Tenant CLI command, we add Agent to Verifier to be polled
+ - Verifier will query Registrar about Agent details, when it is supplied
+ with Agent UUID. This needs Tenant -> Verifier -> Registrar connectivity
+ and configuration to work.
+
+1. Verifier will query Agent for data
+
+ - Verifier now has all needed data to poll Agent. This is the tricky part as
+ it will use data input by Agent to Registrar, but needs to also travel
+ through LoadBalancer IP and Ingress rules to the correct Agent. Data must
+ only go to the Agent with matching UUID.
+ - NOTE: In the POC setup we have only one Agent, leading to the risk of
+ sending all Agent queries to this single Agent, allowing "cheating" in
+ K8s configurations as the sole Agent is the only target and UUID based rules
+ might "cheat".
+
+1. Agent sends data back
+
+ - Agent gets data from TPM and sends it to Verifier.
+ - Agent EK certs and Verifier EK certs need to match at this point.
+
+1. ...
+
+1. Profit!
+
+ There might be some additional steps, but this is how far we've come right now.
+ Stay tuned!
+
+## POC steps
+
+Actual steps to take to achieve [POC plan](#poc-plan). For this, a
+[Makefile](./makefile) and [helper scripts](./scripts/) are implemented in this
+repository.
+
+1. `make e2e` to run e2e, ie. `run` + `verify`
+1. `make run` to just setup everything, ie. `docker` + `kind`
+1. `make docker` to setup Verifier/Registrar correctly
+1. `make kind` to setup Agent in K8s cluster
+1. `make verify` to have Tenant sign up the Agent and trigger the verification
+1. `make clean` to clean everything up
+1. `make realclean` to clean everything up + remove any temporary files + images
+
+Whole POC e2e can be executed with `make e2e` and cleaned away with
+`make realclean`.
+
+For anything else, use `docker` and `kubectl` commands for digging into details,
+and when everything is running, use [scripts/run_tenant.sh](./scripts/run_tenant.sh)
+to issue Tenant commands.
+
+### Verifier and Registrar installation
+
+Running `make docker` sets up Verifier first, which generates certificates for
+mTLS in the shared directory `/tmp/keylime/cv_ca`, which needs to be mounted as
+`/var/lib/keylime/cv_ca` in all containers.
+
+See [scripts/run_docker.sh](./scripts/run_docker.sh) for the code.
+
+### Agent installation
+
+Running `make kind` sets up Kind cluster, then applies K8s manifests from `k8s`
+subdirectory to run Keylime Agent as DaemonSet.
+
+For nitty gritty details of the K8s installation of the agent, see
+[manifest generation details](#manifest-generation).
+
+See [scripts/run_kind.sh](./scripts/run_kind.sh) for the code.
+
+#### Manifest generation
+
+This K8s installation is created based on the
+[attestation-operator](https://github.com/keylime/attestation-operator) templates
+via following process (no need to repeat, resulting files are stored in this
+POC):
+
+1. Clone attestation-operator repo
+1. Run `make helm-keylime`
+1. Run `kind create cluster` to have something to deploy into
+1. Run `make helm-keylime-deploy` run deploy all components from the Helm chart
+1. Run `kubectl -n keylime get daemonset hhkl-keylime-agent -o yaml` to get a
+ manifest for Agent.
+1. Add some adaptation, especially around mTLS and cert mounts. Certs generated
+ by the Docker container must be made secrets etc
+
+### Verification with Tenant
+
+tbd
+
+## Issues found during POC
+
+1. [Agent contact_ip issue](https://github.com/keylime/rust-keylime/issues/848)
+
+## POC shortcomings
+
+As mentioned in [POC requirements](#poc-requirements) section, this POC for
+sake of simplicity has its limits. Following items could be implemented for
+more completeness, if time is not an issue.
+
+1. Two or mode nodes
+1. Two or more machines
+1. Adding TPM simulation (or removal of it)
+1. tbc
+
+## Extras
+
+Some extra more or less useful notes related to this POC.
+
+### Ubuntu 24.04 tools
+
+Per [Keylime documentation on TPM2 tools](https://keylime-docs.readthedocs.io/en/latest/installation.html#tpm-2-0-support)
+we need to install some tools manually.
+
+- `tpm2-tools` and `tpm2-tss-engine-tools` need to be installed (at least for
+ convenience)
+ - `sudo apt install tpm2-tools tpm2-tss-engine-tools`
+
+## References
+
+Collection of referenced docs, issues etc.
+
+1. [Keylime documentation](https://keylime-docs.readthedocs.io/en/latest/)
+1. [Attestation Operator](https://github.com/keylime/attestation-operator)
+ aka Keylime in K8s
+1. [Push model proxy doc](https://github.com/keylime/attestation-operator/blob/main/docs/push-model-proxy.md)
+1. [Agent contact_ip issue](https://github.com/keylime/rust-keylime/issues/848)
+1. [Slack discussion](https://cloud-native.slack.com/archives/C01ARE2QUTZ/p1727792733885549)
+1. [RedHat's Keylime docs](https://docs.redhat.com/de/documentation/red_hat_enterprise_linux/9/html/security_hardening/assembly_ensuring-system-integrity-with-keylime_security-hardening#configuring-keylime-agent_assembly_ensuring-system-integrity-with-keylime)
diff --git a/security/keylime-poc/k8s/clean.sh b/security/keylime-poc/k8s/clean.sh
new file mode 100755
index 00000000..ee5acc06
--- /dev/null
+++ b/security/keylime-poc/k8s/clean.sh
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+# shellcheck disable=SC1091
+
+# a clean up script for everything we've done
+
+set -eu
+
+# shall we do deep clean or not
+REALCLEAN="${1:-}"
+
+. config.sh
+
+remove_docker_instances()
+{
+ docker rm -f "${KEYLIME_VERIFIER_NAME}" || true
+ docker rm -f "${KEYLIME_REGISTRAR_NAME}" || true
+}
+
+remove_kind_cluster()
+{
+ kind delete cluster --name="${KIND_NAME}" || true
+}
+
+remove_temporary_files()
+{
+ sudo rm -rf "${KEYLIME_TMP_DIR:?}"
+}
+
+remove_docker_images()
+{
+ for image in "${KEYLIME_IMAGES[@]}"; do
+ docker image rm -f "${image}"
+ done
+}
+
+# main
+remove_docker_instances
+remove_kind_cluster
+remove_temporary_files
+
+if [[ -n "${REALCLEAN}" ]]; then
+ remove_docker_images
+fi
diff --git a/security/keylime-poc/k8s/config.sh b/security/keylime-poc/k8s/config.sh
new file mode 100644
index 00000000..a471eca7
--- /dev/null
+++ b/security/keylime-poc/k8s/config.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+# shellcheck disable=SC2034
+
+# This holds the shared configuration options for Keylime scripts
+
+# container runtime
+CONTAINER_RUNTIME="${CONTAINER_RUNTIME:-docker}"
+
+# registry and image details
+REGISTRY=quay.io/keylime
+TAG=latest
+
+KEYLIME_TENANT_IMAGE="${REGISTRY}/keylime_tenant:${TAG}"
+KEYLIME_REGISTRAR_IMAGE="${REGISTRY}/keylime_registrar:${TAG}"
+KEYLIME_VERIFIER_IMAGE="${REGISTRY}/keylime_verifier:${TAG}"
+KEYLIME_AGENT_IMAGE="${REGISTRY}/keylime_agent:${TAG}"
+
+declare -a KEYLIME_IMAGES=(
+ "${KEYLIME_TENANT_IMAGE}"
+ "${KEYLIME_REGISTRAR_IMAGE}"
+ "${KEYLIME_VERIFIER_IMAGE}"
+ "${KEYLIME_AGENT_IMAGE}"
+)
+
+# docker instance names
+KEYLIME_VERIFIER_NAME="keylime-verifier"
+KEYLIME_REGISTRAR_NAME="keylime-registrar"
+KEYLIME_TENANT_NAME="keylime-tenant"
+
+# shared directory name
+KEYLIME_TMP_DIR="/tmp/keylime"
+KEYLIME_INTERNAL_TLS_DIR="/var/lib/keylime/cv_ca"
+KEYLIME_EXTERNAL_TLS_DIR="${KEYLIME_TMP_DIR}/cv_ca"
+
+# kind setup
+KIND_NAME="keylime"
diff --git a/security/keylime-poc/k8s/keylime-agent.yaml b/security/keylime-poc/k8s/keylime-agent.yaml
new file mode 100644
index 00000000..309f3f47
--- /dev/null
+++ b/security/keylime-poc/k8s/keylime-agent.yaml
@@ -0,0 +1,169 @@
+apiVersion: apps/v1
+kind: DaemonSet
+metadata:
+ annotations:
+ deprecated.daemonset.template.generation: "1"
+ meta.helm.sh/release-name: hhkl
+ meta.helm.sh/release-namespace: keylime
+ creationTimestamp: "2024-10-24T11:42:03Z"
+ generation: 1
+ labels:
+ app.kubernetes.io/instance: hhkl
+ app.kubernetes.io/managed-by: Helm
+ app.kubernetes.io/name: keylime-agent
+ app.kubernetes.io/version: latest
+ helm.sh/chart: keylime-agent-0.1.0
+ name: hhkl-keylime-agent
+ namespace: keylime
+ resourceVersion: "3324"
+ uid: b271fc2c-af4d-4626-a981-2bda2bcf7fb2
+spec:
+ revisionHistoryLimit: 10
+ selector:
+ matchLabels:
+ app.kubernetes.io/instance: hhkl
+ app.kubernetes.io/name: keylime-agent
+ template:
+ metadata:
+ creationTimestamp: null
+ labels:
+ app.kubernetes.io/instance: hhkl
+ app.kubernetes.io/name: keylime-agent
+ spec:
+ containers:
+ - command:
+ - /bin/keylime_agent
+ env:
+ - name: KEYLIME_AGENT_CONTACT_IP
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: status.podIP
+ - name: KEYLIME_AGENT_SERVER_KEY
+ value: /etc/keylime/agent/server/certs/server-private.pem
+ - name: KEYLIME_AGENT_SERVER_CERT
+ value: /etc/keylime/agent/server/certs/server-cert.crt
+ - name: KEYLIME_AGENT_RUN_AS
+ - name: KEYLIME_AGENT_AGENT_DATA_PATH
+ value: /var/lib/keylime-persistent/agent_data.json
+ - name: RUST_LOG
+ value: keylime_agent=info
+ envFrom:
+ - configMapRef:
+ name: hhkl-keylime-config
+ image: quay.io/keylime/keylime_agent:latest
+ imagePullPolicy: IfNotPresent
+ name: keylime-agent
+ ports:
+ - containerPort: 9002
+ name: agent
+ protocol: TCP
+ resources: {}
+ securityContext:
+ privileged: true
+ terminationMessagePath: /dev/termination-log
+ terminationMessagePolicy: File
+ volumeMounts:
+ - mountPath: /etc/keylime/agent/server/certs
+ name: certs
+ readOnly: true
+ - mountPath: /var/lib/keylime/cv_ca/
+ name: cvca-certs
+ readOnly: true
+ - mountPath: /var/lib/keylime-persistent
+ name: persistent
+ - mountPath: /var/lib/keylime/secure
+ name: secure
+ readOnly: true
+ - mountPath: /tmp
+ name: tmpfs
+ - mountPath: /sys/kernel/security
+ name: securityfs
+ readOnly: true
+ dnsPolicy: ClusterFirst
+ initContainers:
+ - command:
+ - /bin/bash
+ - -c
+ - |
+ # fail if any of the commands fail
+ set -e
+
+ # copy the CA directory where we'll generate the certs from
+ cp -Rv /keylime/cv_ca /tmp/
+ cd /tmp/cv_ca
+
+ # now generate a new cert for the agent
+ # we need to do this on every start as the pod IP changes which is being used as the connection address
+ keylime_ca -d /tmp/cv_ca --command create --name "$POD_IP"
+
+ # copy them to the expected destinations
+ cp -v /tmp/cv_ca/${POD_IP}-private.pem /certs/server-private.pem
+ cp -v /tmp/cv_ca/${POD_IP}-cert.crt /certs/server-cert.crt
+ env:
+ - name: POD_IP
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: status.podIP
+ envFrom:
+ - configMapRef:
+ name: hhkl-keylime-config
+ - secretRef:
+ name: hhkl-keylime-ca-password
+ image: quay.io/keylime/keylime_tenant:latest
+ imagePullPolicy: IfNotPresent
+ name: keylime-agent-init
+ resources: {}
+ terminationMessagePath: /dev/termination-log
+ terminationMessagePolicy: File
+ volumeMounts:
+ - mountPath: /certs
+ name: certs
+ - mountPath: /keylime/cv_ca/
+ name: cvca-certs
+ readOnly: true
+ - mountPath: /tmp
+ name: tmpfs
+ restartPolicy: Always
+ schedulerName: default-scheduler
+ securityContext: {}
+ serviceAccount: hhkl-keylime-agent
+ serviceAccountName: hhkl-keylime-agent
+ terminationGracePeriodSeconds: 30
+ volumes:
+ - hostPath:
+ path: /sys/kernel/security
+ type: Directory
+ name: securityfs
+ - hostPath:
+ path: /var/lib/keylime
+ type: DirectoryOrCreate
+ name: persistent
+ - emptyDir:
+ medium: Memory
+ sizeLimit: 10Mi
+ name: secure
+ - emptyDir:
+ medium: Memory
+ sizeLimit: 10Mi
+ name: certs
+ - name: cvca-certs
+ secret:
+ defaultMode: 420
+ secretName: hhkl-keylime-certs
+ - emptyDir: {}
+ name: tmpfs
+ updateStrategy:
+ rollingUpdate:
+ maxSurge: 0
+ maxUnavailable: 1
+ type: RollingUpdate
+status:
+ currentNumberScheduled: 1
+ desiredNumberScheduled: 1
+ numberMisscheduled: 0
+ numberReady: 0
+ numberUnavailable: 1
+ observedGeneration: 1
+ updatedNumberScheduled: 1
diff --git a/security/keylime-poc/k8s/run_docker.sh b/security/keylime-poc/k8s/run_docker.sh
new file mode 100755
index 00000000..3d569e94
--- /dev/null
+++ b/security/keylime-poc/k8s/run_docker.sh
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+# shellcheck disable=SC1091
+
+# This script runs Keylime Verifier and Registrar as Docker containers.
+# It is complimented by the "run_tenant.sh" that runs Keylime Tenant in
+# container, sharing the config witht this script.
+
+set -eux
+
+. config.sh
+
+run_verifier()
+{
+ # we need to supply cv_ca directory for sharing mtls certs
+ docker run \
+ -d \
+ -v "${KEYLIME_EXTERNAL_TLS_DIR}":"${KEYLIME_INTERNAL_TLS_DIR}":rw \
+ --name "${KEYLIME_VERIFIER_NAME}" \
+ "${KEYLIME_VERIFIER_IMAGE}"
+}
+
+run_registrar()
+{
+ # we need to supply cv_ca directory for sharing mtls certs
+ docker run \
+ -d \
+ -v "${KEYLIME_EXTERNAL_TLS_DIR}":"${KEYLIME_INTERNAL_TLS_DIR}":rw \
+ --name "${KEYLIME_REGISTRAR_NAME}" \
+ "${KEYLIME_REGISTRAR_IMAGE}"
+}
+
+
+# main
+run_verifier
+run_registrar
diff --git a/security/keylime-poc/k8s/run_kind.sh b/security/keylime-poc/k8s/run_kind.sh
new file mode 100755
index 00000000..a28328be
--- /dev/null
+++ b/security/keylime-poc/k8s/run_kind.sh
@@ -0,0 +1,44 @@
+#!/usr/bin/env bash
+# shellcheck disable=SC1091
+
+# This script runs Kind cluster and sets up Keylime Agent in the cluster.
+# Keylime Agent runs as DaemonSet, and is then fronted by LoadBalancer
+# service and Ingress rules.
+#
+# In first step, we run it without Ingress. It should be noted that this
+# cheats in multiple ways:
+#
+# 1. It uses an IP address (not hostname)
+# 2. It avoids Ingress rules for UUID based call routing
+
+set -eux
+
+. config.sh
+
+check_tools()
+{
+ declare -a tools=(
+ kind
+ kubectl
+ )
+
+ for tool in "${tools[@]}"; do
+ command -v "${tool}" &>/dev/null || { echo "error: ${tool} is not in PATH"; exit 1; }
+ done
+}
+
+launch_kind_cluster()
+{
+ kind create cluster --name="${KIND_NAME}"
+}
+
+run_agent()
+{
+ echo TBD
+}
+
+
+# main
+check_tools
+launch_kind_cluster
+run_agent
diff --git a/security/keylime-poc/k8s/run_tenant.sh b/security/keylime-poc/k8s/run_tenant.sh
new file mode 100755
index 00000000..d4ddaff8
--- /dev/null
+++ b/security/keylime-poc/k8s/run_tenant.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+# shellcheck disable=SC1091
+
+# This script run Keylime Tenant in Docker container, allowing non-intrusive
+# way of executing the Tenant CLI commands while sharing the configuration
+# with the other Keylime components.
+
+set -eux
+
+. config.sh
+
+run_tenant()
+{
+ # we need to supply cv_ca directory for sharing mtls certs
+ docker run -it --rm \
+ -v "${KEYLIME_EXTERNAL_TLS_DIR}":"${KEYLIME_INTERNAL_TLS_DIR}":rw \
+ --name "${KEYLIME_TENANT_NAME}" \
+ "${KEYLIME_TENANT_IMAGE}" \
+ "$@"
+}
+
+
+# main
+run_tenant "$@"
diff --git a/security/keylime-poc/scripts/enable_ima_measurement.sh b/security/keylime-poc/scripts/enable_ima_measurement.sh
new file mode 100755
index 00000000..845bee86
--- /dev/null
+++ b/security/keylime-poc/scripts/enable_ima_measurement.sh
@@ -0,0 +1,41 @@
+#!/usr/bin/env bash
+# This is enabling IMA measurement temporarily
+# Need to set up grub/boottime parameters for permanent measurements
+# If it doesn't work, grub config for safe startup:
+# GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX ima=on integrity_audit=1 lsm=integrity,ima"
+
+# policy is auditing: change "audit" to "measure"
+
+# check requirements (tpm2-tools)
+set -e
+command -v tpm2_startup &>/dev/null
+
+# don't fail
+set -x
+set +e
+
+# Enable IMA measurement
+echo "1" | sudo tee /sys/kernel/security/ima/policy_update
+mkdir -p /etc/ima
+sudo tee /etc/ima/ima-policy << 'EOF'
+# Default IMA policy
+# Don't measure files opened with read-only permissions
+dont_measure obj_type=file mask=MAY_READ
+# Measure all executed files
+audit func=BPRM_CHECK mask=MAY_EXEC
+# Measure files mmap()ed for execute
+audit func=FILE_MMAP mask=MAY_EXEC
+# Measure files opened for write or append
+audit func=FILE_CHECK mask=MAY_WRITE uid=0
+EOF
+
+# load the ima policy
+sudo cat /etc/ima/ima-policy | sudo tee /sys/kernel/security/ima/policy
+
+# Configure TPM PRC - this needs
+# setup tpm2-tools to access the tpmserver in docker
+export TPM2TOOLS_TCTI="mssim:host=localhost,port=2321"
+tpm2_startup -c
+
+# PCR 10 will store IMA measurements
+tpm2_pcrextend 10:sha256=0000000000000000000000000000000000000000000000000000000000000000
diff --git a/security/keylime-poc/scripts/gen_allowlist.sh b/security/keylime-poc/scripts/gen_allowlist.sh
new file mode 100755
index 00000000..a5506106
--- /dev/null
+++ b/security/keylime-poc/scripts/gen_allowlist.sh
@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+# Script to generate file hashes for allowlist
+# redirect to target file "allowlist.txt"
+
+set -eu
+
+cat <