Use PropagateTLSInformation to whitelist client certificates

This article recommends the best practice on how to use the PropagateTLSInformation feature to secure API proxies by whitelisting specific client certificates signed by a trusted CA. This is an alternative to creating trust stores with long lists of specific client certificates, which can add overheads to the TLS handshake process.

Prerequisite: An API proxy with a virtual host configured for 2-way TLS (ClientAuthEnabled).

Steps

  1. Update VirtualHost properties

Adding the below two properties to the virtualhost will activate the documented population of TLS flow variables.

<PropagateTLSInformation> 
  <ConnectionProperties>true</ConnectionProperties> 
  <ClientProperties>true</ClientProperties> 
</PropagateTLSInformation>

The Edge UI doesn't currently support these flags. You must use the self-serve Virtual hosts API to make the update. For example:

curl -u $USERID:$USERPASSWORD -X PUT \
https://api.enterprise.apigee.com/v1/o/$org/e/$env/virtualhosts/secure-two-way \
-H "Content-Type: application/xml" \
-d '<VirtualHost name="secure-two-way">
    <HostAliases>
        <HostAlias>gsc-test-twoway.apigee.net</HostAlias>
    </HostAliases>
    <Interfaces/>
    <ListenOptions/>
    <Port>443</Port>
    <Properties>
        <Property name="ssl_protocols">TLSv1.2</Property>
    </Properties>
    <RetryOptions/>
    <SSLInfo>
        <Ciphers/>
        <ClientAuthEnabled>true</ClientAuthEnabled>
        <Enabled>true</Enabled>
        <IgnoreValidationErrors>false</IgnoreValidationErrors>
        <KeyAlias>freetrial</KeyAlias>
        <KeyStore>ref://freetrial</KeyStore>
        <Protocols/>
        <TrustStore>ref://edgemicro-ref</TrustStore>
    </SSLInfo>
    <PropagateTLSInformation>
      <ConnectionProperties>true</ConnectionProperties>
      <ClientProperties>true</ClientProperties>
  	</PropagateTLSInformation>
</VirtualHost>'

Note. ConnectionProperties is optional for the purposes of this tutorial.

Once these flags have been applied, the Edge router will then send HTTP headers to the message processor containing these values (example below). The MP will make them available as flow variables, so you don’t need to access the actual request.header.{header-name}. Instead, use the documented flow vars.

Please note, by default, these headers will be stripped off from the request before it goes to the target from release 190301 of cloud onwards.

Typically, the client certificate would be issued and signed by a Certificate Authority that is in the trust store of the Virtual Host. This trust store may contain an internal CA which is used to sign many x.509 certificates, of which only some should be able to access the proxy.

To get around this without having to load all those specific client certs into the trust store every time, we can whitelist the 'tls.client.s.dn' and/or 'tls.client.cert.fingerprint' in the actual proxy.

The best place to store the whitelist is in a KVM. Be wary, since client DN may contain commas. This means we are not able to use comma separated lists, but instead should choose some other separator and later separate it using a JavaScript callout policy. For fingerprints it is safe to use comma separated lists because these are SHA1 hashes which only have hex characters.

Ensure to set the TTL of the KVM depending on how quickly you want changes in the whitelist to take effect.

Once the KVM values are retrieved using a KVM policy, a Javascript callout policy can then be used to verify that the client cert is in that whitelist. If it is not, a flow variable can be set so that a subsequent RaiseFault Policy can raise an error and return an HTTP 403 Forbidden error to the client.

References:

https://docs.apigee.com/api-platform/fundamentals/virtual-host-property-reference

https://docs.apigee.com/api-platform/system-administration/tls-vars

https://apidocs.apigee.com/api/virtual-hosts

https://docs.apigee.com/api-platform/reference/policies/key-value-map-operations-policy

Comments
dchiesa1
Staff

Whoohoo! Great stuff Mark.

Another option I'd like to call out:

One might imagine binding a client-side cert to a Developer App (or key).

What I mean by "binding" is, your API Proxy can check that when an API Key is presented in a request, that the client-side cert used is the correct DN for that specific API Key.

Or, alternatively, if an OAuth token is presented in a request, the proxy can check that the token particular developer app that was used to generate the token has the specific DN registered.

The way to do this is to use the custom attribute on the developer app to store the acceptable DN, rather than the KVM.

Any questions on this twist to Mark's recommendation, let me know!

deboraelkin
Staff

Excellent article @Mark Eccles!

One thing I'd like to point out it that it's preferable to store the client's distinguished name (tls.client.s.dn) rather thant the certificate fingerprint (tls.client.cert.fingerprint), so that if a certificate expires, you don't need to update your KVM

stevescheider
New Member

Hello Mark,

Thanks for the post! Since the vhost is configured with a truststore for two-way TLS, wouldn't it perform the validation using the truststore and then check the values in the proxy policy flow? Your description makes it appear that the truststore lookup is not done in this configuration. I'd appreciate your clarification.

Thanks!

dchiesa1
Staff

Yes, apigee will validate the client's certificate via the Truststore.

What Mark described is, _after_ validation, Apigee can propagate the information contained in the just-validated certificate to the proxy flow.

The proxy can then.... do something with that information. Whatever it likes.

Version history
Last update:
‎08-25-2019 10:18 PM
Updated by: