Set up mutual TLS with user-provided certificates

This page provides instructions for creating a root certificate and a signed intermediate certificate, and then uploading those certificates to a Certificate Manager TrustConfig resource. If you have existing certificates to upload, skip the steps that create new certificates.

You also create the network security resources required for configuring mutual TLS (mTLS) for Application Load Balancers. The instructions use OpenSSL to create the root and intermediate certificates.

Before you begin

Permissions

To get the permissions that you need to complete this guide, ask your administrator to grant you the following IAM roles on the project:

  • To create load balancer resources such as TargetHTTPSProxy: Compute Load Balancer Admin (roles/compute.loadBalancerAdmin)
  • To use Certificate Manager resources: Certificate Manager Owner (roles/certificatemanager.owner)
  • To create security and networking components: Compute Network Admin (roles/compute.networkAdmin) and Compute Security Admin (roles/compute.securityAdmin)
  • To create a project (optional): Project Creator (roles/resourcemanager.projectCreator)

For more information about granting roles, see Manage access.

You might also be able to get the required permissions through custom roles or other predefined roles.

Generate a key and signed certificates

This section uses openssl commands to create root and intermediate certificates.

Use the following commands to generate a root certificate and a signed intermediate certificate with valid keyUsage and extendedKeyUsage fields.

  1. Create a sample example.cnf file with the minimum configuration required to create valid signing certificates. You can edit this file if you want to set any additional fields on these certificates.

    cat > example.cnf << EOF
    [req]
    distinguished_name = empty_distinguished_name
    [empty_distinguished_name]
    # Kept empty to allow setting via -subj command line arg.
    [ca_exts]
    basicConstraints=critical,CA:TRUE
    keyUsage=keyCertSign
    extendedKeyUsage=clientAuth
    EOF
    
  2. Create the root certificate.

    openssl req -x509 \
        -new -sha256 -newkey rsa:2048 -nodes \
        -days 3650 -subj '/CN=root' \
        -config example.cnf \
        -extensions ca_exts \
        -keyout root.key -out root.cert
    
  3. Create the signing request for the intermediate certificate.

    openssl req \
        -new -sha256 -newkey rsa:2048 -nodes \
        -subj '/CN=int' \
        -config example.cnf \
        -extensions ca_exts \
        -keyout int.key -out int.req
    
  4. Create the intermediate certificate.

    openssl x509 -req \
        -CAkey root.key -CA root.cert \
        -set_serial 1 \
        -days 3650 \
        -extfile example.cnf \
        -extensions ca_exts \
        -in int.req -out int.cert
    

Generate an allowlisted certificate

This section uses openssl commands to create a sample allowlisted certificate.

Use the following commands to generate an allowlisted certificate.

   openssl req -x509 \
       -new -sha256 -newkey rsa:2048 -nodes \
       -days 3650 -subj '/CN=localhost' \
       -keyout allowlisted.key -out allowlisted.cert

Format the certificates

To include new or existing certificates in a TrustStore, format the certificates into a single line and store them in environment variables, so that they can be read into the YAML file. Use the following commands to format the certificates and store them in environment variables:

export ROOT_CERT=$(cat root.cert | sed 's/^[ ]*//g' | tr '\n' $ | sed 's/\$/\\n/g')
export INTERMEDIATE_CERT=$(cat int.cert | sed 's/^[ ]*//g' | tr '\n' $ | sed 's/\$/\\n/g')

To include new or existing allowlisted certificates in a trust config, format the certificates into a single line and store them in environment variables, so that they can be read into the YAML file. Use the following commands to format the allowlisted certificates and store them in environment variables:

export ALLOWLISTED_CERT=$(cat allowlisted.cert | sed 's/^[ ]*//g' | tr '\n' $ | sed 's/\$/\\n/g')

Create a TrustConfig resource

Create a Certificate Manager TrustConfig resource that represents your PKI. This example TrustConfig resource contains a trust store with two trust anchors and two intermediate CA certificates. It reads the certificate content from the environment variables created in the previous Format the certificates step.

To create a trust store with additional trust anchors or intermediate CA certificates, add pemCertificate rows in the appropriate section. If you have fewer trust anchors or intermediate CA certificates, remove the unneeded lines.

This example TrustConfig resource contains an allowlisted certificate. You can specify multiple allowlisted certificates by using multiple instances of the pemCertificate field.

In the following steps, replace TRUST_CONFIG_NAME with the name of the TrustConfig resource:

  1. To create the trust_config.yaml file with a trust store, use the following command:

    cat << EOF > trust_config.yaml
    trustStores:
    - trustAnchors:
      - pemCertificate: "${ROOT_CERT?}"
      - pemCertificate: "${ROOT_CERT_2?}"
      intermediateCas:
      - pemCertificate: "${INTERMEDIATE_CERT?}"
      - pemCertificate: "${INTERMEDIATE_CERT_2?}"
    EOF
    
  2. Optional: To create the trust_config.yaml file with an allowlisted certificate, use the following command:

    cat << EOF > trust_config.yaml
     allowlistedCertificates:
     - pemCertificate: "${ALLOWLISTED_CERT?}"
    EOF
    
  3. To create the Certificate Manager TrustConfig resources, use the gcloud certificate-manager trust-configs import command:

    global

    For external Application Load Balancers and cross-region internal Application Load Balancers, use this command:

    gcloud certificate-manager trust-configs import TRUST_CONFIG_NAME  \
       --source=trust_config.yaml
    

    regional

    For regional external Application Load Balancers and regional internal Application Load Balancers, use this command:

    gcloud certificate-manager trust-configs import TRUST_CONFIG_NAME  \
       --source=trust_config.yaml \
       --location=REGION
    

