diff --git a/_topic_maps/_topic_map.yml b/_topic_maps/_topic_map.yml index 2f9b133f3bf1..e64327f1c787 100644 --- a/_topic_maps/_topic_map.yml +++ b/_topic_maps/_topic_map.yml @@ -66,6 +66,8 @@ Topics: File: deployment - Name: Using on-premise DNS with CoreDNS File: coredns +- Name: Using X.509 cryptographic identity verification + File: rhcl-using-x509-crypt-id-verify --- Name: Registering MCP servers and creating policies Dir: mcp_gateway_config diff --git a/deployment/rhcl-using-x509-crypt-id-verify.adoc b/deployment/rhcl-using-x509-crypt-id-verify.adoc new file mode 100644 index 000000000000..63a60ce98e9a --- /dev/null +++ b/deployment/rhcl-using-x509-crypt-id-verify.adoc @@ -0,0 +1,532 @@ +:_mod-docs-content-type: ASSEMBLY +include::_attributes/attributes.adoc[] +[id="rhcl-using-x509-crypt-id-verify"] += Using X.509 cryptographic identity verification +:context: rhcl-using-x509-crypt-id-verify + +toc::[] + +[role="_abstract"] +When you require strong cryptographic identity verification, you can use X.509 client certificate authentication with {prodname} to perform two-layer validation. + +include::modules/con-rhcl-about-using-x509-auth.adoc[leveloffset=+1] + +include::modules/proc-rhcl-x509-auth-prep-cas-and-certs.adoc[leveloffset=+1] + +// Module included in the following assemblies: +// +// *deployment/rhcl-using-x509-crypt-id-verify.adoc + +:_mod-docs-content-type: PROCEDURE +[id="proc-rhcl-x509-gateway-object"] += Create a Gateway object for use with the X.509 format + +[role="_abstract"] +To use the X.509 format for certificates, after you have your certificate authorities (CAs) and client certificates set up, you must create a `Gateway` object without front-end TLS validation and associated required resources. Not configuring front-end TLS in your Gateway passes authentication to your two-step X.509-based validation. + +You create the following custom resources by using the following procedure: + +* A Gateway object configured for use with the X.509 certificate format. +* A `cert-manager` Issuer: Self-signed certificate issuer for the `Gateway` object's server certificate. +* A `TLSPolicy` CR: Kuadrant policy to manage the `Gateway` object's server TLS certificate. + +.Prerequisites + +* You are logged into {ocp} as a cluster administrator. +* You installed {prodname} using Istio as your `Gateway` object controller. +* You created your CAs and X.509-formatted certificates. + +.Procedure + +. Create the `Gateway` object by using the following example: ++ +[source,yaml] +---- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: mtls-gateway + namespace: gateway-system +spec: + gatewayClassName: istio # TODO I think this is longer in OpenShift, istio-something + listeners: + - name: https + protocol: HTTPS + port: 443 + hostname: "*.nip.io" + allowedRoutes: + namespaces: + from: All + tls: + mode: Terminate + certificateRefs: + - name: gateway-tls-cert + kind: Secret +---- + +Apply the CR ++ +[source,terminal] +---- +$ oc apply +---- +//TODO finish me + +. Create the cert-manager `Issuer` by using the following example: ++ +[source,yaml] +---- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: gateway-cert-issuer + namespace: gateway-system +spec: + selfSigned: {} +---- + +Apply the CR ++ +[source,terminal] +---- +$ oc apply +---- +//TODO finish me + +. Create the `TLSPolicy` object by using the following example: ++ +[source,yaml] +---- +apiVersion: kuadrant.io/v1 +kind: TLSPolicy +metadata: + name: mtls-gateway-tls + namespace: gateway-system +spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: mtls-gateway + sectionName: https + issuerRef: + group: cert-manager.io + kind: Issuer + name: gateway-cert-issuer +---- + +Apply the CR ++ +[source,terminal] +---- +$ oc apply +---- +//TODO finish me + + +### Step 5: Create EnvoyFilter for mTLS validation + +//Q: would we create this filter manually on OpenShift, or just use the Gateway or AuthPolicy? + +Create an EnvoyFilter to configure Envoy's DownstreamTlsContext for client certificate validation: + +[source,yaml] +---- +apiVersion: networking.istio.io/v1alpha3 +kind: EnvoyFilter +metadata: + name: mtls-validation + namespace: gateway-system +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: mtls-gateway + configPatches: + - applyTo: FILTER_CHAIN + match: + context: GATEWAY + listener: + portNumber: 443 + patch: + operation: MERGE + value: + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + commonTlsContext: + validationContext: + trustedCa: + filename: /etc/certs/ca.crt + requireClientCertificate: true +---- + +. Apply the filter + +[source,terminal] +---- +$ oc apply -f +---- + +//Q: are these just general required steps? e.g, create an SA, Service, and Deployment; does an OpenShift user need these YAMLs as examples? + +### Step 6: Deploy application + +. Deploy httpbin application + +[source,yaml] +---- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: httpbin +--- +apiVersion: v1 +kind: Service +metadata: + name: httpbin + labels: + app: httpbin + service: httpbin +spec: + ports: + - name: http + port: 80 + selector: + app: httpbin +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: httpbin +spec: + replicas: 1 + selector: + matchLabels: + app: httpbin + version: v1 + template: + metadata: + labels: + app: httpbin + version: v1 + spec: + serviceAccountName: httpbin + securityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + containers: + - name: httpbin + image: quay.io/kuadrant/httpbin@sha256:2865a9ac6596340135fc3cd82cbc543c4050124afb10361e36f727abe4f71900 + imagePullPolicy: IfNotPresent + securityContext: + runAsUser: 1000 + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + ports: + - containerPort: 80 + volumeMounts: + - name: tmp + mountPath: /tmp + volumes: + - name: tmp + emptyDir: {} +---- + +[source,terminal] +---- +$ oc apply -f +---- + +//Q: can I use gRCP with this also? + +// Module included in the following assemblies: +// +// *deployment/rhcl-using-x509-crypt-id-verify.adoc + +:_mod-docs-content-type: PROCEDURE +[id="proc-rhcl-x509-httproute-object"] += Creating an HTTPRoute custom resource for X.509 certificates + +[role="_abstract"] +To use the X.509 format for certificate, you must create an `HTTPRoute` custom resource (CR) that references your `Gateway` object. + +//Q: Can we also use a GRCPRoute instead? If yes, is there anything we should caveat about fields there? + +.Prerequisites + +* You are logged into {ocp} as a cluster administrator. +* You installed {prodname} using Istio as your `Gateway` object controller. +* You created your CAs and X.509-formatted certificates. +* You created your `Gateway` object. + +.Procedure + +. Create your `HTTPRoute` CR by using the following example: ++ +[source,yaml] +---- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: __ + namespace: __ + labels: + app: __ +spec: + parentRefs: + - name: mtls-gateway + namespace: gateway-system + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: httpbin + port: 80 +---- ++ +* Replace the `metadata.name` parameter value with the name of your application route. +* Replace the `metadata.namespace` parameter with the namespace where your `Gateway` object is applied. + +. Apply your `HTTPRoute` custom resource by running the following command: ++ +[source,terminal,subs="+quotes"] +---- +$ oc apply -f __ +---- ++ +Replace `__` with the name of your `HTTPRoute` YAML. + + +// Module included in the following assemblies: +// +// *deployment/rhcl-using-x509-crypt-id-verify.adoc + +:_mod-docs-content-type: PROCEDURE +[id="proc-rhcl-x509-create-auth-policy"] += Creating an AuthPolicy custom resource for X.509 certificates + +[role="_abstract"] +To use the X.509 format for certificate, you must create an `AuthPolicy` custom resource (CR) that completes critical functions of your two-step validation. + +When you use the following procedure, you create an `AuthPolicy` CR that implements the following steps: + +* Extracts the certificate from the `x-forwarded-client-cert` header. +* Validates the certificate chain against CA certificates labeled `app.kubernetes.io/name: trusted-client`. +* Enforces authorization based on a certificate organization attribute. +* Injects certificate attributes into request headers for fine-grained access control. + +.Prerequisites + +* You are logged into {ocp} as a cluster administrator. +* You installed {prodname} using Istio as your `Gateway` object controller. +* You created your CAs and X.509-formatted certificates. +* You created your `Gateway` object. +* You created your route. + +. Create your `AuthPolicy` CR by using the following example: ++ +[source,yaml] +---- +apiVersion: kuadrant.io/v1 +kind: AuthPolicy +metadata: + name: x509-auth-policy + namespace: default +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httpbin-route + rules: + authentication: + "x509-client-cert": + x509: + # Extract certificate from XFCC header + # The gateway populates this header after validating the client certificate + source: + # Name of the XFCC header (Envoy's X-Forwarded-Client-Cert standard) + # This is the default header name used by Istio/Envoy + xfccHeader: "x-forwarded-client-cert" + selector: + matchLabels: + app.kubernetes.io/name: trusted-client + authorization: + "certificate-attributes": + patternMatching: + patterns: + - predicate: "size(auth.identity.Organization) > 0 && auth.identity.Organization[0] == 'Kuadrant'" + response: + success: + headers: + # Extract the Common Name (CN) from the certificate + "x-client-common-name": + plain: + expression: auth.identity.CommonName + # Extract the Organization (O) from the certificate + "x-client-org": + plain: + expression: auth.identity.Organization[0] +---- +//TODO + +. Apply your `AuthPolicy` custom resource by running the following command: ++ +[source,terminal,subs="+quotes"] +---- +$ oc apply -f __ +---- ++ +Replace `__` with the name of your `AuthPolicy` YAML. + + +// Module included in the following assemblies: +// +// *deployment/rhcl-using-x509-crypt-id-verify.adoc + +:_mod-docs-content-type: PROCEDURE +[id="proc-rhcl-verifying-x509-auth"] += Verifying X.509 authentication + +//Q: would we test this way on OpenShift? + +[role="_abstract"] +FPO check your bouncer so she ain't letting everybody in and stuff + +Run the test scenario: ++ +[source,terminal,subs="+quotes"] +---- +$ GATEWAY_IP=$(oc get gateway mtls-gateway -n gateway-system -o jsonpath='{.status.addresses[0].value}') +---- + +# Test 1: Valid certificate → HTTP 200 ++ +[source,terminal,subs="+quotes"] +---- +$ curl -ik https://httpbin.$GATEWAY_IP.nip.io/get \ + --cert /tmp/client.crt \ + --key /tmp/client.key +---- + +# Test 2: No certificate → TLS handshake fails ++ +[source,terminal,subs="+quotes"] +---- +$ curl -ik https://httpbin.$GATEWAY_IP.nip.io/get +---- + +# Test 3: Untrusted certificate → TLS handshake fails ++ +[source,terminal,subs="+quotes"] +---- +$ curl -ik https://httpbin.$GATEWAY_IP.nip.io/get \ + --cert /tmp/untrusted.crt \ + --key /tmp/untrusted.key +---- + +# Test 4: Valid cert, wrong attributes → HTTP 403 ++ +[source,terminal,subs="+quotes"] +---- +$ curl -ik https://httpbin.$GATEWAY_IP.nip.io/get \ + --cert /tmp/unauthorized-client.crt \ + --key /tmp/unauthorized-client.key +---- + +// Module included in the following assemblies: +// +// *deployment/rhcl-using-x509-crypt-id-verify.adoc + +:_mod-docs-content-type: PROCEDURE +[id="proc-rhcl-ts-x509-auth"] += Troubleshooting X.509 authentication + +[role="_abstract"] +FPO various things can go wrong and stuff + +### EnvoyFilter not applied + +**Symptoms**: TLS handshake succeeds without client certificate + +**Resolution**: + +. Verify EnvoyFilter exists ++ +[source,terminal,subs="+quotes"] +---- +$ oc get envoyfilter mtls-validation -n gateway-system +---- + +. Check targetRef points to gateway ++ +[source,terminal,subs="+quotes"] +---- +$ oc get envoyfilter mtls-validation -n gateway-system -o yaml | grep -A 3 targetRefs +---- + +. Check Envoy configuration was patched ++ +[source,terminal,subs="+quotes"] +---- +$ oc exec -n gateway-system \ + $(kubectl get pod -n gateway-system -l gateway.networking.k8s.io/gateway-name=mtls-gateway -o jsonpath='{.items[0].metadata.name}') \ + -- curl -s localhost:15000/config_dump?include_eds | grep -A 20 validation_context +---- + +### CA certificate not mounted + +**Symptoms**: Gateway logs show "failed to load trusted CA" + +**Resolution**: + +# Verify ConfigMap exists ++ +[source,terminal,subs="+quotes"] +---- +$ oc get configmap client-ca-bundle -n gateway-system +---- + +# Check volume mount in gateway pod ++ +[source,terminal,subs="+quotes"] +---- +$ oc describe pod -n gateway-system -l gateway.networking.k8s.io/gateway-name=mtls-gateway | grep -A 5 Mounts +---- + +# Verify file exists in pod ++ +[source,terminal,subs="+quotes"] +---- +$ oc exec -n gateway-system \ + $(kubectl get pod -n gateway-system -l gateway.networking.k8s.io/gateway-name=mtls-gateway -o jsonpath='{.items[0].metadata.name}') \ + -- cat /etc/certs/ca.crt +---- + +### XFCC header not set correctly + +**Symptoms**: Authorino rejects with "certificate not found" + +**Resolution**: + +# Verify forward_client_cert_details configuration ++ +[source,terminal,subs="+quotes"] +---- +$ oc exec -n gateway-system \ + $(kubectl get pod -n gateway-system -l gateway.networking.k8s.io/gateway-name=mtls-gateway -o jsonpath='{.items[0].metadata.name}') \ + -- curl -s localhost:15000/config_dump?include_eds | grep -i forward_client_cert +---- + +# Check Envoy access logs for XFCC ++ +[source,terminal,subs="+quotes"] +---- +$ oc logs -n gateway-system -l gateway.networking.k8s.io/gateway-name=mtls-gateway | grep XFCC +---- \ No newline at end of file diff --git a/modules/con-rhcl-about-using-x509-auth.adoc b/modules/con-rhcl-about-using-x509-auth.adoc new file mode 100644 index 000000000000..cc817e29f58b --- /dev/null +++ b/modules/con-rhcl-about-using-x509-auth.adoc @@ -0,0 +1,33 @@ +// Module included in the following assemblies: +// +// *deployment/rhcl-using-x509-crypt-id-verify.adoc + +:_mod-docs-content-type: CONCEPT +[id="con-rhcl-about-using-x509-auth"] += About using X.509 authentication in {prodname} + +[role="_abstract"] +When you require strong cryptographic identity verification, you can use the X.509 certificate format to implement a two-step validation model. + +Use the X.509 format for your certificates in the following scenarios: + +* Zero-trust architecture: You need cryptographic proof of identity for every request. +* Compliance requirements: Security standards, such as PCI DSS, HIPAA, and FedRAMP, mandate mutual TLS (mTLS). +* Machine-to-machine communication: Services authenticate with long-lived certificates from your Public Key Infrastructure (PKI). If you are using mTLS, you are using PKI. PKI issues, distributes, and renews your root certificate authorities (CAs). +* Multi-CA trust: You have different clients that are issued certificates by different CAs and you need fine-grained control over which CAs are trusted for which routes. +* No shared secrets: You want to avoid rotating and distributing API keys or tokens. + +When you use X.509 authentication in {prodname}, you implement the following two-step validation model: + +* Step 1. Cryptographic proof: + +** During the initial TLS handshake, the client presents s certificate that the `Gateway` object validates against your trusted CA certificates. This cryptographically verifies that the client is who they say they are because they have the private key and your certificate is trusted. +** Invalid, expired, or untrusted certificates are rejected at the TLS layer before reaching the application. +** The `Gateway` object sets the `x-forwarded-client-cert` (XFCC) header with the certificate details so that incoming XFCC headers from clients are sanitized. + +* Step 2. Context-aware validation: + +** Authorino validates certificates extracted from the `XFCC` header. This means that you can apply fine-grained rules and manage trust across multiple CAs by using simple label selectors. +** Certificate attributes extract identity details directly from client certificates. You can use these details to create precise access rules, or pass them along to your backend services as custom headers, ensuring that your applications always know exactly who is calling them. + +Both layers must succeed for a request to be authenticated. diff --git a/modules/proc-create-gateway.adoc b/modules/proc-create-gateway.adoc index 8d0707ac5cee..7bb5f8ef6817 100644 --- a/modules/proc-create-gateway.adoc +++ b/modules/proc-create-gateway.adoc @@ -7,7 +7,7 @@ = Create your Gateway object [role="_abstract"] -As a platform engineer or cluster administrator, you must deploy a `Gateway` object in your {ocp} cluster to begin setting up the infrastructure used by application developers. The `Gateway` is the instantiation of your entry point. It tells the controller to provision load balancer with specific ports and security credentials. +You must deploy a `Gateway` object in your {ocp} cluster to begin setting up the infrastructure used by application developers. The `Gateway` is the instantiation of your entry point. It tells the controller to provision load balancer with specific ports and security credentials. [IMPORTANT] ==== @@ -55,7 +55,7 @@ spec: + [IMPORTANT] ==== -In a multicluster environment, for {prodname} to balance traffic by using DNS across clusters, you must specify a gateway with a shared hostname. You can define this by using an HTTPS listener with a wildcard hostname based on the root domain. +If you require cryptographic identity verification, see "Create a Gateway object for use with the X.509 format" for a `Gateway` object example to use instead of this default. ==== . Apply the `Gateway` CR by running the following command: diff --git a/modules/proc-rhcl-x509-auth-prep-cas-and-certs.adoc b/modules/proc-rhcl-x509-auth-prep-cas-and-certs.adoc new file mode 100644 index 000000000000..27e7b7dd5bc5 --- /dev/null +++ b/modules/proc-rhcl-x509-auth-prep-cas-and-certs.adoc @@ -0,0 +1,118 @@ +// Module included in the following assemblies: +// +// *deployment/rhcl-using-x509-crypt-id-verify.adoc + +:_mod-docs-content-type: PROCEDURE +[id="proc-rhcl-x509-auth-prep-cas-and-certs"] += Prepare certificate authorites and client certificates + +[role="_abstract"] +To set up authentication for clients with provider-specific TLS validation, you must first have certificate authorities (CAs) and client certificates. + +You can use the following procedure to manually generate resources, or you can provision certificates by using an existing certificate manager or other automated process. Whichever method you select, you must complete the following tasks to use X.509 validation with {prodname}: + +* Include the `extendedKeyUsage=clientAuth` parameter in your client certificates. +* Mount the root CA certificate into the `Gateway` object pods. This enables network-level verification. The `Gateway` object requires the CA bundle to allow the TLS connection to establish. +* Store the CA used to sign the client certificate in a Secret and label the Secret for discovery by Authorino. This enables policy-level verification. Authorino requires the CA bundle to perform deep packet inspection of the identity. Then Authorino can grant or deny the specific client's access a specific API. + +.Prerequisites + +* You are logged into {ocp} as a cluster administrator. +* You installed {prodname}. + +.Procedure + +. On your local host, generate your certificate authority (CA) by running the following command: ++ +[source,terminal] +---- +$ openssl req -x509 -sha512 -nodes \ + -days 365 \ + -newkey rsa:4096 \ + -subj "/CN=Test CA/O=Kuadrant/C=US" \ + -addext basicConstraints=CA:TRUE \ + -addext keyUsage=digitalSignature,keyCertSign \ + -keyout /tmp/ca.key \ + -out /tmp/ca.crt +---- + +. On your local host, create X.509 v3 extensions for your client certificate by running the following command: ++ +[source,terminal] +---- +$ cat > /tmp/x509v3.ext << EOF +authorityKeyIdentifier=keyid,issuer +basicConstraints=CA:FALSE +keyUsage=digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment +extendedKeyUsage=clientAuth +EOF +---- + +. Generate the client certificate on your local host by running the following commands: + +.. Generate your Private Key. This is the secret file that stays on the client. Run the following command: ++ +[source,terminal] +---- +$ openssl genrsa -out /tmp/client.key 4096 +---- + +.. Create a Certificate Signing Request (CSR). The following command uses your private key and identity information to create a request for a certificate: ++ +[source,terminal] +---- +$ openssl req -new \ + -subj "/CN=test-client/O=Kuadrant/C=US" \ + -key /tmp/client.key \ + -out /tmp/client.csr +---- + +.. Sign the certificate. This command takes your request, looks at your internal CA, and issues the final Public Certificate, the `client.crt`: ++ +[source,terminal] +---- +$ openssl x509 -req -sha512 \ + -days 365 \ + -CA /tmp/ca.crt \ + -CAkey /tmp/ca.key \ + -CAcreateserial \ + -extfile /tmp/x509v3.ext \ + -in /tmp/client.csr \ + -out /tmp/client.crt +---- + +. If you do not have a CA that you use to issue and sign client certificates, create the self-signed CA certificate in your cluster by running the following commands: + +.. Create the ConfigMap for `Gateway` TLS validation by running the following command: ++ +[source,terminal] +---- +$ oc create configmap gateway-client-ca \ + -n gateway-system \ + ----from-file=trust-bundle.crt=/tmp/ca.crt +---- ++ +The ConfigMap must be in the same namespace as the `Gateway` object. +Your `Gateway` object listener `clientCertificateRefs` must point to this ConfigMap. + +.. Create the Authorino validation `Secret` by running the following command: ++ +[source,terminal] +---- +$ oc create secret generic authorino-ca-validation \ + -n kuadrant-system \ + --from-file=ext-auth-ca.crt=/tmp/ca.crt +---- ++ +The `Secret` must be in the same namespace as Authorino. +//AI said, "Never upload your CA private key to the cluster unless you are running a certificate issuer (like cert-manager)." and suggested using only the public key here b/c Authorino does not need the private key WDYT? + +.. Label the Secret so that Authorino can find its trusted anchors by running the following command: ++ +[source,terminal] +---- +$ oc label secret authorino-ca-validation \ + -n kuadrant-system \ + authorino.kuadrant.io/managed-by=authorino \ + app.kubernetes.io/name=kuadrant-auth +----