Chapter 10. Security 10.1. Configuring JSON Web Token authentication for Knative services OpenShift Serverless does not currently have user-defined authorization features. To add user-defined authorization to your deployment, you must integrate OpenShift Serverless with Red Hat OpenShift Service Mesh, and then configure JSON Web Token (JWT) authentication and sidecar injection for Knative services. 10.1.1. Using JSON Web Token authentication with Service Mesh 2.x and OpenShift Serverless You can use JSON Web Token (JWT) authentication with Knative services by using Service Mesh 2.x and OpenShift Serverless. To do this, you must create authentication requests and policies in the application namespace that is a member of the ServiceMeshMemberRoll object. You must also enable sidecar injection for the service. Important Adding sidecar injection to pods in system namespaces, such as knative-serving and knative-serving-ingress , is not supported when Kourier is enabled. If you require sidecar injection for pods in these namespaces, see the OpenShift Serverless documentation on Integrating Service Mesh with OpenShift Serverless natively . Prerequisites You have installed the OpenShift Serverless Operator, Knative Serving, and Red Hat OpenShift Service Mesh on your cluster. Install the OpenShift CLI ( oc ). You have created a project or have access to a project with the appropriate roles and permissions to create applications and other workloads in OpenShift Container Platform. Procedure Add the sidecar.istio.io/inject="true" annotation to your service: Example service apiVersion: serving.knative.dev/v1 kind: Service metadata: name: <service_name> spec: template: metadata: annotations: sidecar.istio.io/inject: "true" 1 sidecar.istio.io/rewriteAppHTTPProbers: "true" 2 ... 1 Add the sidecar.istio.io/inject="true" annotation. 2 You must set the annotation sidecar.istio.io/rewriteAppHTTPProbers: "true" in your Knative service, because OpenShift Serverless versions 1.14.0 and higher use an HTTP probe as the readiness probe for Knative services by default. Apply the Service resource: USD oc apply -f <filename> Create a RequestAuthentication resource in each serverless application namespace that is a member in the ServiceMeshMemberRoll object: apiVersion: security.istio.io/v1beta1 kind: RequestAuthentication metadata: name: jwt-example namespace: <namespace> spec: jwtRules: - issuer: testing@secure.istio.io jwksUri: https://raw.githubusercontent.com/istio/istio/release-1.8/security/tools/jwt/samples/jwks.json Apply the RequestAuthentication resource: USD oc apply -f <filename> Allow access to the RequestAuthenticaton resource from system pods for each serverless application namespace that is a member in the ServiceMeshMemberRoll object, by creating the following AuthorizationPolicy resource: apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: allowlist-by-paths namespace: <namespace> spec: action: ALLOW rules: - to: - operation: paths: - /metrics 1 - /healthz 2 1 The path on your application to collect metrics by system pod. 2 The path on your application to probe by system pod. Apply the AuthorizationPolicy resource: USD oc apply -f <filename> For each serverless application namespace that is a member in the ServiceMeshMemberRoll object, create the following AuthorizationPolicy resource: apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: require-jwt namespace: <namespace> spec: action: ALLOW rules: - from: - source: requestPrincipals: ["testing@secure.istio.io/testing@secure.istio.io"] Apply the AuthorizationPolicy resource: USD oc apply -f <filename> Verification If you try to use a curl request to get the Knative service URL, it is denied: Example command USD curl http://hello-example-1-default.apps.mycluster.example.com/ Example output RBAC: access denied Verify the request with a valid JWT. Get the valid JWT token: USD TOKEN=USD(curl https://raw.githubusercontent.com/istio/istio/release-1.8/security/tools/jwt/samples/demo.jwt -s) && echo "USDTOKEN" | cut -d '.' -f2 - | base64 --decode - Access the service by using the valid token in the curl request header: USD curl -H "Authorization: Bearer USDTOKEN" http://hello-example-1-default.apps.example.com The request is now allowed: Example output Hello OpenShift! 10.1.2. Using JSON Web Token authentication with Service Mesh 1.x and OpenShift Serverless You can use JSON Web Token (JWT) authentication with Knative services by using Service Mesh 1.x and OpenShift Serverless. To do this, you must create a policy in the application namespace that is a member of the ServiceMeshMemberRoll object. You must also enable sidecar injection for the service. Important Adding sidecar injection to pods in system namespaces, such as knative-serving and knative-serving-ingress , is not supported when Kourier is enabled. If you require sidecar injection for pods in these namespaces, see the OpenShift Serverless documentation on Integrating Service Mesh with OpenShift Serverless natively . Prerequisites You have installed the OpenShift Serverless Operator, Knative Serving, and Red Hat OpenShift Service Mesh on your cluster. Install the OpenShift CLI ( oc ). You have created a project or have access to a project with the appropriate roles and permissions to create applications and other workloads in OpenShift Container Platform. Procedure Add the sidecar.istio.io/inject="true" annotation to your service: Example service apiVersion: serving.knative.dev/v1 kind: Service metadata: name: <service_name> spec: template: metadata: annotations: sidecar.istio.io/inject: "true" 1 sidecar.istio.io/rewriteAppHTTPProbers: "true" 2 ... 1 Add the sidecar.istio.io/inject="true" annotation. 2 You must set the annotation sidecar.istio.io/rewriteAppHTTPProbers: "true" in your Knative service, because OpenShift Serverless versions 1.14.0 and higher use an HTTP probe as the readiness probe for Knative services by default. Apply the Service resource: USD oc apply -f <filename> Create a policy in a serverless application namespace which is a member in the ServiceMeshMemberRoll object, that only allows requests with valid JSON Web Tokens (JWT): Important The paths /metrics and /healthz must be included in excludedPaths because they are accessed from system pods in the knative-serving namespace. apiVersion: authentication.istio.io/v1alpha1 kind: Policy metadata: name: default namespace: <namespace> spec: origins: - jwt: issuer: testing@secure.istio.io jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.6/security/tools/jwt/samples/jwks.json" triggerRules: - excludedPaths: - prefix: /metrics 1 - prefix: /healthz 2 principalBinding: USE_ORIGIN 1 The path on your application to collect metrics by system pod. 2 The path on your application to probe by system pod. Apply the Policy resource: USD oc apply -f <filename> Verification If you try to use a curl request to get the Knative service URL, it is denied: USD curl http://hello-example-default.apps.mycluster.example.com/ Example output Origin authentication failed. Verify the request with a valid JWT. Get the valid JWT token: USD TOKEN=USD(curl https://raw.githubusercontent.com/istio/istio/release-1.6/security/tools/jwt/samples/demo.jwt -s) && echo "USDTOKEN" | cut -d '.' -f2 - | base64 --decode - Access the service by using the valid token in the curl request header: USD curl http://hello-example-default.apps.mycluster.example.com/ -H "Authorization: Bearer USDTOKEN" The request is now allowed: Example output Hello OpenShift! 10.2. Configuring a custom domain for a Knative service Knative services are automatically assigned a default domain name based on your cluster configuration. For example, <service_name>-<namespace>.example.com . You can customize the domain for your Knative service by mapping a custom domain name that you own to a Knative service. You can do this by creating a DomainMapping resource for the service. You can also create multiple DomainMapping resources to map multiple domains and subdomains to a single service. 10.2.1. Creating a custom domain mapping You can customize the domain for your Knative service by mapping a custom domain name that you own to a Knative service. To map a custom domain name to a custom resource (CR), you must create a DomainMapping CR that maps to an Addressable target CR, such as a Knative service or a Knative route. Prerequisites The OpenShift Serverless Operator and Knative Serving are installed on your cluster. Install the OpenShift CLI ( oc ). You have created a project or have access to a project with the appropriate roles and permissions to create applications and other workloads in OpenShift Container Platform. You have created a Knative service and control a custom domain that you want to map to that service. Note Your custom domain must point to the IP address of the OpenShift Container Platform cluster. Procedure Create a YAML file containing the DomainMapping CR in the same namespace as the target CR you want to map to: apiVersion: serving.knative.dev/v1alpha1 kind: DomainMapping metadata: name: <domain_name> 1 namespace: <namespace> 2 spec: ref: name: <target_name> 3 kind: <target_type> 4 apiVersion: serving.knative.dev/v1 1 The custom domain name that you want to map to the target CR. 2 The namespace of both the DomainMapping CR and the target CR. 3 The name of the target CR to map to the custom domain. 4 The type of CR being mapped to the custom domain. Example service domain mapping apiVersion: serving.knative.dev/v1alpha1 kind: DomainMapping metadata: name: example.com namespace: default spec: ref: name: example-service kind: Service apiVersion: serving.knative.dev/v1 Example route domain mapping apiVersion: serving.knative.dev/v1alpha1 kind: DomainMapping metadata: name: example.com namespace: default spec: ref: name: example-route kind: Route apiVersion: serving.knative.dev/v1 Apply the DomainMapping CR as a YAML file: USD oc apply -f <filename> 10.2.2. Creating a custom domain mapping by using the Knative CLI You can customize the domain for your Knative service by mapping a custom domain name that you own to a Knative service. You can use the Knative ( kn ) CLI to create a DomainMapping custom resource (CR) that maps to an Addressable target CR, such as a Knative service or a Knative route. Prerequisites The OpenShift Serverless Operator and Knative Serving are installed on your cluster. You have created a Knative service or route, and control a custom domain that you want to map to that CR. Note Your custom domain must point to the DNS of the OpenShift Container Platform cluster. You have installed the Knative ( kn ) CLI. You have created a project or have access to a project with the appropriate roles and permissions to create applications and other workloads in OpenShift Container Platform. Procedure Map a domain to a CR in the current namespace: USD kn domain create <domain_mapping_name> --ref <target_name> Example command USD kn domain create example.com --ref example-service The --ref flag specifies an Addressable target CR for domain mapping. If a prefix is not provided when using the --ref flag, it is assumed that the target is a Knative service in the current namespace. Map a domain to a Knative service in a specified namespace: USD kn domain create <domain_mapping_name> --ref <ksvc:service_name:service_namespace> Example command USD kn domain create example.com --ref ksvc:example-service:example-namespace Map a domain to a Knative route: USD kn domain create <domain_mapping_name> --ref <kroute:route_name> Example command USD kn domain create example.com --ref kroute:example-route 10.2.3. Securing a service with a custom domain by using a TLS certificate After you have configured a custom domain for a Knative service, you can use a TLS certificate to secure the mapped service. To do this, you must create a Kubernetes TLS secret, and then update the DomainMapping CR to use the TLS secret that you have created. Prerequisites You configured a custom domain for a Knative service and have a working DomainMapping CR. You have a TLS certificate from your Certificate Authority provider or a self-signed certificate. You have obtained the cert and key files from your Certificate Authority provider, or a self-signed certificate. Install the OpenShift CLI ( oc ). Procedure Create a Kubernetes TLS secret: USD oc create secret tls <tls_secret_name> --cert=<path_to_certificate_file> --key=<path_to_key_file> If you are using Red Hat OpenShift Service Mesh as the ingress for your OpenShift Serverless installation, label the Kubernetes TLS secret with the following: "networking.internal.knative.dev/certificate-uid": "<value>" If you are using a third-party secret provider such as cert-manager, you can configure your secret manager to label the Kubernetes TLS secret automatically. Cert-manager users can use the secret template offered to automatically generate secrets with the correct label. In this case, secret filtering is done based on the key only, but this value can carry useful information such as the certificate ID that the secret contains. Note The {cert-manager-operator} is a Technology Preview feature. For more information, see the Installing the {cert-manager-operator} documentation. Update the DomainMapping CR to use the TLS secret that you have created: apiVersion: serving.knative.dev/v1alpha1 kind: DomainMapping metadata: name: <domain_name> namespace: <namespace> spec: ref: name: <service_name> kind: Service apiVersion: serving.knative.dev/v1 # TLS block specifies the secret to be used tls: secretName: <tls_secret_name> Verification Verify that the DomainMapping CR status is True , and that the URL column of the output shows the mapped domain with the scheme https : USD oc get domainmapping <domain_name> Example output NAME URL READY REASON example.com https://example.com True Optional: If the service is exposed publicly, verify that it is available by running the following command: USD curl https://<domain_name> If the certificate is self-signed, skip verification by adding the -k flag to the curl command.

Commands:
apiVersion: serving.knative.dev/v1 kind: Service metadata: name: <service_name> spec: template: metadata: annotations: sidecar.istio.io/inject: "true" 1 sidecar.istio.io/rewriteAppHTTPProbers: "true" 2
oc apply -f <filename>
apiVersion: security.istio.io/v1beta1 kind: RequestAuthentication metadata: name: jwt-example namespace: <namespace> spec: jwtRules: - issuer: testing@secure.istio.io jwksUri: https://raw.githubusercontent.com/istio/istio/release-1.8/security/tools/jwt/samples/jwks.json
oc apply -f <filename>
apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: allowlist-by-paths namespace: <namespace> spec: action: ALLOW rules: - to: - operation: paths: - /metrics 1 - /healthz 2
oc apply -f <filename>
apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: require-jwt namespace: <namespace> spec: action: ALLOW rules: - from: - source: requestPrincipals: ["testing@secure.istio.io/testing@secure.istio.io"]
oc apply -f <filename>
curl http://hello-example-1-default.apps.mycluster.example.com/
RBAC: access denied
TOKEN=USD(curl https://raw.githubusercontent.com/istio/istio/release-1.8/security/tools/jwt/samples/demo.jwt -s) && echo "USDTOKEN" | cut -d '.' -f2 - | base64 --decode -
curl -H "Authorization: Bearer USDTOKEN" http://hello-example-1-default.apps.example.com
Hello OpenShift!
apiVersion: serving.knative.dev/v1 kind: Service metadata: name: <service_name> spec: template: metadata: annotations: sidecar.istio.io/inject: "true" 1 sidecar.istio.io/rewriteAppHTTPProbers: "true" 2
oc apply -f <filename>
apiVersion: authentication.istio.io/v1alpha1 kind: Policy metadata: name: default namespace: <namespace> spec: origins: - jwt: issuer: testing@secure.istio.io jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.6/security/tools/jwt/samples/jwks.json" triggerRules: - excludedPaths: - prefix: /metrics 1 - prefix: /healthz 2 principalBinding: USE_ORIGIN
oc apply -f <filename>
curl http://hello-example-default.apps.mycluster.example.com/
Origin authentication failed.
TOKEN=USD(curl https://raw.githubusercontent.com/istio/istio/release-1.6/security/tools/jwt/samples/demo.jwt -s) && echo "USDTOKEN" | cut -d '.' -f2 - | base64 --decode -
curl http://hello-example-default.apps.mycluster.example.com/ -H "Authorization: Bearer USDTOKEN"
Hello OpenShift!
apiVersion: serving.knative.dev/v1alpha1 kind: DomainMapping metadata: name: <domain_name> 1 namespace: <namespace> 2 spec: ref: name: <target_name> 3 kind: <target_type> 4 apiVersion: serving.knative.dev/v1
apiVersion: serving.knative.dev/v1alpha1 kind: DomainMapping metadata: name: example.com namespace: default spec: ref: name: example-service kind: Service apiVersion: serving.knative.dev/v1
apiVersion: serving.knative.dev/v1alpha1 kind: DomainMapping metadata: name: example.com namespace: default spec: ref: name: example-route kind: Route apiVersion: serving.knative.dev/v1
oc apply -f <filename>
kn domain create <domain_mapping_name> --ref <target_name>
kn domain create example.com --ref example-service
kn domain create <domain_mapping_name> --ref <ksvc:service_name:service_namespace>
kn domain create example.com --ref ksvc:example-service:example-namespace
kn domain create <domain_mapping_name> --ref <kroute:route_name>
kn domain create example.com --ref kroute:example-route
oc create secret tls <tls_secret_name> --cert=<path_to_certificate_file> --key=<path_to_key_file>
"networking.internal.knative.dev/certificate-uid": "<value>"
apiVersion: serving.knative.dev/v1alpha1 kind: DomainMapping metadata: name: <domain_name> namespace: <namespace> spec: ref: name: <service_name> kind: Service apiVersion: serving.knative.dev/v1 TLS block specifies the secret to be used tls: secretName: <tls_secret_name>
oc get domainmapping <domain_name>
NAME URL READY REASON example.com https://example.com True
curl https://<domain_name>