Currently, supported TLS versions cannot be configured. This means that a Delivery Service can have clients requesting its content over old, insecure versions of TLS, and Tenants controlling that Delivery Service have no way to force clients to use more recent versions.
Changes to ATS in version 8.1 allow cache server configuration generation to produce configuration files that can effectively control the TLS versions allowed by a Delivery Service, causing clients using other versions to result in request failure. Currently, (in unreleased behavior on the master branch), this is implemented using a Delivery Service Profile Parameter. However, Parameters are notorious for being unvalidated, unstructured data that can be just obscure enough to escape notice, even when properly documented. For the best possible user, operator, and developer experiences, the data should be formally added to Delivery Service objects.
Delivery Service objects will be augmented to include a set of allowed TLS versions for the Delivery Service's content. Specifically, They will be expanded to implement this interface:
interface TLSVersionSpecifier { tlsVersions: [string, ...Array<string>] | null; }
For those not super familiar with Typescript, this interface specifies a tlsVersions
property that can be either null, or an array of N strings, where N is at least one. In other words, this can be null, but cannot be an empty array.
The reason for this is best understood by first understanding what null
means for this field. If tlsVersions
is null
, it means that no TLS version restrictions are in place; the behavior is the same as today without the field, all TLS versions are implicitly supported.
An empty array would seem to signify that no TLS versions are supported, which is an invalid configuration because then HTTPS content cannot be served - and if that‘s the intention, the Delivery Service’s Protocol should be modified such that it doesn't serve HTTPs content.
Speaking of which, the only valid value for the tlsVersions
of a Delivery Service with a Protocol set to 0
(which is called “HTTP” in Traffic Portal) is null
.
This change primarily concerns Traffic Ops, Traffic Portal, and ORT/T3C/atstccfg.
Traffic Router should not concern itself with validating the TLS versions used by routing requests for HTTP-routed and STEERING/CLIENT STEERING Delivery Services. Doing so wouldn't significantly increase content security since that would all be enforced by the caching layers anyway (even in the case of an HTTP_NO_CACHE
-Type Delivery Service) and would require invasive, complex changes as well as a headache-inducing web of edge cases to handle for STEERING and CLIENT STEERING routing.
Traffic Portal's Delivery Services forms (all types) will need to add two controls. First, a checkbox that controls whether TLS Version settings will exist on the Delivery Service. Secondly, a list of TLS Versions to be permitted for the Delivery Service - shown if and only if the aforementioned checkbox is checked. Below is a sample of what this would look like while the box is unchecked:
... and while the box is checked:
If desired, the markup used for that sample may be found in Appendix A.
These form fields should not appear on forms for STEERING-Type and CLIENT_STEERING-Type Delivery Services. Traffic Portal should always submit update and create requests for these types of Delivery Services with tlsVersions
set to null
.
Traffic Portal should disallow the addition of a TLS version that is not a “whole match” of the regular expression \d+\.\d+
, and should issue a warning if and when a TLS version is entered that is not known to exist (at the time of this writing, that would be any version besides 1.0, 1.1, 1.2, and 1.3). In keeping with the API restrictions, it MUST NOT disallow the addition of unknown protocol versions, to facilitate the ability of system administrators to quickly upgrade to a new protocol as it's released.
A warning should also be issued if any if the Delivery Service being edited is the target of a Delivery Service which has other targets that support TLS versions not supported by the Delivery Service being edited.
Traffic Portal should also display some kind of warning - possibly in a dialog box - whenever the “Restrict TLS Versions” box is checked, advising that clients using TLS versions not listed will become unable to retrieve the Delivery Service's content.
Finally, Traffic Portal should display a warning whenever there are TLS versions enabled that exclude newer versions but specify older versions (where unknown versions are ignored). For example, at the time of this writing, the following TLS version sets should produce this warning:
Traffic Ops will require changes to its API endpoints that return or manipulate Delivery Services and/or representations thereof. There are also a few database changes that are required, but client code should be generally unaffected, since it is using the the library structures that will already be changed as a result of changes to Traffic Ops itself.
The endpoints that will need to be updated to reflect the new structure of a Delivery service are:
/deliveryservices
/deliveryservices/{{ID}}
/deliveryservices/{{ID}}/safe
/deliveryservice_requests
/deliveryservice_requests/{{ID}}/assign
/deliveryservice_requests/{{ID}}/status
/servers/{{ID}}/deliveryservices
Endpoints relating to CDN Snapshots and/or monitoring configuration do not need changes, since those already do not represent Delivery Services in the same way as the rest of the API specifically because they omit data unnecessary for Traffic Monitor and Traffic Router, and the supported TLS versions of a Delivery Service do not affect monitoring or routing.
As a Delivery Service's Supported TLS Versions are not considered a “safe” field, the only endpoints that will need changes beyond just the response format are:
/deliveryservices
/deliveryservices/{{ID}}
/deliveryservice_requests
When a Delivery Service or Delivery Service Request is created or modified using one of these endpoints, the API MUST verify that the tlsVersions
property is either null
or an array of strings that match the regular expression \d+\.\d+
(an empty array may be treated implicitly as null
in request bodies, but MUST ALWAYS be emitted as null
in responses). In the event that one or more entries in the array does not match the required pattern, the response should be in the form of a 400 Bad Request with an error-level Alert indicating the problematic entry(ies).
A 400 Bad Request response must also be returned if the user tries to create a STEERING-Type or CLIENT_STEERING-Type Delivery Service with non-null
tlsVersions
, or attempts to edit an existing so-Typed Delivery Service such that its tlsVersions
property is not null
. The response must include an error-level Alert indicating that this configuration is invalid.
Also, similar to Traffic Portal, Traffic Ops should emit warning-level alerts whenever a Delivery Service is created with or modified to have a tlsVersions
property that is not null
, indicating that this could cause problems for certain clients.
It must also issue a warning-level Alert whenever the tlsVersions
property is set such that it includes TLS versions not known to exist - for example at the time of this writing that would be in the case that it includes any element that is not exactly one of:
"1.0"
"1.1"
"1.2"
"1.3"
Finally, a warning-level Alert is to be returned when the TLS versions exclude newer versions but include older versions. For example the values that would cause that warning to be issued today are:
["1.0"]
["1.0", "1.1"]
["1.1"]
["1.0", "1.1", "1.2"]
["1.1", "1.2"]
["1.0", "1.2"]
(Note that ordering is not important.)
To implement the storage of a Delivery Service's supported TLS versions, a new table should be created like so:
Table "public.deliveryservice_supported_tls_version" Column | Type | Collation | Nullable | Default -----------------+--------+-----------+----------+--------- deliveryservice | bigint | | not null | tls_version | text | | not null | Check constraints: "valid_tls_version" CHECK (tls_version ~ similar_to_escape('[0-9]+.[0-9]+'::text)) Foreign-key constraints: "deliveryservice_supported_tls_version_deliveryservice_fkey" FOREIGN KEY (deliveryservice) REFERENCES deliveryservice(id) ON UPDATE CASCADE ON DELETE CASCADE
Additionally, a migration will be needed to set the value of tlsVersions
on existing Delivery Service Requests to null
.
The new field will need to be added to both the Delivery Services overview section as well as all of the API documentation for endpoints in which it will appear. Note also that the current documentation for the PUT
method of /deliveryservices/{{ID}}
incorrectly states that the response is empty, whereas it actually returns an array of Delivery Service representations. The documentation should warn the reader that setting explicitly supported TLS versions could break clients that expect the support of excluded versions.
The integration tests between Traffic Ops and its Go client will need to be updated to verify the new validation and behavior, and unit tests should also exist for the behavior added to the Traffic Ops server.
No significant performance impact is expected. The lists of supported TLS versions are expected to be small, but do technically allow for arbitrary sizes, which could possibly cause problems.
Allowing arbitrary numbers of supported TLS versions is a possible attack vector for Denial-of-Service attacks that cause Traffic Ops to spend large amounts of time parsing and validating massive arrays of strings using what will probably actually just be a Regular Expression that is an exact copy of the one specified above (though it need only be compiled once).
Historically, though, the reaction to such things is “yes, operators have great power and it is up to system administrators to ensure that power is given only to those that can be trusted to wield it”. So likely nobody thinks this is a true concern, but if it were Traffic Ops could be configured to refuse to handle request payloads greater than a certain size, which would mitigate the problem.
There will be a database migration to run, but since the default configuration will be to behave as it was prior to the upgrade, there isn't much in the way of upgrade impact.
Operators will need to be aware of the meaning and use-cases for the new Delivery Service property, and most importantly must be well advised that using it can break clients.
Beyond the development changes necessary to implement the new property, virtually no developer impact is expected.
The alternative to doing this is simple: don‘t. Today, the known ATC instances are mostly used to transfer video content that isn’t particularly sensitive, so ensuring strong encryption isn't much of a priority.
However, for content that actually is sensitive, it is reasonable for the Tenant that controls an Origin to desire that its data is only transferred in a secure manner, so this seems a worthwhile change.
<div class="form-group"> <label class="control-label col-md-2 col-sm-2 col-xs-12" for="restrictTLS" > Restrict TLS Versions </label> <div class="col-md-10 col-sm-10 col-xs-12" style="display:inline-grid;grid-template-columns:20px;justify-content:start;" > <input type="checkbox" id="restrictTLS" name="restrictTLS" class="form-control" ng-model="restrictTLS" /> </div> </div> <ng-form ng-if="restrictTLS" name="tlsForm" ng-repeat="ver in deliveryService.tlsVersions track by $index" > <div class="form-group"> <div> <label class="control-label col-md-2 col-sm-2 col-xs-12" for="tlsVersion1" > TLS Version #1 <</label> <div class="col-md-10 col-sm-10 col-xs-12"> <div> <div class="input-group add-more-inputs"> <input id="tlsVersion1" name="tlsVersion1" type="text" class="form-control" ng-model="deliveryService.tlsVersion1s[$index]" pattern="[0-9]+\.[0-9]+" placeholder="1.3" required /> <span class="form-input-group-btn input-group-btn"> <button type="button" title="add a supported TLS version" class="btn btn-default" ng-show="deliveryservice.tlsVersions.length > 0" > <i class="fa fa-minus"></i> </button> <button type="button" title="remove support for this TLS version" class="btn btn-default" ng-show="$index == (deliveryService.tlsVersion1s.length - 1)" > <i class="fa fa-plus"></i> </button> </span> </div> </div> </div> </div> </div> </ng-form>