--- title: Data protection weight: 20 aliases: - /layered-zero-trust/lzt-data-protection/ - /layered-zero-trust/lzt-secure-multitier --- :toc: :imagesdir: /images :_mod-docs-content-type: ASSEMBLY include::modules/comm-attributes.adoc[] [id="lzt-demo-application"] = Data protection This use case demonstrates Zero Trust data protection using the `qtodo` demo application — a simple to-do list app built with Quarkus and PostgreSQL that is included in the default Layered Zero Trust pattern installation described in the link:/patterns/layered-zero-trust/lzt-getting-started/[Getting Started] guide. No additional setup is required to explore this use case. [id="what-this-demonstrates"] == What this demonstrates On its own, the `qtodo` application is unremarkable. What makes it special is _how_ it runs inside the ZTVP — every interaction is authenticated, every credential is dynamic, and every network connection is explicitly authorized. [id="lzt-application-components"] == Application components The `qtodo` application consists of the following key components: * *`qtodo`*: A link:https://quarkus.io[Quarkus-based] frontend application protected by OpenID Connect (OIDC) authentication. Users are managed in Red{nbsp}Hat build of Keycloak (RHBK). * *PostgreSQL*: The relational database used by `qtodo`, deployed in a separate `qtodo-db` namespace. Its credentials are dynamically generated and stored in HashiCorp Vault. * *HashiCorp Vault*: Stores sensitive values for all components. Implements JSON Web Token (JWT)-based authentication to enable access using SPIFFE-based identities. * *Zero Trust Workload Identity Manager (SPIRE/SPIFFE)*: Assigns a cryptographic identity to the `qtodo` pod, allowing it to authenticate with Vault and obtain database credentials. * *link:https://github.com/spiffe/spiffe-helper[spiffe-helper]*: A sidecar container that dynamically fetches JWT-based identities from the SPIFFE Workload API. * *`spiffe-vault-client`*: A sidecar container that uses the SPIFFE identity to authenticate with Vault and retrieve database credentials just-in-time. [NOTE] ==== The `qtodo` pod uses a series of init containers and sidecar containers to supply the application with the credentials required for operation. Explore the pod details in the {ocp} web console (*Workloads > Pods* in the `qtodo` namespace) to see this in action. ==== [id="security-features-in-action"] == Security features in action When a user accesses the `qtodo` application, the following security features activate: [id="user-authentication"] === 1. User authentication The user is redirected to Red{nbsp}Hat build of Keycloak for login. Only users with the correct role in the `ztvp` realm can access the application. No credentials are stored in the application code. [id="workload-identity"] === 2. Workload identity The `qtodo` pod proves its identity using a cryptographic SPIFFE certificate (SVID). This identity is issued by SPIRE and rotates automatically every hour. No static service account tokens are used. [id="dynamic-secrets"] === 3. Dynamic secrets The `spiffe-vault-client` sidecar uses the pod's SPIFFE identity to authenticate with HashiCorp Vault and retrieve the PostgreSQL password. The password is never stored as a Kubernetes Secret — it's fetched just-in-time and exists only in memory. [id="namespace-separation"] === 4. Namespace separation The `qtodo` application and its PostgreSQL database run in separate namespaces (`qtodo` and `qtodo-db`). This isolation limits the blast radius of a compromise — an attacker who gains access to the application namespace cannot directly reach the database resources, and vice versa. [id="network-isolation"] === 5. Network isolation Default-deny NetworkPolicies are applied independently to both the application and database namespaces. Explicit allow rules permit only the connections that are strictly required: browser to `qtodo` (port 8443), `qtodo` to PostgreSQL (port 5432, cross-namespace), `qtodo` to Vault (port 8200), and `qtodo` to DNS (port 5353). Nothing else is permitted. [id="before-vs-after"] == Before vs. after [cols="2,2,3",options="header"] |=== | Aspect | Traditional deployment | With ZTVP | Database credentials | Static Kubernetes Secret in etcd | Dynamic, fetched from Vault via SPIFFE | User authentication | Application-managed or none | Centralized Keycloak SSO | Network access | Allow-all (Kubernetes default) | Default-deny with per-pod allow rules across separate namespaces | Workload identity | Shared service account | Cryptographic SPIFFE SVID (1-hour TTL) | Code changes required | N/A | Application reads credentials from sidecar-generated files instead of environment variables |=== [id="try-it-yourself"] == Try it yourself After deploying the ZTVP, you can access the `qtodo` application and observe the Zero Trust security features in action: . Navigate to the `qtodo` route in the {ocp} Console (*Networking > Routes* in the `qtodo` namespace). . You will be redirected to the Keycloak login page. Log in with the `qtodo` user credentials from Vault (path: `secret/hub/infra/users/keycloak-users`). See link:/patterns/layered-zero-trust/lzt-getting-started/#lzt-accessing-credentials[Accessing credentials] for instructions on how to retrieve secrets from Vault. . Observe the OIDC redirect flow — the browser is redirected to Keycloak, authenticated, and then returned to the application. During the first login you will be asked to change your credentials. . Add and remove to-do items to verify the application works end-to-end. You can also login as a `qtodo-admin` user, which can modify all demo application entries. For a detailed technical breakdown of the sidecar architecture and data protection workflow, see the link:/patterns/layered-zero-trust/lzt-architecture/#sidecar-pattern[Sidecar pattern] section in the Architecture page.