Skip to content
This repository was archived by the owner on Nov 24, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
- [#6034](https://github.com/apache/trafficcontrol/issues/6034) Added new query parameter `cdn` to the `GET /api/x/deliveryserviceserver` Traffic Ops API to filter by CDN name
- Added a new Traffic Monitor configuration option -- `short_hostname_override` -- to traffic_monitor.cfg to allow overriding the system hostname that Traffic Monitor uses.
- A new Traffic Portal server command-line option `-c` to specify a configuration file, and the ability to set `log: null` to log to stdout (consult documentation for details).
- SANs information to the SSL key endpoint and Traffic Portal page.

### Fixed
- Fixed Traffic Router crs/stats to prevent overflow and to correctly record the time used in averages.
Expand Down
6 changes: 5 additions & 1 deletion docs/source/api/v4/deliveryservices_xmlid_xmlid_sslkeys.rst
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ Response Structure
:state: An optional field which, if present, contains the state entered by the user when generating certificate\ [1]_
:version: An integer that defines the "version" of the key - which may be thought of as the sequential generation; that is, the higher the number the more recent the key
:expiration: The expiration date of the certificate for the :term:`Delivery Service` in :rfc:`3339` format
:sans: The :abbr:`SANs (Subject Alternate Names)` from the SSL certificate.

.. versionadded:: 4.1

.. code-block:: http
:caption: Response Example
Expand All @@ -93,7 +96,8 @@ Response Structure
"country": "US",
"state": "Colorado",
"version": "1",
"expiration": "2020-08-18T13:53:06Z"
"expiration": "2020-08-18T13:53:06Z",
"sans": ["*.foober.com", "*.foober2.com"]
}}

``DELETE``
Expand Down
16 changes: 15 additions & 1 deletion lib/go-tc/deliveryservice_ssl_keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,20 @@ type DeliveryServiceSSLKeys struct {
Certificate DeliveryServiceSSLKeysCertificate `json:"certificate,omitempty"`
}

// DeliveryServiceSSLKeysV4 is the representation of a DeliveryServiceSSLKeys in the latest minor version of
// version 4 of the Traffic Ops API.
type DeliveryServiceSSLKeysV4 = DeliveryServiceSSLKeysV41

// DeliveryServiceSSLKeysV41 structures contain information about an SSL key
// certificate pair used by a Delivery Service.
//
// "V41" is used because this structure was first introduced in version 4.1 of
// the Traffic Ops API.
type DeliveryServiceSSLKeysV41 struct {
DeliveryServiceSSLKeysV15
Sans []string `json:"sans,omitempty"`
}

