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

TykTechnologies / tyk / 3106
47%
master: %

Build:
Build:
LAST BUILD BRANCH: v2.9.4.8
DEFAULT BRANCH: master
Ran 28 Sep 2017 05:19AM UTC
Jobs 1
Files 87
Run time 4s
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
3106

push

travis-ci

buger
Mutual TLS protection on API Level

Fixes #357

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 (white listing certificates on api level)
* Authentification (creating keys based on certificates)
* Upstream access (including JSVM)
* Control API
* Dashboard and MDCB API

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 works 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 store 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.

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>"...}...}`.
* 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 analyze 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.

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

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.

You can specify certificates IDs per hostname, on API or global level.
In both cases it is a hash when key is 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.

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

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.

Old configuration option is still supported, and will be marked as legacy.

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 host name), Tyk fallsback to HTTP validation middleware, which checks client certificate via `request.TLS.PeerCertificates` variable.

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 commans, since it require server certificate to have SNI DNS extension, and 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.

- [] JSVM http calls should use upstream certificates
- [] MDCB sync layer
- [] Dashboard and MDCB API

6314 of 13705 relevant lines covered (46.07%)

0.5 hits per line

Jobs
ID Job ID Ran Files Coverage
2 3106.2 (LATEST_GO=true) 28 Sep 2017 05:19AM UTC 0
46.07
Travis Job 3106.2
Source Files on build 3106
Detailed source file information is not available for this build.
  • Back to Repo
  • Travis Build #3106
  • 447f5166 on github
  • Prev Build on mutual_tls (#3104)
  • Next Build on mutual_tls (#3168)
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