Chapter 9. Security

9.1. Configuring JSON Web Token authentication for Knative services

After the Service Mesh integration with OpenShift Serverless and Kourier has been configured on your cluster, you can enable JSON Web Token (JWT) authentication for your Knative services.

9.1.1. Enabling sidecar injection for a Knative service

You can add the sidecar.istio.io/inject="true" annotation to a Knative service to enable sidecar injection for that 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.

Procedure

  1. Add the sidecar.istio.io/inject="true" annotation to your Service resource:

    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 as OpenShift Serverless versions 1.14.0 and higher use an HTTP probe as the readiness probe for Knative services by default.
  2. Apply your Service resource YAML file:

    $ oc apply -f <filename>

9.1.2. Using JSON Web Token authentication with Service Mesh 2.x and OpenShift Serverless

Procedure

  1. 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
  2. Apply the RequestAuthentication resource:

    $ oc apply -f <filename>
  3. 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.
  4. Apply the AuthorizationPolicy resource:

    $ oc apply -f <filename>
  5. 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"]
  6. Apply the AuthorizationPolicy resource:

    $ oc apply -f <filename>

Verification

  1. If you try to use a curl request to get the Knative service URL, it is denied:

    Example command

    $ curl http://hello-example-1-default.apps.mycluster.example.com/

    Example output

    RBAC: access denied

  2. Verify the request with a valid JWT.

    1. Get the valid JWT token:

      $ TOKEN=$(curl https://raw.githubusercontent.com/istio/istio/release-1.8/security/tools/jwt/samples/demo.jwt -s) && echo "$TOKEN" | cut -d '.' -f2 - | base64 --decode -
    2. Access the service by using the valid token in the curl request header:

      $ curl -H "Authorization: Bearer $TOKEN"  http://hello-example-1-default.apps.example.com

      The request is now allowed:

      Example output

      Hello OpenShift!

9.1.3. Using JSON Web Token authentication with Service Mesh 1.x and OpenShift Serverless

Procedure

  1. 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.
  2. Apply the Policy resource:

    $ oc apply -f <filename>

Verification

  1. If you try to use a curl request to get the Knative service URL, it is denied:

    $ curl http://hello-example-default.apps.mycluster.example.com/

    Example output

    Origin authentication failed.

  2. Verify the request with a valid JWT.

    1. Get the valid JWT token:

      $ TOKEN=$(curl https://raw.githubusercontent.com/istio/istio/release-1.6/security/tools/jwt/samples/demo.jwt -s) && echo "$TOKEN" | cut -d '.' -f2 - | base64 --decode -
    2. Access the service by using the valid token in the curl request header:

      $ curl http://hello-example-default.apps.mycluster.example.com/ -H "Authorization: Bearer $TOKEN"

      The request is now allowed:

      Example output

      Hello OpenShift!

9.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, 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.

9.2.1. Creating a custom domain mapping

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.
  • 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

  1. 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-domain
     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-domain
     namespace: default
    spec:
     ref:
       name: example-route
       kind: Route
       apiVersion: serving.knative.dev/v1

  2. Apply the DomainMapping CR as a YAML file:

    $ oc apply -f <filename>

9.2.2. Creating a custom domain mapping by using the Knative CLI

You can use the 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.

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. The examples in the following procedure show the prefixes for mapping to 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 kn CLI tool.

Procedure

  • Map a domain to a CR in the current namespace:

    $ kn domain create <domain_mapping_name> --ref <target_name>

    Example command

    $ kn domain create example-domain-map --ref example-service

  • Map a domain to a Knative service in a specified namespace:

    $ kn domain create <domain_mapping_name> --ref <ksvc:service_name:service_namespace>

    Example command

    $ kn domain create example-domain-map --ref ksvc:example-service:example-namespace

  • Map a domain to a Knative route:

    $ kn domain create <domain_mapping_name> --ref <kroute:route_name>

    Example command

    $ kn domain create example-domain-map --ref kroute:example-route

9.2.3. Creating a custom domain mapping by using the web console

You can use the Administrator or Developer perspective of the OpenShift Container Platform web console to create a custom domain mapping for a Knative service.

9.2.3.1. Mapping a custom domain to a service by using the Administrator perspective

If you have cluster administrator permissions, you can create a DomainMapping custom resource (CR) by using the Administrator perspective in the OpenShift Container Platform web console.

Prerequisites

  • You have logged in to the web console.
  • You are in the Administrator perspective.
  • You have installed the OpenShift Serverless Operator.
  • You have installed Knative Serving.
  • 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

  1. Navigate to CustomResourceDefinitions and use the search box to find the DomainMapping custom resource definition (CRD).
  2. Click the DomainMapping CRD, then navigate to the Instances tab.
  3. Click Create DomainMapping.
  4. Modify the YAML for the DomainMapping CR so that it includes the following information for your instance:

    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 domain mapping to a Knative service

    apiVersion: serving.knative.dev/v1alpha1
    kind: DomainMapping
    metadata:
     name: custom-ksvc-domain.example.com
     namespace: default
    spec:
     ref:
       name: example-service
       kind: Service
       apiVersion: serving.knative.dev/v1

Verification

  • Access the custom domain by using a curl request. For example:

    Example command

    $ curl custom-ksvc-domain.example.com

    Example output

    Hello OpenShift!

9.2.3.2. Mapping a custom domain to a service by using the Developer perspective

You can use the Developer perspective of the OpenShift Container Platform web console to map a DomainMapping custom resource (CR) to a Knative service.

Prerequisites

  • You have logged in to the web console.
  • You are in the Developer perspective.
  • The OpenShift Serverless Operator and Knative Serving are installed on your cluster. This must be completed by a cluster administrator.
  • 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

  1. Navigate to the Topology page.
  2. Right-click on the service that you want to map to a domain, and select the Edit option that contains the service name. For example, if the service is named example-service, select the Edit example-service option.
  3. In the Advanced options section, click Show advanced Routing options.

    1. If the domain mapping CR that you want to map to the service already exists, you can select it from the Domain mapping drop-down.
    2. If you want to create a new domain mapping CR, type the domain name into the box, and select the Create option. For example, if you type in example.com, the Create option is Create "example.com".
  4. Click Save to save the changes to your service.

Verification

  1. Navigate to the Topology page.
  2. Click on the service that you have created.
  3. In the Resources tab of the service information window, you can see the domain you have mapped to the service listed under Domain mappings.

9.3. Using a custom TLS certificate for domain mapping

You can use an existing TLS certificate with a DomainMapping custom resource (CR) to secure the mapped service.

Prerequisites

9.3.1. Adding a custom TLS certificate to a DomainMapping CR

You can add an existing TLS certificate with a DomainMapping custom resource (CR) to secure the mapped service.

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.

Procedure

  1. Create a Kubernetes TLS secret:

    $ oc create secret tls <tls_secret_name> --cert=<path_to_certificate_file> --key=<path_to_key_file>
  2. Update the DomainMapping CR to use the TLS secret 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

  1. Verify that the DomainMapping CR status is True, and that the URL column of the output shows the mapped domain with the scheme https:

    $ oc get domainmapping <domain_name>

    Example output

    NAME                      URL                               READY   REASON
    example.com               https://example.com               True

  2. Optional: If the service is exposed publicly, verify that it is available by running the following command:

    $ curl https://<domain_name>

    If the certificate is self-signed, skip verification by adding the -k flag to the curl command.

9.4. Security configuration for Knative Kafka

In production, Kafka clusters are often secured using the TLS or SASL authentication methods. This section shows how to configure a Kafka channel to work against a protected Red Hat AMQ Streams cluster using TLS or SASL.

Note

If you choose to enable SASL, Red Hat recommends to also enable TLS.

9.4.1. Configuring TLS authentication

Prerequisites

  • A Kafka cluster CA certificate as a .pem file.
  • A Kafka cluster client certificate and key as .pem files.

Procedure

  1. Create the certificate files as secrets in your chosen namespace:

    $ kubectl create secret -n <namespace> generic <kafka_auth_secret> \
      --from-file=ca.crt=caroot.pem \
      --from-file=user.crt=certificate.pem \
      --from-file=user.key=key.pem
    Important

    Use the key names ca.crt, user.crt, and user.key. Do not change them.

  2. Start editing the KnativeKafka custom resource:

    $ oc edit knativekafka
  3. Reference your secret and the namespace of the secret:

    apiVersion: operator.serverless.openshift.io/v1alpha1
    kind: KnativeKafka
    metadata:
      namespace: knative-eventing
      name: knative-kafka
    spec:
      channel:
        authSecretName: <kafka_auth_secret>
        authSecretNamespace: <kafka_auth_secret_namespace>
        bootstrapServers: <bootstrap_servers>
        enabled: true
      source:
        enabled: true
    Note

    Make sure to specify the matching port in the bootstrap server.

    For example:

    apiVersion: operator.serverless.openshift.io/v1alpha1
    kind: KnativeKafka
    metadata:
      namespace: knative-eventing
      name: knative-kafka
    spec:
      channel:
        authSecretName: tls-user
        authSecretNamespace: kafka
        bootstrapServers: eventing-kafka-bootstrap.kafka.svc:9094
        enabled: true
      source:
        enabled: true

Additional resources

9.4.2. Configuring SASL authentication

Prerequisites

  • A username and password for the Kafka cluster.
  • Choose the SASL mechanism to use, for example PLAIN, SCRAM-SHA-256, or SCRAM-SHA-512.
  • If TLS is enabled, you also need the ca.crt certificate file for the Kafka cluster.
Note

Red Hat recommends to enable TLS in addition to SASL.

Procedure

  1. Create the certificate files as secrets in your chosen namespace:

    $ oc create secret --namespace <namespace> generic <kafka_auth_secret> \
      --from-file=ca.crt=caroot.pem \
      --from-literal=password="SecretPassword" \
      --from-literal=saslType="SCRAM-SHA-512" \
      --from-literal=user="my-sasl-user"
    Important

    Use the key names ca.crt, password, and saslType. Do not change them.

  2. Start editing the KnativeKafka custom resource:

    $ oc edit knativekafka
  3. Reference your secret and the namespace of the secret:

    apiVersion: operator.serverless.openshift.io/v1alpha1
    kind: KnativeKafka
    metadata:
      namespace: knative-eventing
      name: knative-kafka
    spec:
      channel:
        authSecretName: <kafka_auth_secret>
        authSecretNamespace: <kafka_auth_secret_namespace>
        bootstrapServers: <bootstrap_servers>
        enabled: true
      source:
        enabled: true
    Note

    Make sure to specify the matching port in the bootstrap server.

    For example:

    apiVersion: operator.serverless.openshift.io/v1alpha1
    kind: KnativeKafka
    metadata:
      namespace: knative-eventing
      name: knative-kafka
    spec:
      channel:
        authSecretName: scram-user
        authSecretNamespace: kafka
        bootstrapServers: eventing-kafka-bootstrap.kafka.svc:9093
        enabled: true
      source:
        enabled: true

Additional resources

9.4.3. Configuring SASL authentication using public CA certificates

If you want to use SASL with public CA certificates, you must use the tls.enabled=true flag, rather than the ca.crt argument, when creating the secret. For example:

$ oc create secret --namespace <namespace> generic <kafka_auth_secret> \
  --from-literal=tls.enabled=true \
  --from-literal=password="SecretPassword" \
  --from-literal=saslType="SCRAM-SHA-512" \
  --from-literal=user="my-sasl-user"

Additional resources