You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by sh...@apache.org on 2021/10/15 18:02:19 UTC

[trafficcontrol] branch master updated: Added SANs to Cert Page (#6282)

This is an automated email from the ASF dual-hosted git repository.

shamrick pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new 1f56100  Added SANs to Cert Page (#6282)
1f56100 is described below

commit 1f5610066c9191eeba29dcacb25182e6c676c82e
Author: mattjackson220 <33...@users.noreply.github.com>
AuthorDate: Fri Oct 15 12:02:12 2021 -0600

    Added SANs to Cert Page (#6282)
    
    * Added SANs to Cert Page
    
    * updated changelog
    
    * updated per comment
---
 CHANGELOG.md                                       |  1 +
 .../v4/deliveryservices_xmlid_xmlid_sslkeys.rst    |  6 +-
 lib/go-tc/deliveryservice_ssl_keys.go              | 16 ++++-
 lib/go-util/str.go                                 | 11 ++++
 .../deliveryservice/autorenewcerts.go              |  2 +-
 .../traffic_ops_golang/deliveryservice/keys.go     | 69 ++++++++++++++--------
 traffic_ops/traffic_ops_golang/routing/routes.go   |  9 ++-
 .../FormDeliveryServiceSslKeysController.js        |  1 +
 .../form.deliveryServiceSslKeys.tpl.html           |  6 ++
 traffic_portal/app/src/scripts/config.js           |  2 +-
 10 files changed, 90 insertions(+), 33 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7286bf6..9624e01 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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.
diff --git a/docs/source/api/v4/deliveryservices_xmlid_xmlid_sslkeys.rst b/docs/source/api/v4/deliveryservices_xmlid_xmlid_sslkeys.rst
index efc2cb5..3130a23 100644
--- a/docs/source/api/v4/deliveryservices_xmlid_xmlid_sslkeys.rst
+++ b/docs/source/api/v4/deliveryservices_xmlid_xmlid_sslkeys.rst
@@ -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
@@ -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``
diff --git a/lib/go-tc/deliveryservice_ssl_keys.go b/lib/go-tc/deliveryservice_ssl_keys.go
index ec6dc4c..fd67722 100644
--- a/lib/go-tc/deliveryservice_ssl_keys.go
+++ b/lib/go-tc/deliveryservice_ssl_keys.go
@@ -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.
 //
@@ -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"`
diff --git a/lib/go-util/str.go b/lib/go-util/str.go
index 842def7..755fc8c 100644
--- a/lib/go-util/str.go
+++ b/lib/go-util/str.go
@@ -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 {
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/autorenewcerts.go b/traffic_ops/traffic_ops_golang/deliveryservice/autorenewcerts.go
index 05327d2..84be21f 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/autorenewcerts.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/autorenewcerts.go
@@ -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
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/keys.go b/traffic_ops/traffic_ops_golang/deliveryservice/keys.go
index 79fcd69..b4d1502 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/keys.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/keys.go
@@ -21,6 +21,7 @@ package deliveryservice
 
 import (
 	"bytes"
+	"context"
 	"crypto/ecdsa"
 	"crypto/rsa"
 	"crypto/x509"
@@ -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)
@@ -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 {
diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go b/traffic_ops/traffic_ops_golang/routing/routes.go
index b8a3fc6..8c3ccf0 100644
--- a/traffic_ops/traffic_ops_golang/routing/routes.go
+++ b/traffic_ops/traffic_ops_golang/routing/routes.go
@@ -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},
+
 		// 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},
@@ -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},
@@ -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},
@@ -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},
diff --git a/traffic_portal/app/src/common/modules/form/deliveryServiceSslKeys/FormDeliveryServiceSslKeysController.js b/traffic_portal/app/src/common/modules/form/deliveryServiceSslKeys/FormDeliveryServiceSslKeysController.js
index 59596af..205b303 100644
--- a/traffic_portal/app/src/common/modules/form/deliveryServiceSslKeys/FormDeliveryServiceSslKeysController.js
+++ b/traffic_portal/app/src/common/modules/form/deliveryServiceSslKeys/FormDeliveryServiceSslKeysController.js
@@ -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');
diff --git a/traffic_portal/app/src/common/modules/form/deliveryServiceSslKeys/form.deliveryServiceSslKeys.tpl.html b/traffic_portal/app/src/common/modules/form/deliveryServiceSslKeys/form.deliveryServiceSslKeys.tpl.html
index 6f0bf3a..4894918 100644
--- a/traffic_portal/app/src/common/modules/form/deliveryServiceSslKeys/form.deliveryServiceSslKeys.tpl.html
+++ b/traffic_portal/app/src/common/modules/form/deliveryServiceSslKeys/form.deliveryServiceSslKeys.tpl.html
@@ -61,6 +61,12 @@ under the License.
 					<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">
diff --git a/traffic_portal/app/src/scripts/config.js b/traffic_portal/app/src/scripts/config.js
index 52e5f0c..3973a15 100644
--- a/traffic_portal/app/src/scripts/config.js
+++ b/traffic_portal/app/src/scripts/config.js
@@ -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/" } });