// DeliveryServiceSSLKeysV15 structures contain information about an SSL key
// certificate pair used by a Delivery Service.
//
Expand All @@ -73,7 +87,7 @@ type DeliveryServiceSSLKeys struct {
//
// This is, ostensibly, an updated version of DeliveryServiceSSLKeys, but
// beware that this may not be completely accurate as the predecessor structure
// appears to be used in many more contexts than this this structure.
// appears to be used in many more contexts than this structure.
type DeliveryServiceSSLKeysV15 struct {
DeliveryServiceSSLKeys
Expiration time.Time `json:"expiration,omitempty"`
Expand Down
11 changes: 11 additions & 0 deletions lib/go-util/str.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,17 @@ func StrInArray(strs []string, s string) bool {
return false
}

// RemoveStrFromArray removes a specific string from a string slice.
func RemoveStrFromArray(strs []string, s string) []string {
newStrArray := []string{}
for _, str := range strs {
if str != s {
newStrArray = append(newStrArray, str)
}
}
return newStrArray
}

func ContainsStr(a []string, x string) bool {
for _, n := range a {
if x == n {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ func RunAutorenewal(existingCerts []ExistingCerts, cfg *config.Config, ctx conte
continue
}

expiration, err := parseExpirationFromCert([]byte(keyObj.Certificate.Crt))
expiration, _, err := parseExpirationAndSansFromCert([]byte(keyObj.Certificate.Crt), keyObj.Hostname)
if err != nil {
log.Errorf("cert autorenewal: %s: %s", ds.XmlId, err.Error())
dsExpInfo.XmlId = ds.XmlId
Expand Down
69 changes: 43 additions & 26 deletions traffic_ops/traffic_ops_golang/deliveryservice/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package deliveryservice

import (
"bytes"
"context"
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
Expand Down Expand Up @@ -135,8 +136,8 @@ func AddSSLKeys(w http.ResponseWriter, r *http.Request) {
api.WriteResp(w, r, "Successfully added ssl keys for "+*req.DeliveryService)
}

// GetSSLKeysByXMLIDV15 fetches the deliveryservice ssl keys by the specified xmlID. V15 includes expiration date.
func GetSSLKeysByXMLIDV15(w http.ResponseWriter, r *http.Request) {
// GetSSLKeysByXMLID fetches the deliveryservice ssl keys by the specified xmlID. V15 includes expiration date.
func GetSSLKeysByXMLID(w http.ResponseWriter, r *http.Request) {
inf, userErr, sysErr, errCode := api.NewInfo(r, []string{"xmlid"}, nil)
if userErr != nil || sysErr != nil {
api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
Expand All @@ -149,67 +150,83 @@ func GetSSLKeysByXMLIDV15(w http.ResponseWriter, r *http.Request) {
}
xmlID := inf.Params["xmlid"]
alerts := tc.Alerts{}
version := inf.Params["version"]
decode := inf.Params["decode"]
if userErr, sysErr, errCode := tenant.Check(inf.User, xmlID, inf.Tx.Tx); userErr != nil || sysErr != nil {
userErr = api.LogErr(r, errCode, userErr, sysErr)
alerts.AddNewAlert(tc.ErrorLevel, userErr.Error())
api.WriteAlerts(w, r, errCode, alerts)
return
}
keyObj, ok, err := inf.Vault.GetDeliveryServiceSSLKeys(xmlID, version, inf.Tx.Tx, r.Context())

keyObjV4, err := getSslKeys(inf, r.Context())
if err != nil {
userErr := api.LogErr(r, http.StatusInternalServerError, nil, errors.New("getting ssl keys: "+err.Error()))
userErr := api.LogErr(r, http.StatusInternalServerError, nil, err)
alerts.AddNewAlert(tc.ErrorLevel, userErr.Error())
api.WriteAlerts(w, r, http.StatusInternalServerError, alerts)
return
}
if !ok {
keyObj = tc.DeliveryServiceSSLKeysV15{}

var keyObj interface{}
if inf.Version.Major < 4 || (inf.Version.Major == 4 && inf.Version.Minor < 1) {
keyObj = keyObjV4.DeliveryServiceSSLKeysV15
} else {
keyObj = keyObjV4
}

if len(alerts.Alerts) == 0 {
api.WriteResp(w, r, keyObj)
} else {
api.WriteAlertsObj(w, r, http.StatusOK, alerts, keyObj)
}
}

func getSslKeys(inf *api.APIInfo, ctx context.Context) (tc.DeliveryServiceSSLKeysV4, error) {
xmlID := inf.Params["xmlid"]
version := inf.Params["version"]
decode := inf.Params["decode"]

keyObjFromTv, ok, err := inf.Vault.GetDeliveryServiceSSLKeys(xmlID, version, inf.Tx.Tx, ctx)
if err != nil {
return tc.DeliveryServiceSSLKeysV4{}, errors.New("getting ssl keys: " + err.Error())
}
keyObj := tc.DeliveryServiceSSLKeysV4{}
if ok {
keyObj.DeliveryServiceSSLKeysV15 = keyObjFromTv
parsedCert := keyObj.Certificate
err = Base64DecodeCertificate(&parsedCert)
if err != nil {
userErr := api.LogErr(r, http.StatusInternalServerError, nil, errors.New("getting SSL keys for XMLID '"+xmlID+"': "+err.Error()))
alerts.AddNewAlert(tc.ErrorLevel, userErr.Error())
api.WriteAlerts(w, r, http.StatusInternalServerError, alerts)
return
return tc.DeliveryServiceSSLKeysV4{}, errors.New("getting SSL keys for XMLID '" + xmlID + "': " + err.Error())
}
if decode != "" && decode != "0" { // the Perl version checked the decode string as: if ( $decode )
keyObj.Certificate = parsedCert
}

if keyObj.Certificate.Crt != "" && keyObj.Expiration.IsZero() {
exp, err := parseExpirationFromCert([]byte(parsedCert.Crt))
exp, sans, err := parseExpirationAndSansFromCert([]byte(parsedCert.Crt), keyObj.Hostname)
if err != nil {
userErr := api.LogErr(r, http.StatusInternalServerError, nil, errors.New(xmlID+": "+err.Error()))
alerts.AddNewAlert(tc.ErrorLevel, userErr.Error())
api.WriteAlerts(w, r, http.StatusInternalServerError, alerts)
return
return tc.DeliveryServiceSSLKeysV4{}, errors.New(xmlID + ": " + err.Error())
}
keyObj.Expiration = exp
keyObj.Sans = sans
}
}

if len(alerts.Alerts) == 0 {
api.WriteResp(w, r, keyObj)
} else {
api.WriteAlertsObj(w, r, http.StatusOK, alerts, keyObj)
}
return keyObj, nil
}

func parseExpirationFromCert(cert []byte) (time.Time, error) {
func parseExpirationAndSansFromCert(cert []byte, commonName string) (time.Time, []string, error) {
block, _ := pem.Decode(cert)
if block == nil {
return time.Time{}, errors.New("Error decoding cert to parse expiration")
return time.Time{}, []string{}, errors.New("Error decoding cert to parse expiration")
}

x509cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return time.Time{}, errors.New("Error parsing cert to get expiration - " + err.Error())
return time.Time{}, []string{}, errors.New("Error parsing cert to get expiration - " + err.Error())
}

return x509cert.NotAfter, nil
dnsNames := util.RemoveStrFromArray(x509cert.DNSNames, commonName)

return x509cert.NotAfter, dnsNames, nil
}

func Base64DecodeCertificate(cert *tc.DeliveryServiceSSLKeysCertificate) error {
Expand Down
9 changes: 6 additions & 3 deletions traffic_ops/traffic_ops_golang/routing/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ func Routes(d ServerData) ([]Route, http.Handler, error) {
* 4.x API
*/

// SSL Keys
{api.Version{Major: 4, Minor: 1}, http.MethodGet, `deliveryservices/xmlId/{xmlid}/sslkeys$`, deliveryservice.GetSSLKeysByXMLID, auth.PrivLevelAdmin, nil, Authenticated, nil, 41357729074},
Comment thread
ocket8888 marked this conversation as resolved.

// CDN lock
{api.Version{Major: 4, Minor: 0}, http.MethodGet, `cdn_locks/?$`, cdn_lock.Read, auth.PrivLevelReadOnly, nil, Authenticated, nil, 4134390561},
{api.Version{Major: 4, Minor: 0}, http.MethodPost, `cdn_locks/?$`, cdn_lock.Create, auth.PrivLevelOperations, nil, Authenticated, nil, 4134390562},
Expand Down Expand Up @@ -484,7 +487,7 @@ func Routes(d ServerData) ([]Route, http.Handler, error) {
{api.Version{Major: 4, Minor: 0}, http.MethodDelete, `deliveryservices/{id}/?$`, api.DeleteHandler(&deliveryservice.TODeliveryService{}), auth.PrivLevelOperations, nil, Authenticated, nil, 4226420743},
{api.Version{Major: 4, Minor: 0}, http.MethodGet, `deliveryservices/{id}/servers/eligible/?$`, deliveryservice.GetServersEligible, auth.PrivLevelReadOnly, nil, Authenticated, nil, 4747615843},

{api.Version{Major: 4, Minor: 0}, http.MethodGet, `deliveryservices/xmlId/{xmlid}/sslkeys$`, deliveryservice.GetSSLKeysByXMLIDV15, auth.PrivLevelAdmin, nil, Authenticated, nil, 41357729073},
{api.Version{Major: 4, Minor: 0}, http.MethodGet, `deliveryservices/xmlId/{xmlid}/sslkeys$`, deliveryservice.GetSSLKeysByXMLID, auth.PrivLevelAdmin, nil, Authenticated, nil, 41357729073},
{api.Version{Major: 4, Minor: 0}, http.MethodPost, `deliveryservices/sslkeys/add$`, deliveryservice.AddSSLKeys, auth.PrivLevelAdmin, nil, Authenticated, nil, 48728785833},
{api.Version{Major: 4, Minor: 0}, http.MethodDelete, `deliveryservices/xmlId/{xmlid}/sslkeys$`, deliveryservice.DeleteSSLKeys, auth.PrivLevelOperations, nil, Authenticated, nil, 49267343},
{api.Version{Major: 4, Minor: 0}, http.MethodPost, `deliveryservices/sslkeys/generate/?$`, deliveryservice.GenerateSSLKeys, auth.PrivLevelOperations, nil, Authenticated, nil, 4534390513},
Expand Down Expand Up @@ -872,7 +875,7 @@ func Routes(d ServerData) ([]Route, http.Handler, error) {
{api.Version{Major: 3, Minor: 0}, http.MethodDelete, `deliveryservices/{id}/?$`, api.DeleteHandler(&deliveryservice.TODeliveryService{}), auth.PrivLevelOperations, nil, Authenticated, nil, 2226420743},
{api.Version{Major: 3, Minor: 0}, http.MethodGet, `deliveryservices/{id}/servers/eligible/?$`, deliveryservice.GetServersEligible, auth.PrivLevelReadOnly, nil, Authenticated, nil, 2747615843},

{api.Version{Major: 3, Minor: 0}, http.MethodGet, `deliveryservices/xmlId/{xmlid}/sslkeys$`, deliveryservice.GetSSLKeysByXMLIDV15, auth.PrivLevelAdmin, nil, Authenticated, nil, 21357729073},
{api.Version{Major: 3, Minor: 0}, http.MethodGet, `deliveryservices/xmlId/{xmlid}/sslkeys$`, deliveryservice.GetSSLKeysByXMLID, auth.PrivLevelAdmin, nil, Authenticated, nil, 21357729073},
{api.Version{Major: 3, Minor: 0}, http.MethodPost, `deliveryservices/sslkeys/add$`, deliveryservice.AddSSLKeys, auth.PrivLevelAdmin, nil, Authenticated, nil, 28728785833},
{api.Version{Major: 3, Minor: 0}, http.MethodDelete, `deliveryservices/xmlId/{xmlid}/sslkeys$`, deliveryservice.DeleteSSLKeys, auth.PrivLevelOperations, nil, Authenticated, nil, 29267343},
{api.Version{Major: 3, Minor: 0}, http.MethodPost, `deliveryservices/sslkeys/generate/?$`, deliveryservice.GenerateSSLKeys, auth.PrivLevelOperations, nil, Authenticated, nil, 2534390513},
Expand Down Expand Up @@ -1236,7 +1239,7 @@ func Routes(d ServerData) ([]Route, http.Handler, error) {
{api.Version{Major: 2, Minor: 0}, http.MethodDelete, `deliveryservices/{id}/?$`, api.DeleteHandler(&deliveryservice.TODeliveryService{}), auth.PrivLevelOperations, nil, Authenticated, nil, 222642074},
{api.Version{Major: 2, Minor: 0}, http.MethodGet, `deliveryservices/{id}/servers/eligible/?$`, deliveryservice.GetServersEligible, auth.PrivLevelReadOnly, nil, Authenticated, nil, 274761584},

{api.Version{Major: 2, Minor: 0}, http.MethodGet, `deliveryservices/xmlId/{xmlid}/sslkeys$`, deliveryservice.GetSSLKeysByXMLIDV15, auth.PrivLevelAdmin, nil, Authenticated, nil, 2135772907},
{api.Version{Major: 2, Minor: 0}, http.MethodGet, `deliveryservices/xmlId/{xmlid}/sslkeys$`, deliveryservice.GetSSLKeysByXMLID, auth.PrivLevelAdmin, nil, Authenticated, nil, 2135772907},
{api.Version{Major: 2, Minor: 0}, http.MethodPost, `deliveryservices/sslkeys/add$`, deliveryservice.AddSSLKeys, auth.PrivLevelAdmin, nil, Authenticated, nil, 2872878583},
{api.Version{Major: 2, Minor: 0}, http.MethodDelete, `deliveryservices/xmlId/{xmlid}/sslkeys$`, deliveryservice.DeleteSSLKeys, auth.PrivLevelOperations, nil, Authenticated, nil, 2926734},
{api.Version{Major: 2, Minor: 0}, http.MethodPost, `deliveryservices/sslkeys/generate/?$`, deliveryservice.GenerateSSLKeys, auth.PrivLevelOperations, nil, Authenticated, nil, 253439051},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ var FormDeliveryServiceSslKeysController = function(deliveryService, sslKeys, $s
$scope.navigateToPath = locationUtils.navigateToPath;

$scope.formattedExpiration = $scope.sslKeys.expiration !== undefined ? $filter('date')($scope.sslKeys.expiration, 'MM/dd/yyyy') : undefined;
$scope.sans = $scope.sslKeys.sans !== undefined ? sslKeys.sans.join(', ') : ""

$scope.generateKeys = function() {
locationUtils.navigateToPath('/delivery-services/' + deliveryService.id + '/ssl-keys/generate');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@
<output name="expiration" class="form-control">{{formattedExpiration}}</output>
</div>
</div>
<div class="form-group">
<label for="sans" class="control-label col-md-2 col-sm-2 col-xs-12">SANs</label>
<div class="col-md-10 col-sm-10 col-xs-12">
<output name="sans" class="form-control">{{sans}}</output>
</div>
</div>
<div class="form-group" ng-class="{'has-error': hasError(dsSslKeyForm.authType), 'has-feedback': hasError(dsSslKeyForm.authType)}">
<label for="authType" class="control-label col-md-2 col-sm-2 col-xs-12">Certificate Source (Self Signed, CA, etc) *</label>
<div class="col-md-10 col-sm-10 col-xs-12">
Expand Down
2 changes: 1 addition & 1 deletion traffic_portal/app/src/scripts/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@

angular.module('config', [])

.constant('ENV', { api: { root:'/api/4.0/', stable: "/api/3.1/" } });
.constant('ENV', { api: { root:'/api/4.1/', stable: "/api/3.1/" } });