Securing service traffic using service serving certificate secrets

    The controller uses the x509.SHA256WithRSA signature algorithm to generate service certificates.

    The generated certificate and key are in PEM format, stored in tls.crt and tls.key respectively, within a created secret. The certificate and key are automatically replaced when they get close to expiration.

    The service CA certificate, which issues the service certificates, is valid for 26 months and is automatically rotated when there is less than 13 months validity left. After rotation, the previous service CA configuration is still trusted until its expiration. This allows a grace period for all affected services to refresh their key material before the expiration. If you do not upgrade your cluster during this grace period, which restarts services and refreshes their key material, you might need to manually restart services to avoid failures after the previous service CA expires.

    Add a service certificate

    To secure communication to your service, generate a signed serving certificate and key pair into a secret in the same namespace as the service.

    The generated certificate is only valid for the internal service DNS name <service.name>.<service.namespace>.svc, and is only valid for internal communications. If your service is a headless service (no clusterIP value set), the generated certificate also contains a wildcard subject in the format of *.<service.name>.<service.namespace>.svc.

    Because the generated certificates contain wildcard subjects for headless services, you must not use the service CA if your client must differentiate between individual pods. In this case:

    • Generate individual TLS certificates by using a different CA.

    • Do not accept the service CA as a trusted CA for connections that are directed to individual pods and must not be impersonated by other pods. These connections must be configured to trust the CA that was used to generate the individual TLS certificates.

    Prerequisites:

    • You must have a service defined.

    Procedure

    1. Annotate the service with service.beta.openshift.io/serving-cert-secret-name:

      1. $ oc annotate service <service_name> \(1)
      2. service.beta.openshift.io/serving-cert-secret-name=<secret_name> (2)
      1Replace <service_name> with the name of the service to secure.
      2<secret_name> will be the name of the generated secret containing the certificate and key pair. For convenience, it is recommended that this be the same as <service_name>.

      For example, use the following command to annotate the service test1:

      1. $ oc annotate service test1 service.beta.openshift.io/serving-cert-secret-name=test1
    2. Examine the service to confirm that the annotations are present:

      1. $ oc describe service <service_name>

      Example output

      1. ...
      2. Annotations: service.beta.openshift.io/serving-cert-secret-name: <service_name>
      3. service.beta.openshift.io/serving-cert-signed-by: openshift-service-serving-signer@1556850837
      4. ...
    3. After the cluster generates a secret for your service, your Pod spec can mount it, and the pod will run after it becomes available.

    Additional resources

    Add the service CA bundle to a config map

    A pod can access the service CA certificate by mounting a ConfigMap object that is annotated with service.beta.openshift.io/inject-cabundle=true. Once annotated, the cluster automatically injects the service CA certificate into the service-ca.crt key on the config map. Access to this CA certificate allows TLS clients to verify connections to services using service serving certificates.

    After adding this annotation to a config map all existing data in it is deleted. It is recommended to use a separate config map to contain the service-ca.crt, instead of using the same config map that stores your pod configuration.

    Procedure

    1. Annotate the config map with service.beta.openshift.io/inject-cabundle=true:

      1. $ oc annotate configmap <config_map_name> \(1)
      2. service.beta.openshift.io/inject-cabundle=true
      1Replace <config_map_name> with the name of the config map to annotate.
      1. $ oc annotate configmap test1 service.beta.openshift.io/inject-cabundle=true
    2. View the config map to ensure that the service CA bundle has been injected:

      1. $ oc get configmap <config_map_name> -o yaml

      The CA bundle is displayed as the value of the service-ca.crt key in the YAML output:

      1. apiVersion: v1
      2. data:
      3. service-ca.crt: |
      4. -----BEGIN CERTIFICATE-----
      5. ...

    You can annotate an APIService object with service.beta.openshift.io/inject-cabundle=true to have its spec.caBundle field populated with the service CA bundle. This allows the Kubernetes API server to validate the service CA certificate used to secure the targeted endpoint.

    Procedure

    1. Annotate the API service with service.beta.openshift.io/inject-cabundle=true:

      1. $ oc annotate apiservice <api_service_name> \(1)
      2. service.beta.openshift.io/inject-cabundle=true
      1Replace <api_service_name> with the name of the API service to annotate.

      For example, use the following command to annotate the API service test1:

    2. View the API service to ensure that the service CA bundle has been injected:

      The CA bundle is displayed in the spec.caBundle field in the YAML output:

      1. apiVersion: apiregistration.k8s.io/v1
      2. kind: APIService
      3. annotations:
      4. service.beta.openshift.io/inject-cabundle: "true"
      5. ...
      6. spec:
      7. caBundle: <CA_BUNDLE>
      8. ...

    Add the service CA bundle to a custom resource definition

    You can annotate a CustomResourceDefinition (CRD) object with service.beta.openshift.io/inject-cabundle=true to have its spec.conversion.webhook.clientConfig.caBundle field populated with the service CA bundle. This allows the Kubernetes API server to validate the service CA certificate used to secure the targeted endpoint.

    The service CA bundle will only be injected into the CRD if the CRD is configured to use a webhook for conversion. It is only useful to inject the service CA bundle if a CRD’s webhook is secured with a service CA certificate.

    Procedure

    1. Annotate the CRD with service.beta.openshift.io/inject-cabundle=true:

      1. $ oc annotate crd <crd_name> \(1)
      2. service.beta.openshift.io/inject-cabundle=true
      1Replace <crd_name> with the name of the CRD to annotate.

      For example, use the following command to annotate the CRD test1:

      1. $ oc annotate crd test1 service.beta.openshift.io/inject-cabundle=true
    2. View the CRD to ensure that the service CA bundle has been injected:

      1. $ oc get crd <crd_name> -o yaml

      The CA bundle is displayed in the spec.conversion.webhook.clientConfig.caBundle field in the YAML output:

      1. apiVersion: apiextensions.k8s.io/v1
      2. kind: CustomResourceDefinition
      3. metadata:
      4. annotations:
      5. service.beta.openshift.io/inject-cabundle: "true"
      6. ...
      7. spec:
      8. conversion:
      9. strategy: Webhook
      10. webhook:
      11. clientConfig:
      12. caBundle: <CA_BUNDLE>
      13. ...

    Add the service CA bundle to a mutating webhook configuration

    You can annotate a MutatingWebhookConfiguration object with service.beta.openshift.io/inject-cabundle=true to have the clientConfig.caBundle field of each webhook populated with the service CA bundle. This allows the Kubernetes API server to validate the service CA certificate used to secure the targeted endpoint.

    Do not set this annotation for admission webhook configurations that need to specify different CA bundles for different webhooks. If you do, then the service CA bundle will be injected for all webhooks.

    Procedure

    1. Annotate the mutating webhook configuration with service.beta.openshift.io/inject-cabundle=true:

      1. $ oc annotate mutatingwebhookconfigurations <mutating_webhook_name> \(1)
      2. service.beta.openshift.io/inject-cabundle=true

      For example, use the following command to annotate the mutating webhook configuration test1:

      1. $ oc annotate mutatingwebhookconfigurations test1 service.beta.openshift.io/inject-cabundle=true
    2. View the mutating webhook configuration to ensure that the service CA bundle has been injected:

      1. $ oc get mutatingwebhookconfigurations <mutating_webhook_name> -o yaml

      The CA bundle is displayed in the clientConfig.caBundle field of all webhooks in the YAML output:

      1. apiVersion: admissionregistration.k8s.io/v1
      2. kind: MutatingWebhookConfiguration
      3. metadata:
      4. service.beta.openshift.io/inject-cabundle: "true"
      5. ...
      6. webhooks:
      7. - myWebhook:
      8. - v1beta1
      9. clientConfig:
      10. caBundle: <CA_BUNDLE>

    Do not set this annotation for admission webhook configurations that need to specify different CA bundles for different webhooks. If you do, then the service CA bundle will be injected for all webhooks.

    Procedure

    1. Annotate the validating webhook configuration with service.beta.openshift.io/inject-cabundle=true:

      1. $ oc annotate validatingwebhookconfigurations <validating_webhook_name> \(1)
      2. service.beta.openshift.io/inject-cabundle=true
      1Replace <validating_webhook_name> with the name of the validating webhook configuration to annotate.

      For example, use the following command to annotate the validating webhook configuration test1:

    2. View the validating webhook configuration to ensure that the service CA bundle has been injected:

      1. $ oc get validatingwebhookconfigurations <validating_webhook_name> -o yaml

      The CA bundle is displayed in the clientConfig.caBundle field of all webhooks in the YAML output:

      1. apiVersion: admissionregistration.k8s.io/v1
      2. kind: ValidatingWebhookConfiguration
      3. metadata:
      4. annotations:
      5. service.beta.openshift.io/inject-cabundle: "true"
      6. ...
      7. webhooks:
      8. - myWebhook:
      9. - v1beta1
      10. clientConfig:
      11. caBundle: <CA_BUNDLE>
      12. ...

    Manually rotate the generated service certificate

    You can rotate the service certificate by deleting the associated secret. Deleting the secret results in a new one being automatically created, resulting in a new certificate.

    Prerequisites

    • A secret containing the certificate and key pair must have been generated for the service.

    Procedure

    1. Examine the service to determine the secret containing the certificate. This is found in the serving-cert-secret-name annotation, as seen below.

      1. $ oc describe service <service_name>

      Example output

      1. ...
      2. service.beta.openshift.io/serving-cert-secret-name: <secret>
      3. ...
    2. Delete the generated secret for the service. This process will automatically recreate the secret.

      1. $ oc delete secret <secret> (1)
      1Replace <secret> with the name of the secret from the previous step.
    3. Confirm that the certificate has been recreated by obtaining the new secret and examining the AGE.

      1. $ oc get secret <service_name>

      Example output

      1. NAME TYPE DATA AGE
      2. <service.name> kubernetes.io/tls 2 1s

    Manually rotate the service CA certificate

    The service CA is valid for 26 months and is automatically refreshed when there is less than 13 months validity left.

    If necessary, you can manually refresh the service CA by using the following procedure.

    A manually-rotated service CA does not maintain trust with the previous service CA. You might experience a temporary service disruption until the pods in the cluster are restarted, which ensures that pods are using service serving certificates issued by the new service CA.

    Prerequisites

    • You must be logged in as a cluster admin.

    Procedure

    1. View the expiration date of the current service CA certificate by using the following command.

      1. $ oc get secrets/signing-key -n openshift-service-ca \
      2. -o template='{{index .data "tls.crt"}}' \
      3. | base64 --decode \
      4. | openssl x509 -noout -enddate
    2. Manually rotate the service CA. This process generates a new service CA which will be used to sign the new service certificates.

      1. $ oc delete secret/signing-key -n openshift-service-ca
    3. To apply the new certificates to all services, restart all the pods in your cluster. This command ensures that all services use the updated certificates.

      1. $ for I in $(oc get ns -o jsonpath='{range .items[*]} {.metadata.name}{"\n"} {end}'); \
      2. do oc delete pods --all -n $I; \
      3. done