• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

TykTechnologies / tyk / 3458
50%
master: %

Build:
Build:
LAST BUILD BRANCH: v2.9.4.8
DEFAULT BRANCH: master
Ran 16 Nov 2017 08:22PM UTC
Jobs 1
Files 82
Run time 6s
Badge
Embed ▾
README BADGES
x

If you need to use a raster PNG badge, change the '.svg' to '.png' in the link

Markdown

Textile

RDoc

HTML

Rst

pending completion
3458

push

travis-ci

web-flow
Mutual TLS (#828)

Fixes #357

Overview

In most of the cases when you try to access secured HTTPS/TLS endpoint, you experience only client-side check of the server certificate. Purpose of this check is to ensure that no fraud involved and data transfer between client and the server is encrypted.

In fact, TLS standard allows specifying client TLS certificate, so the server can accept connections only from clients which certificates registered at server certificate authority. In other words, clients are required to provide certificate and this certificate should be whitelisted on the server. This is what we call “Mutual TLS”, e.g. both sides require and verify certificates.

This change introduce Mutual TLS on the following layers:

Authorization (whitelisting certificates on API level)
Authentification (creating keys based on certificates)
Upstream access (including JSVM)
Control API
Dashboard and MDCB API
Basic architecture

There is 2 types of certificates: with and without private keys. Certificates without public keys used for authorization and authentification. Certificates with private keys used for upstream access, and server certificates: in other words when we need sign and encrypt request or response.

We support only certificates in PEM format. Nice bonus of PEM that it allows having multiple entries inside same file. It helps simplify the logic: certificate with private keys stored in the same file.

This PR adds new certs package, introducing CertificateManager API, which handle all the certificate parsing, storage and retrieval logic. Certificates can be stored inside Redis or plain files. Certificates stored in Redis identified by their SHA256 fingerprint. Worth noticing that x509 certificate format implies that fingerprint is already embed into certificate, same as information about algorithm which was used to generate fingerprint. Tyk certificate storage do not use embed certificate fingeprints, and insted always use SHA256 algorithm all the time.

Certificate management API

Using new Gateway API you can create, remove, list, and see info about certificates.

Create: POST /tyk/certs with PEM body. Returns {"id": "<cert-id>", ...}
Delete: DELETE /tyk/certs/<cert-id>
Get info: GET /tyk/certs/<cert-id>. Return meta info about certificate, smth like this: {"id": "<cert-id>", "has_private_key": false, "subject": {"common_name": "<cn>"...}...}.
Get info about multiple certificate: GET /tyk/certs/<cert-id1>,<cert-id2>,<cert-id3>. Returns array of meta info objects, similar to above.
List all certificate IDs: GET /tyk/certs. Returns: {"certs": ["<cert-id1>", "<cert-id2>", ...]}
Note that this API works only with Redis certificates, and will not list of get info for file-based certs.

In addition, you may notice that you can't get raw certificate back, only its meta information. It is expected to improve security.

Certificates with private keys have special treatment and encoded before storing in redis. On certificate creation process, it first analyzes all PEM records, and if private certificate found, it decompose PEM, encode Private key with AES256, and encoded to PEM back. For encoding secret it use security.private_certificate_encoding_secret from tyk.conf, and if it is empty, fallbacks to secret value.

Authorization

Added 2 new fields to API definition. First is use_mutual_tls_auth, boolean field, which enable client certificate check for given API. Second is client_certificates, which is a string array, with list of certificate ids. As mentioned previously, ids can be either SHA256 fingerprints, if certificate stored in Redis, or file paths.

When use_mutual_tls_auth set to true Gateway starts requiring and verifying client certificates, against list defined in client_certificates field. Client without certificate or with wrong certificate will be rejected.

It is imporant to mention that rejecting client during TLS handshake require that each API have a separate domain. However Tyk provides fallback when API have no separate domain, and additionaly verify client certificate on HTTP level. Using such fallback enables Mutual TLS for multiple APIs, with different listen paths, on the same domain.

In addition you can set global list of client certificates, available to each API, using security.certificates.apis string array option, in tyk configuration file.

Authentification

You can create a key based on a provided certificate, and user will be authentificated based on client certificate. From technical point of view this is extension of "Auth token" authentication mode. You need to enable certificate check, by setting API definition auth.use_certificate boolean variable to true. After, when creating a key, you need to set certificate field to the existing certificate ID or path. Such keys has special treatment, and key ID will be generated as OrgID + SHA256(certificate). "Auth token" middleware, if auth.use_certificate is enabled, and key is not provided via headers or url, will try to find key based on client certificate.

Upstream access

You can specify certificates IDs per hostname, on API or global level.
In both cases, it is a hash when a key is a hostname, and value is certificate ID.
For API you should set upsteam_certificates to following format: {"example.com": "<cert-id>"}. For the global level it is same, but should be specified via security.certificates.upstream field in tyk.conf, same format.

For each HTTP call including reverse proxy, or JSVM http call, or Dashboard API call, Tyk checks agains maps mentioned above, and apply client certificate.

It is possible to specify * instead of hostname name, to match all domains.

Worth noticing that hostname name can include port if request made via non-standard HTTP port.

Control API

You can protect Control API with mutual TLS, separately from APIs. Similar to authentification it will work in both cases when control API bind on separate domain or on the same as rest of APIs. In the last case it will fall back to validation on HTTP level.

Allowed certificate IDs should be specified via security.certificates.control_api configuration option in tyk.conf.

Dashboard and MDCB API

Dashboard and MDCB APIs can be protected with mutual TLS, and you can specify client certificates used for talking with this APIs. Here tyk utilize upstream functionality mentioned above, so you just need to specify this certificates via security.certificates.upstream configuration option.

Changes in server certificates

Right now to specify certificates for tyk server itself, you need to use http_server_options.certificates config option in following format: [{"domain_name": "<domain>", "cert_file": "<path>", "key_file": "<path>"}].

In order to follow new architecture, and support certificates via PEM format, and certificates stored in Redis, you now can use new http_server_options.ssl_certificates option, which is just list of certificate IDs, similar to functionality described in previous paragraphs. Note that "domain_name" field is gone, and now it follows the guidelines, and just reads "common_name" or "dns_names" fields from certificate itself.

The old configuration option is still supported and will be marked as legacy.

Technical implementation

Go 1.8 added a handy helper for tls.Config called GetConfigForClient, which allow loading different TLS configurations for each client based on TLS handshake info. The only attribute of handshake payload can be used in our case is ServerName.
Note: TLS is separate protocol from HTTP, so it does not include the notion of hostname (only IP), and ServerName is actually SNI extension added to TLS quite long ago and supported by most of the clients.

By having access to ServerName we can enable Mutual TLS per API, based on APISpec.Domain. The only requirement is that each API which uses Mutual TLS should have own domain.

In cases when API domain is not specified or SNI extension is not provided by the client (so we do not know hostname), Tyk fallback to HTTP validation middleware, which checks client certificate via request.TLS.PeerCertificates variable.

Testing

You can create self-signed client and server certificates with this command: openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365

For the server in common name specify domain, or just pass -subj "/CN=localhost" to OpenSSL command. After, follow https://www.tyk.io/docs/security/tls-and-ssl/ guide.

To get certificate SHA256 fingerprint use following command:
openssl x509 -noout -fingerprint -sha256 -inform pem -in <cert>

If you testing using curl command may look like: curl —cert client_cert.pem --key client_key.pem https://localhost:8181

Be aware that Chrome 58 and after, may require a bit more complex commands, since it requires server certificate to have SNI DNS extension, and the command will look like:

openssl req \
    -newkey rsa:2048 \
    -x509 \
    -nodes \
    -keyout server.pem \
    -new \
    -out server.pem \
    -subj /CN=localhost \
    -reqexts SAN \
    -extensions SAN \
    -config <(cat /System/Library/OpenSSL/openssl.cnf \
        <(printf '[SAN]\nsubjectAltName=DNS:localhost')) \
    -sha256 \
    -days 3650
Where you should replayce /System/Library/OpenSSL/openssl.cnf to your path, and localhost to your domain.

Since all certificate ID configuration options is just arrays, you can set them via environment variables, like this: TYK_GW_SECUIRITY_CERTIFICATES_CONTROLAPI="<cert1>,<cert2>", separated by commas. Docker users should be really happy with such approach. In this case updating Tyk SSL certificate is just matter of doing API call to the Tyk for adding certificate, and restarting container with environment variable with new Cert ID.

6280 of 13320 relevant lines covered (47.15%)

0.52 hits per line

Jobs
ID Job ID Ran Files Coverage
2 3458.2 (LATEST_GO=true) 16 Nov 2017 08:22PM UTC 0
47.15
Travis Job 3458.2
Source Files on build 3458
Detailed source file information is not available for this build.
  • Back to Repo
  • Travis Build #3458
  • 0370f0a8 on github
  • Prev Build on release-2.4 (#3393)
  • Next Build on release-2.4 (#3473)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc