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 <