Create the network security resources

A server TLS policy (ServerTLSPolicy network security resource) lets you specify the server-side TLS mode and the TrustConfig resource to use when validating client certificates. When the client presents an invalid certificate or no certificate to the load balancer, the clientValidationMode specifies how the client connection is handled.

  • When the clientValidationMode is set to ALLOW_INVALID_OR_MISSING_CLIENT_CERT, all requests are passed to the backend even if the validation fails or the client certificate is missing.
  • When the clientValidationMode is set to REJECT_INVALID, only requests that supply a client certificate that can be validated against a TrustConfig resource are passed to the backend.

To create the ServerTLSPolicy resource, complete the following steps:

  1. Based on how you want to handle the connection, select one of the following options.

    In the following steps, replace SERVER_TLS_POLICY_NAME with the name of the server TLS policy, and replace PROJECT_ID with the ID of your Google Cloud project.

    • Option 1: clientValidationMode is set to ALLOW_INVALID_OR_MISSING_CLIENT_CERT.

      To create the server_tls_policy.yaml file, use the following command:

      global

      For external Application Load Balancers and cross-region internal Application Load Balancers, use the command:

      cat << EOF > server_tls_policy.yaml
      name: SERVER_TLS_POLICY_NAME
      mtlsPolicy:
        clientValidationMode: ALLOW_INVALID_OR_MISSING_CLIENT_CERT
        clientValidationTrustConfig: projects/PROJECT_ID/locations/global/trustConfigs/TRUST_CONFIG_NAME
      EOF
      

      regional

      For regional external Application Load Balancers and regional internal Application Load Balancers, use the command:

      cat << EOF > server_tls_policy.yaml
      name: SERVER_TLS_POLICY_NAME
      mtlsPolicy:
        clientValidationMode: ALLOW_INVALID_OR_MISSING_CLIENT_CERT
        clientValidationTrustConfig: projects/PROJECT_ID/locations/REGION/trustConfigs/TRUST_CONFIG_NAME
      EOF
      
    • Option 2: clientValidationMode is set to REJECT_INVALID.

      To create the server_tls_policy.yaml file, use the following command:

      global

      For external Application Load Balancers and cross-region internal Application Load Balancers, use the command:

      cat << EOF > server_tls_policy.yaml
      name: SERVER_TLS_POLICY_NAME
      mtlsPolicy:
        clientValidationMode: REJECT_INVALID
        clientValidationTrustConfig: projects/PROJECT_ID/locations/global/trustConfigs/TRUST_CONFIG_NAME
      EOF
      

      regional

      For regional external Application Load Balancers and regional internal Application Load Balancers, use the command:

      cat << EOF > server_tls_policy.yaml
      name: SERVER_TLS_POLICY_NAME
      mtlsPolicy:
        clientValidationMode: REJECT_INVALID
        clientValidationTrustConfig: projects/PROJECT_ID/locations/REGION/trustConfigs/TRUST_CONFIG_NAME
      EOF
      
  2. To create the ServerTlsPolicy resource, use the gcloud network-security server-tls-policies import command:

    global

    For external Application Load Balancers and cross-region internal Application Load Balancers, use the command:

    gcloud network-security server-tls-policies import SERVER_TLS_POLICY_NAME \
      --source=server_tls_policy.yaml \
      --location=global
    

    regional

    For regional external Application Load Balancers and regional internal Application Load Balancers, use the command:

    gcloud network-security server-tls-policies import SERVER_TLS_POLICY_NAME \
      --source=server_tls_policy.yaml \
      --location=REGION
    

For more information, see MTLS client validation modes.

Sign a client key with the intermediate certificate

This section provides an additional configuration option to generate a leaf certificate. If you have already created a TrustConfig resource by using intermediate certificates (int.cert and int.key), use the following instructions:

  1. Create a client key configuration file.

    cat > client.config << EOF
    [req]
    default_bits              = 2048
    req_extensions            = extension_requirements
    distinguished_name        = dn_requirements
    prompt                    = no
    
    [extension_requirements]
    basicConstraints          = critical, CA:FALSE
    keyUsage                  = critical, nonRepudiation, digitalSignature, keyEncipherment
    extendedKeyUsage          = clientAuth
    
    [dn_requirements]
    countryName               = US
    stateOrProvinceName       = California
    localityName              = San Francisco
    0.organizationName        = example
    organizationalUnitName    = test
    commonName                = test.example.com
    emailAddress              = test@example.com
    
    EOF
    

    If you want to have a SPIFFE identity attached:

    • Add a subjectAltName to your [extension_requirements] section as follows:

      subjectAltName            = @sans_list
      
    • Add a new section at the bottom of the client.config file with the following:

      [sans_list]
      URI.1                     = spiffe://example.com/test-identity
      
  2. Sign the key.

    openssl req -new -keyout client.key -out client.csr -config client.config
    
    openssl x509 -req -in client.csr -out client.cert -extfile client.config -extensions extension_requirements -days 365 -CA int.cert -CAkey int.key
    
  3. To test send a curl request to the load balancer's IP address.

    curl -v -k --key client.key --cert client.cert https://IP_ADDRESS
    

    Replace IP_ADDRESS with the load balancer's IP address.

What's next