From 14e7993eb14e9951d242a4c640beed02170d37e1 Mon Sep 17 00:00:00 2001 From: mattjackson220 Date: Wed, 14 Jul 2021 15:02:03 -0600 Subject: [PATCH 01/10] Generate a self signed cert on ds creation/update --- CHANGELOG.md | 1 + .../deliveryservice/deliveryservices.go | 20 ++++++++ .../deliveryservice/sslkeys.go | 50 +++++++++++++++++-- 3 files changed, 68 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88afdf08a9..50ecd074fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Fixed - Fixed Traffic Router crs/stats to prevent overflow and to correctly record the time used in averages. +- [#5893](https://github.com/apache/trafficcontrol/issues/5893) - A self signed certificate is created when an HTTPS delivery service is created or an HTTP delivery service is updated to HTTPS. ### Changed - Updated `t3c` to request less unnecessary deliveryservice-server assignment and invalidation jobs data via new query params supported by Traffic Ops diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go index 77d71edf45..c51581a055 100644 --- a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go +++ b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go @@ -219,6 +219,16 @@ func CreateV40(w http.ResponseWriter, r *http.Request) { } alerts := res.TLSVersionsAlerts() alerts.AddNewAlert(tc.SuccessLevel, "Delivery Service creation was successful") + + if inf.Config.TrafficVaultEnabled && (*ds.Protocol == tc.DSProtocolHTTPS || *ds.Protocol == tc.DSProtocolHTTPAndHTTPS) && (ds.SSLKeyVersion == nil || *ds.SSLKeyVersion == 0) { + err, errCode := GeneratePlaceholderSelfSignedCert(*res, inf, r.Context()) + if err != nil || errCode != http.StatusOK { + api.HandleErr(w, r, inf.Tx.Tx, errCode, nil, err) + return + } + alerts.AddNewAlert(tc.SuccessLevel, " A self-signed certificate was created as a placeholder.") + } + w.Header().Set("Location", fmt.Sprintf("/api/4.0/deliveryservices?id=%d", *res.ID)) api.WriteAlertsObj(w, r, http.StatusCreated, alerts, []tc.DeliveryServiceV40{*res}) } @@ -715,6 +725,16 @@ func UpdateV40(w http.ResponseWriter, r *http.Request) { } alerts := res.TLSVersionsAlerts() alerts.AddNewAlert(tc.SuccessLevel, "Delivery Service update was successful") + + if inf.Config.TrafficVaultEnabled && (*ds.Protocol == tc.DSProtocolHTTPS || *ds.Protocol == tc.DSProtocolHTTPAndHTTPS) && (ds.SSLKeyVersion == nil || *ds.SSLKeyVersion == 0) { + err, errCode := GeneratePlaceholderSelfSignedCert(*res, inf, r.Context()) + if err != nil || errCode != http.StatusOK { + api.HandleErr(w, r, inf.Tx.Tx, errCode, nil, err) + return + } + alerts.AddNewAlert(tc.SuccessLevel, " A self-signed certificate was created as a placeholder.") + } + api.WriteAlertsObj(w, r, http.StatusOK, alerts, []tc.DeliveryServiceV40{*res}) } diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go b/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go index a1a7f16e2c..1cb5bc9d57 100644 --- a/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go +++ b/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go @@ -25,8 +25,10 @@ import ( "errors" "net/http" "strconv" + "strings" "github.com/apache/trafficcontrol/lib/go-tc" + "github.com/apache/trafficcontrol/lib/go-util" "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api" "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers" "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/tenant" @@ -70,7 +72,7 @@ func GenerateSSLKeys(w http.ResponseWriter, r *http.Request) { api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr) return } - if err := generatePutRiakKeys(req, inf.Tx.Tx, inf.Vault, r.Context()); err != nil { + if err := GeneratePutRiakKeys(req, inf.Tx.Tx, inf.Vault, r.Context()); err != nil { api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("generating and putting SSL keys: "+err.Error())) return } @@ -82,9 +84,9 @@ func GenerateSSLKeys(w http.ResponseWriter, r *http.Request) { api.WriteResp(w, r, "Successfully created ssl keys for "+*req.DeliveryService) } -// generatePutRiakKeys generates a certificate, csr, and key from the given request, and insert it into the Riak key database. +// GeneratePutRiakKeys generates a certificate, csr, and key from the given request, and insert it into the Riak key database. // The req MUST be validated, ensuring required fields exist. -func generatePutRiakKeys(req tc.DeliveryServiceGenSSLKeysReq, tx *sql.Tx, tv trafficvault.TrafficVault, ctx context.Context) error { +func GeneratePutRiakKeys(req tc.DeliveryServiceGenSSLKeysReq, tx *sql.Tx, tv trafficvault.TrafficVault, ctx context.Context) error { dsSSLKeys := tc.DeliveryServiceSSLKeys{ CDN: *req.CDN, DeliveryService: *req.DeliveryService, @@ -110,3 +112,45 @@ func generatePutRiakKeys(req tc.DeliveryServiceGenSSLKeysReq, tx *sql.Tx, tv tra } return nil } + +func GeneratePlaceholderSelfSignedCert(ds tc.DeliveryServiceV40, inf *api.APIInfo, context context.Context) (error, int) { + version := util.JSONIntStr(1) + hostname := strings.Split(ds.ExampleURLs[0], "://")[1] + if strings.Contains(ds.Type.String(), "HTTP") { + parts := strings.Split(hostname, ".") + parts[0] = "*" + hostname = strings.Join(parts, ".") + } + + cdnName, ok, err := dbhelpers.GetCDNNameFromID(inf.Tx.Tx, int64(*ds.CDNID)) + if err != nil { + return err, http.StatusInternalServerError + } else if !ok { + return nil, http.StatusNotFound + } + + cdnNameStr := string(cdnName) + + req := tc.DeliveryServiceGenSSLKeysReq{ + DeliveryServiceSSLKeysReq: tc.DeliveryServiceSSLKeysReq{ + CDN: &cdnNameStr, + DeliveryService: ds.XMLID, + HostName: &hostname, + Key: ds.XMLID, + Version: &version, + BusinessUnit: util.StrPtr("Placeholder"), + City: util.StrPtr("Placeholder"), + Organization: util.StrPtr("Placeholder"), + Country: util.StrPtr("United States (US)"), + State: util.StrPtr("CO"), + }, + } + if err := GeneratePutRiakKeys(req, inf.Tx.Tx, inf.Vault, context); err != nil { + return errors.New("generating and putting SSL keys: " + err.Error()), http.StatusInternalServerError + } + if err := updateSSLKeyVersion(*req.DeliveryService, req.Version.ToInt64(), inf.Tx.Tx); err != nil { + return errors.New("generating SSL keys for delivery service '" + *req.DeliveryService + "': " + err.Error()), http.StatusInternalServerError + } + + return nil, http.StatusOK +} From 641ba5065400a8640a64f2ab9adc105cc7975af1 Mon Sep 17 00:00:00 2001 From: mattjackson220 Date: Thu, 15 Jul 2021 09:21:00 -0600 Subject: [PATCH 02/10] Added nil check for example urls, fixed test that was failing --- .../testing/api/v4/deliveryservices_test.go | 2 +- .../deliveryservice/sslkeys.go | 46 +++++++++++++++---- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/traffic_ops/testing/api/v4/deliveryservices_test.go b/traffic_ops/testing/api/v4/deliveryservices_test.go index d3187c8002..b244acf03e 100644 --- a/traffic_ops/testing/api/v4/deliveryservices_test.go +++ b/traffic_ops/testing/api/v4/deliveryservices_test.go @@ -137,7 +137,7 @@ func CUDDeliveryServiceWithLocks(t *testing.T) { if len(types.Response) < 1 { t.Fatal("expected at least one type") } - customDS := getCustomDS(cdn.ID, types.Response[0].ID, "cdn_locks_test_ds_name", "routingName", "https://test_cdn_locks.com", "cdn_locks_test_ds_xml_id") + customDS := getCustomDS(cdn.ID, types.Response[0].ID, "cdn-locks-test-ds-name", "edge", "https://test-cdn-locks.com", "cdn-locks-test-ds-xml-id") // Create a lock for this user _, _, err = userSession.CreateCDNLock(tc.CDNLock{ diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go b/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go index 1cb5bc9d57..1af8db34db 100644 --- a/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go +++ b/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go @@ -23,6 +23,7 @@ import ( "context" "database/sql" "errors" + "fmt" "net/http" "strconv" "strings" @@ -113,24 +114,39 @@ func GeneratePutRiakKeys(req tc.DeliveryServiceGenSSLKeysReq, tx *sql.Tx, tv tra return nil } +// GeneratePlaceholderSelfSignedCert generates a self-signed SSL certificate as a placeholder when a new HTTPS +// delivery service is created or an HTTP delivery service is updated to use HTTPS. func GeneratePlaceholderSelfSignedCert(ds tc.DeliveryServiceV40, inf *api.APIInfo, context context.Context) (error, int) { version := util.JSONIntStr(1) - hostname := strings.Split(ds.ExampleURLs[0], "://")[1] - if strings.Contains(ds.Type.String(), "HTTP") { - parts := strings.Split(hostname, ".") - parts[0] = "*" - hostname = strings.Join(parts, ".") + + db, err := api.GetDB(context) + if err != nil { + return err, http.StatusInternalServerError + } + tx, err := db.Begin() + if err != nil { + return err, http.StatusInternalServerError } + defer tx.Commit() - cdnName, ok, err := dbhelpers.GetCDNNameFromID(inf.Tx.Tx, int64(*ds.CDNID)) + cdnName, cdnDomain, err := getCDNNameDomain(*ds.CDNID, tx) if err != nil { return err, http.StatusInternalServerError - } else if !ok { - return nil, http.StatusNotFound } cdnNameStr := string(cdnName) + if ds.ExampleURLs == nil { + ds.ExampleURLs = MakeExampleURLs(ds.Protocol, *ds.Type, *ds.RoutingName, *ds.MatchList, cdnDomain) + } + + hostname := strings.Split(ds.ExampleURLs[0], "://")[1] + if strings.Contains(ds.Type.String(), "HTTP") { + parts := strings.Split(hostname, ".") + parts[0] = "*" + hostname = strings.Join(parts, ".") + } + req := tc.DeliveryServiceGenSSLKeysReq{ DeliveryServiceSSLKeysReq: tc.DeliveryServiceSSLKeysReq{ CDN: &cdnNameStr, @@ -145,12 +161,22 @@ func GeneratePlaceholderSelfSignedCert(ds tc.DeliveryServiceV40, inf *api.APIInf State: util.StrPtr("CO"), }, } - if err := GeneratePutRiakKeys(req, inf.Tx.Tx, inf.Vault, context); err != nil { + if err := GeneratePutRiakKeys(req, tx, inf.Vault, context); err != nil { return errors.New("generating and putting SSL keys: " + err.Error()), http.StatusInternalServerError } - if err := updateSSLKeyVersion(*req.DeliveryService, req.Version.ToInt64(), inf.Tx.Tx); err != nil { + if err := updateSSLKeyVersion(*req.DeliveryService, req.Version.ToInt64(), tx); err != nil { return errors.New("generating SSL keys for delivery service '" + *req.DeliveryService + "': " + err.Error()), http.StatusInternalServerError } return nil, http.StatusOK } + +func getCDNNameDomain(cdnID int, tx *sql.Tx) (string, string, error) { + q := `SELECT cdn.name, cdn.domain_name from cdn where cdn.id = $1` + cdnName := "" + cdnDomain := "" + if err := tx.QueryRow(q, cdnID).Scan(&cdnName, &cdnDomain); err != nil { + return "", "", fmt.Errorf("getting cdn name and domain for cdn '%v': "+err.Error(), cdnID) + } + return cdnName, cdnDomain, nil +} From b137e0dad7bb7f23cb6061a25eb80332fb3b43ab Mon Sep 17 00:00:00 2001 From: mattjackson220 Date: Thu, 22 Jul 2021 15:14:18 -0600 Subject: [PATCH 03/10] updated per comments --- .../dbhelpers/db_helpers.go | 11 +++++ .../deliveryservice/deliveryservices.go | 33 ++++++------- .../deliveryservice/sslkeys.go | 46 ++++++++----------- 3 files changed, 44 insertions(+), 46 deletions(-) diff --git a/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go b/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go index f3f35e62e4..8d70fc320a 100644 --- a/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go +++ b/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go @@ -1603,3 +1603,14 @@ func GetDSIDFromStaticDNSEntry(tx *sql.Tx, staticDNSEntryID int) (int, error) { } return dsID, nil } + +// GetCDNNameDomain returns the name and domain for a given CDN ID. +func GetCDNNameDomain(cdnID int, tx *sql.Tx) (string, string, error) { + q := `SELECT cdn.name, cdn.domain_name from cdn where cdn.id = $1` + cdnName := "" + cdnDomain := "" + if err := tx.QueryRow(q, cdnID).Scan(&cdnName, &cdnDomain); err != nil { + return "", "", fmt.Errorf("getting cdn name and domain for cdn '%v': "+err.Error(), cdnID) + } + return cdnName, cdnDomain, nil +} diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go index c51581a055..0bc485fbdc 100644 --- a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go +++ b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go @@ -220,15 +220,6 @@ func CreateV40(w http.ResponseWriter, r *http.Request) { alerts := res.TLSVersionsAlerts() alerts.AddNewAlert(tc.SuccessLevel, "Delivery Service creation was successful") - if inf.Config.TrafficVaultEnabled && (*ds.Protocol == tc.DSProtocolHTTPS || *ds.Protocol == tc.DSProtocolHTTPAndHTTPS) && (ds.SSLKeyVersion == nil || *ds.SSLKeyVersion == 0) { - err, errCode := GeneratePlaceholderSelfSignedCert(*res, inf, r.Context()) - if err != nil || errCode != http.StatusOK { - api.HandleErr(w, r, inf.Tx.Tx, errCode, nil, err) - return - } - alerts.AddNewAlert(tc.SuccessLevel, " A self-signed certificate was created as a placeholder.") - } - w.Header().Set("Location", fmt.Sprintf("/api/4.0/deliveryservices?id=%d", *res.ID)) api.WriteAlertsObj(w, r, http.StatusCreated, alerts, []tc.DeliveryServiceV40{*res}) } @@ -558,6 +549,13 @@ func createV40(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV40 t dsV40 = ds + if inf.Config.TrafficVaultEnabled && (*ds.Protocol == tc.DSProtocolHTTPS || *ds.Protocol == tc.DSProtocolHTTPAndHTTPS || *ds.Protocol == tc.DSProtocolHTTPToHTTPS) { + err, errCode := GeneratePlaceholderSelfSignedCert(dsV40, inf, r.Context()) + if err != nil || errCode != http.StatusOK { + return nil, http.StatusInternalServerError, nil, fmt.Errorf("creating self signed default cert: %v", err) + } + } + return &dsV40, http.StatusOK, nil, nil } @@ -726,15 +724,6 @@ func UpdateV40(w http.ResponseWriter, r *http.Request) { alerts := res.TLSVersionsAlerts() alerts.AddNewAlert(tc.SuccessLevel, "Delivery Service update was successful") - if inf.Config.TrafficVaultEnabled && (*ds.Protocol == tc.DSProtocolHTTPS || *ds.Protocol == tc.DSProtocolHTTPAndHTTPS) && (ds.SSLKeyVersion == nil || *ds.SSLKeyVersion == 0) { - err, errCode := GeneratePlaceholderSelfSignedCert(*res, inf, r.Context()) - if err != nil || errCode != http.StatusOK { - api.HandleErr(w, r, inf.Tx.Tx, errCode, nil, err) - return - } - alerts.AddNewAlert(tc.SuccessLevel, " A self-signed certificate was created as a placeholder.") - } - api.WriteAlertsObj(w, r, http.StatusOK, alerts, []tc.DeliveryServiceV40{*res}) } @@ -1144,6 +1133,14 @@ func updateV40(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV40 * } dsV40 = (*tc.DeliveryServiceV40)(&ds) + + if inf.Config.TrafficVaultEnabled && (*ds.Protocol == tc.DSProtocolHTTPS || *ds.Protocol == tc.DSProtocolHTTPAndHTTPS || *ds.Protocol == tc.DSProtocolHTTPToHTTPS) { + err, errCode := GeneratePlaceholderSelfSignedCert(*dsV40, inf, r.Context()) + if err != nil || errCode != http.StatusOK { + return nil, http.StatusInternalServerError, nil, fmt.Errorf("creating self signed default cert: %v", err) + } + } + return dsV40, http.StatusOK, nil, nil } diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go b/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go index 1af8db34db..d364c0b617 100644 --- a/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go +++ b/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go @@ -23,7 +23,6 @@ import ( "context" "database/sql" "errors" - "fmt" "net/http" "strconv" "strings" @@ -73,7 +72,7 @@ func GenerateSSLKeys(w http.ResponseWriter, r *http.Request) { api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr) return } - if err := GeneratePutRiakKeys(req, inf.Tx.Tx, inf.Vault, r.Context()); err != nil { + if err := generatePutTrafficVaultSSLKeys(req, inf.Tx.Tx, inf.Vault, r.Context()); err != nil { api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("generating and putting SSL keys: "+err.Error())) return } @@ -85,9 +84,9 @@ func GenerateSSLKeys(w http.ResponseWriter, r *http.Request) { api.WriteResp(w, r, "Successfully created ssl keys for "+*req.DeliveryService) } -// GeneratePutRiakKeys generates a certificate, csr, and key from the given request, and insert it into the Riak key database. +// generatePutTrafficVaultSSLKeys generates a certificate, csr, and key from the given request, and insert it into the Riak key database. // The req MUST be validated, ensuring required fields exist. -func GeneratePutRiakKeys(req tc.DeliveryServiceGenSSLKeysReq, tx *sql.Tx, tv trafficvault.TrafficVault, ctx context.Context) error { +func generatePutTrafficVaultSSLKeys(req tc.DeliveryServiceGenSSLKeysReq, tx *sql.Tx, tv trafficvault.TrafficVault, ctx context.Context) error { dsSSLKeys := tc.DeliveryServiceSSLKeys{ CDN: *req.CDN, DeliveryService: *req.DeliveryService, @@ -116,20 +115,21 @@ func GeneratePutRiakKeys(req tc.DeliveryServiceGenSSLKeysReq, tx *sql.Tx, tv tra // GeneratePlaceholderSelfSignedCert generates a self-signed SSL certificate as a placeholder when a new HTTPS // delivery service is created or an HTTP delivery service is updated to use HTTPS. -func GeneratePlaceholderSelfSignedCert(ds tc.DeliveryServiceV40, inf *api.APIInfo, context context.Context) (error, int) { - version := util.JSONIntStr(1) - - db, err := api.GetDB(context) +func GeneratePlaceholderSelfSignedCert(ds tc.DeliveryServiceV4, inf *api.APIInfo, context context.Context) (error, int) { + tv := inf.Vault + _, ok, err := tv.GetDeliveryServiceSSLKeys(*ds.XMLID, "", inf.Tx.Tx, context) if err != nil { - return err, http.StatusInternalServerError + return errors.New("getting latest ssl keys for xmlId: " + *ds.XMLID + " : " + err.Error()), http.StatusInternalServerError } - tx, err := db.Begin() - if err != nil { - return err, http.StatusInternalServerError + if ok { + return nil, http.StatusOK } - defer tx.Commit() - cdnName, cdnDomain, err := getCDNNameDomain(*ds.CDNID, tx) + version := util.JSONIntStr(1) + + tx := inf.Tx.Tx + + cdnName, cdnDomain, err := dbhelpers.GetCDNNameDomain(*ds.CDNID, tx) if err != nil { return err, http.StatusInternalServerError } @@ -141,7 +141,7 @@ func GeneratePlaceholderSelfSignedCert(ds tc.DeliveryServiceV40, inf *api.APIInf } hostname := strings.Split(ds.ExampleURLs[0], "://")[1] - if strings.Contains(ds.Type.String(), "HTTP") { + if ds.Type.IsHTTP() { parts := strings.Split(hostname, ".") parts[0] = "*" hostname = strings.Join(parts, ".") @@ -157,11 +157,11 @@ func GeneratePlaceholderSelfSignedCert(ds tc.DeliveryServiceV40, inf *api.APIInf BusinessUnit: util.StrPtr("Placeholder"), City: util.StrPtr("Placeholder"), Organization: util.StrPtr("Placeholder"), - Country: util.StrPtr("United States (US)"), - State: util.StrPtr("CO"), + Country: util.StrPtr("Placeholder"), + State: util.StrPtr("Placeholder"), }, } - if err := GeneratePutRiakKeys(req, tx, inf.Vault, context); err != nil { + if err := generatePutTrafficVaultSSLKeys(req, tx, inf.Vault, context); err != nil { return errors.New("generating and putting SSL keys: " + err.Error()), http.StatusInternalServerError } if err := updateSSLKeyVersion(*req.DeliveryService, req.Version.ToInt64(), tx); err != nil { @@ -170,13 +170,3 @@ func GeneratePlaceholderSelfSignedCert(ds tc.DeliveryServiceV40, inf *api.APIInf return nil, http.StatusOK } - -func getCDNNameDomain(cdnID int, tx *sql.Tx) (string, string, error) { - q := `SELECT cdn.name, cdn.domain_name from cdn where cdn.id = $1` - cdnName := "" - cdnDomain := "" - if err := tx.QueryRow(q, cdnID).Scan(&cdnName, &cdnDomain); err != nil { - return "", "", fmt.Errorf("getting cdn name and domain for cdn '%v': "+err.Error(), cdnID) - } - return cdnName, cdnDomain, nil -} From 070b1d8f15fa61a9fd5038697a624ce344b9f6c9 Mon Sep 17 00:00:00 2001 From: mattjackson220 Date: Mon, 26 Jul 2021 10:38:08 -0600 Subject: [PATCH 04/10] fixed tests --- .../traffic_ops_golang/deliveryservice/sslkeys.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go b/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go index d364c0b617..ded4d05a52 100644 --- a/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go +++ b/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go @@ -127,7 +127,15 @@ func GeneratePlaceholderSelfSignedCert(ds tc.DeliveryServiceV4, inf *api.APIInfo version := util.JSONIntStr(1) - tx := inf.Tx.Tx + db, err := api.GetDB(context) + if err != nil { + return err, http.StatusInternalServerError + } + tx, err := db.Begin() + if err != nil { + return err, http.StatusInternalServerError + } + defer tx.Commit() cdnName, cdnDomain, err := dbhelpers.GetCDNNameDomain(*ds.CDNID, tx) if err != nil { From 72bce58844173e1cebbd97c51c40488da78d446f Mon Sep 17 00:00:00 2001 From: mattjackson220 Date: Mon, 26 Jul 2021 11:30:14 -0600 Subject: [PATCH 05/10] more test fixing --- .../deliveryservice/sslkeys.go | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go b/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go index ded4d05a52..fd8f5bd7ea 100644 --- a/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go +++ b/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go @@ -116,17 +116,6 @@ func generatePutTrafficVaultSSLKeys(req tc.DeliveryServiceGenSSLKeysReq, tx *sql // GeneratePlaceholderSelfSignedCert generates a self-signed SSL certificate as a placeholder when a new HTTPS // delivery service is created or an HTTP delivery service is updated to use HTTPS. func GeneratePlaceholderSelfSignedCert(ds tc.DeliveryServiceV4, inf *api.APIInfo, context context.Context) (error, int) { - tv := inf.Vault - _, ok, err := tv.GetDeliveryServiceSSLKeys(*ds.XMLID, "", inf.Tx.Tx, context) - if err != nil { - return errors.New("getting latest ssl keys for xmlId: " + *ds.XMLID + " : " + err.Error()), http.StatusInternalServerError - } - if ok { - return nil, http.StatusOK - } - - version := util.JSONIntStr(1) - db, err := api.GetDB(context) if err != nil { return err, http.StatusInternalServerError @@ -137,6 +126,17 @@ func GeneratePlaceholderSelfSignedCert(ds tc.DeliveryServiceV4, inf *api.APIInfo } defer tx.Commit() + tv := inf.Vault + _, ok, err := tv.GetDeliveryServiceSSLKeys(*ds.XMLID, "", tx, context) + if err != nil { + return errors.New("getting latest ssl keys for xmlId: " + *ds.XMLID + " : " + err.Error()), http.StatusInternalServerError + } + if ok { + return nil, http.StatusOK + } + + version := util.JSONIntStr(1) + cdnName, cdnDomain, err := dbhelpers.GetCDNNameDomain(*ds.CDNID, tx) if err != nil { return err, http.StatusInternalServerError From f2d7038262b5817b960f161214daa985c4fbcb51 Mon Sep 17 00:00:00 2001 From: mattjackson220 Date: Tue, 27 Jul 2021 10:30:03 -0600 Subject: [PATCH 06/10] added default cert verification test --- .../testing/api/v4/deliveryservices_test.go | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/traffic_ops/testing/api/v4/deliveryservices_test.go b/traffic_ops/testing/api/v4/deliveryservices_test.go index b244acf03e..09fdef15c2 100644 --- a/traffic_ops/testing/api/v4/deliveryservices_test.go +++ b/traffic_ops/testing/api/v4/deliveryservices_test.go @@ -42,6 +42,7 @@ func TestDeliveryServices(t *testing.T) { header.Set(rfc.IfModifiedSince, ti) header.Set(rfc.IfUnmodifiedSince, ti) if includeSystemTests { + t.Run("Verify SSL key generation on DS creation", VerifySSLKeysOnDsCreationTest) t.Run("Update CDN for a Delivery Service with SSL keys", SSLDeliveryServiceCDNUpdateTest) t.Run("Create URL Signature keys for a Delivery Service", CreateTestDeliveryServicesURLSignatureKeys) t.Run("Retrieve URL Signature keys for a Delivery Service", GetTestDeliveryServicesURLSignatureKeys) @@ -706,6 +707,43 @@ func DeliveryServiceSSLKeys(t *testing.T) { } } +func VerifySSLKeysOnDsCreationTest(t *testing.T) { + for _, ds := range testData.DeliveryServices { + if !(*ds.Protocol == tc.DSProtocolHTTPS || *ds.Protocol == tc.DSProtocolHTTPAndHTTPS || *ds.Protocol == tc.DSProtocolHTTPToHTTPS) { + continue + } + var err error + dsSSLKey := new(tc.DeliveryServiceSSLKeys) + for tries := 0; tries < 5; tries++ { + time.Sleep(time.Second) + var sslKeysResp tc.DeliveryServiceSSLKeysResponse + sslKeysResp, _, err = TOSession.GetDeliveryServiceSSLKeys(*ds.XMLID, client.RequestOptions{}) + *dsSSLKey = sslKeysResp.Response + if err == nil && dsSSLKey != nil { + break + } + } + + if err != nil || dsSSLKey == nil { + t.Fatalf("unable to get DS %s SSL key: %v", *ds.XMLID, err) + } + if dsSSLKey.Certificate.Key == "" { + t.Errorf("expected a valid key but got nothing") + } + if dsSSLKey.Certificate.Crt == "" { + t.Errorf("expected a valid certificate, but got nothing") + } + if dsSSLKey.Certificate.CSR == "" { + t.Errorf("expected a valid CSR, but got nothing") + } + + err = deliveryservice.Base64DecodeCertificate(&dsSSLKey.Certificate) + if err != nil { + t.Fatalf("couldn't decode certificate: %v", err) + } + } +} + func SSLDeliveryServiceCDNUpdateTest(t *testing.T) { cdnNameOld := "sslkeytransfer" oldCdn := createBlankCDN(cdnNameOld, t) From 069130ceab4b09798b83cdfe7fc36190773e4f2e Mon Sep 17 00:00:00 2001 From: mattjackson220 Date: Mon, 2 Aug 2021 15:58:33 -0600 Subject: [PATCH 07/10] added default cert config to cdn.conf --- docs/source/admin/traffic_ops.rst | 8 ++++++++ traffic_ops/app/conf/cdn.conf | 9 ++++++++- traffic_ops/traffic_ops_golang/config/config.go | 9 +++++++++ .../traffic_ops_golang/deliveryservice/sslkeys.go | 11 +++++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/docs/source/admin/traffic_ops.rst b/docs/source/admin/traffic_ops.rst index 2d4af77a37..e4b57655e9 100644 --- a/docs/source/admin/traffic_ops.rst +++ b/docs/source/admin/traffic_ops.rst @@ -315,6 +315,14 @@ This file deals with the configuration parameters of running Traffic Ops itself. :renew_days_before_expiration: Set the number of days before expiration date to renew certificates. :summary_email: The email address to use for summarizing certificate expiration and renewal status. If it is blank, no email will be sent. +:default_certificate_info: This is an optional object to define default values when generating a self signed certificate when an HTTPS delivery service is created or updated. If this is an empty object or not present in the :ref:`cdn.conf` then the term "Placeholder" will be used for all fields. + + :business_unit: An optional field which, if present, will represent the business unit for which the SSL certificate was generated + :city: An optional field which, if present, will represent the resident city of the generated SSL certificate + :organization: An optional field which, if present, will represent the organization for which the SSL certificate was generated + :country: An optional field which, if present, will represent the resident country of the generated SSL certificate + :state: An optional field which, if present, will represent the resident state or province of the generated SSL certificate + :geniso: This object contains configuration options for system ISO generation. :iso_root_path: Sets the filesystem path to the root of the ISO generation directory. For default installations, this should usually be set to :file:`/opt/traffic_ops/app/public`. diff --git a/traffic_ops/app/conf/cdn.conf b/traffic_ops/app/conf/cdn.conf index b46bf81f37..f44c4a4689 100644 --- a/traffic_ops/app/conf/cdn.conf +++ b/traffic_ops/app/conf/cdn.conf @@ -90,5 +90,12 @@ "kid" : "", "hmac_encoded" : "" } - ] + ], + "default_certificate_info" : { + "business_unit" : "", + "city" : "", + "organization" : "", + "country" : "", + "state" : "" + } } diff --git a/traffic_ops/traffic_ops_golang/config/config.go b/traffic_ops/traffic_ops_golang/config/config.go index af632f891b..2744a030dd 100644 --- a/traffic_ops/traffic_ops_golang/config/config.go +++ b/traffic_ops/traffic_ops_golang/config/config.go @@ -60,6 +60,7 @@ type Config struct { Version string UseIMS bool `json:"use_ims"` RoleBasedPermissions bool `json:"role_based_permissions"` + DefaultCertificateInfo *DefaultCertificateInfo `json:"default_certificate_info"` } // ConfigHypnotoad carries http setting for hypnotoad (mojolicious) server @@ -173,6 +174,14 @@ type ConfigAcmeAccount struct { HmacEncoded string `json:"hmac_encoded"` } +type DefaultCertificateInfo struct { + BusinessUnit string `json:"business_unit"` + City string `json:"city"` + Organization string `json:"organization"` + Country string `json:"country"` + State string `json:"state"` +} + // ConfigDatabase reflects the structure of the database.conf file type ConfigDatabase struct { Description string `json:"description"` diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go b/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go index fd8f5bd7ea..adb04e0c9a 100644 --- a/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go +++ b/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go @@ -23,6 +23,7 @@ import ( "context" "database/sql" "errors" + "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config" "net/http" "strconv" "strings" @@ -169,6 +170,16 @@ func GeneratePlaceholderSelfSignedCert(ds tc.DeliveryServiceV4, inf *api.APIInfo State: util.StrPtr("Placeholder"), }, } + + if (inf.Config.DefaultCertificateInfo != nil && *inf.Config.DefaultCertificateInfo != config.DefaultCertificateInfo{}) { + defaultCertInfo := inf.Config.DefaultCertificateInfo + req.BusinessUnit = &defaultCertInfo.BusinessUnit + req.City = &defaultCertInfo.City + req.Organization = &defaultCertInfo.Organization + req.Country = &defaultCertInfo.Country + req.State = &defaultCertInfo.State + } + if err := generatePutTrafficVaultSSLKeys(req, tx, inf.Vault, context); err != nil { return errors.New("generating and putting SSL keys: " + err.Error()), http.StatusInternalServerError } From efaf0ea667c0ac10bd8a4f7198ba968cfdf4459b Mon Sep 17 00:00:00 2001 From: mattjackson220 Date: Fri, 10 Sep 2021 11:42:36 -0600 Subject: [PATCH 08/10] updated gofmt --- traffic_ops/traffic_ops_golang/config/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/traffic_ops/traffic_ops_golang/config/config.go b/traffic_ops/traffic_ops_golang/config/config.go index 2744a030dd..a4a4cedace 100644 --- a/traffic_ops/traffic_ops_golang/config/config.go +++ b/traffic_ops/traffic_ops_golang/config/config.go @@ -58,8 +58,8 @@ type Config struct { InfluxEnabled bool InfluxDBConfPath string `json:"influxdb_conf_path"` Version string - UseIMS bool `json:"use_ims"` - RoleBasedPermissions bool `json:"role_based_permissions"` + UseIMS bool `json:"use_ims"` + RoleBasedPermissions bool `json:"role_based_permissions"` DefaultCertificateInfo *DefaultCertificateInfo `json:"default_certificate_info"` } From a5daf3027cb881030146b3556f600ddfa7794df0 Mon Sep 17 00:00:00 2001 From: mattjackson220 Date: Mon, 13 Sep 2021 11:02:58 -0600 Subject: [PATCH 09/10] added nil check --- .../traffic_ops_golang/deliveryservice/deliveryservices.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go index 0bc485fbdc..e432bfccd4 100644 --- a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go +++ b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go @@ -549,7 +549,7 @@ func createV40(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV40 t dsV40 = ds - if inf.Config.TrafficVaultEnabled && (*ds.Protocol == tc.DSProtocolHTTPS || *ds.Protocol == tc.DSProtocolHTTPAndHTTPS || *ds.Protocol == tc.DSProtocolHTTPToHTTPS) { + if inf.Config.TrafficVaultEnabled && ds.Protocol != nil && (*ds.Protocol == tc.DSProtocolHTTPS || *ds.Protocol == tc.DSProtocolHTTPAndHTTPS || *ds.Protocol == tc.DSProtocolHTTPToHTTPS) { err, errCode := GeneratePlaceholderSelfSignedCert(dsV40, inf, r.Context()) if err != nil || errCode != http.StatusOK { return nil, http.StatusInternalServerError, nil, fmt.Errorf("creating self signed default cert: %v", err) @@ -1134,7 +1134,7 @@ func updateV40(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV40 * dsV40 = (*tc.DeliveryServiceV40)(&ds) - if inf.Config.TrafficVaultEnabled && (*ds.Protocol == tc.DSProtocolHTTPS || *ds.Protocol == tc.DSProtocolHTTPAndHTTPS || *ds.Protocol == tc.DSProtocolHTTPToHTTPS) { + if inf.Config.TrafficVaultEnabled && ds.Protocol != nil && (*ds.Protocol == tc.DSProtocolHTTPS || *ds.Protocol == tc.DSProtocolHTTPAndHTTPS || *ds.Protocol == tc.DSProtocolHTTPToHTTPS) { err, errCode := GeneratePlaceholderSelfSignedCert(*dsV40, inf, r.Context()) if err != nil || errCode != http.StatusOK { return nil, http.StatusInternalServerError, nil, fmt.Errorf("creating self signed default cert: %v", err) From e5cf40b6d1eece3a0a14fe706267dc6d81c86a6a Mon Sep 17 00:00:00 2001 From: mattjackson220 Date: Tue, 21 Sep 2021 10:25:04 -0600 Subject: [PATCH 10/10] added default cert config validation and updated db tx use --- .../traffic_ops_golang/config/config.go | 24 +++++++++++++++++++ .../deliveryservice/deliveryservices.go | 4 ++-- .../deliveryservice/sslkeys.go | 17 +++++-------- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/traffic_ops/traffic_ops_golang/config/config.go b/traffic_ops/traffic_ops_golang/config/config.go index a4a4cedace..b7be58ae24 100644 --- a/traffic_ops/traffic_ops_golang/config/config.go +++ b/traffic_ops/traffic_ops_golang/config/config.go @@ -182,6 +182,30 @@ type DefaultCertificateInfo struct { State string `json:"state"` } +func (d *DefaultCertificateInfo) Validate() (error, bool) { + missingList := []string{} + if d.BusinessUnit == "" { + missingList = append(missingList, "BusinessUnit") + } + if d.City == "" { + missingList = append(missingList, "City") + } + if d.Organization == "" { + missingList = append(missingList, "Organization") + } + if d.Country == "" { + missingList = append(missingList, "Country") + } + if d.State == "" { + missingList = append(missingList, "State") + } + + if len(missingList) != 0 { + return fmt.Errorf("default certificate information is missing: %s", missingList), false + } + return nil, true +} + // ConfigDatabase reflects the structure of the database.conf file type ConfigDatabase struct { Description string `json:"description"` diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go index e432bfccd4..7a675fb212 100644 --- a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go +++ b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go @@ -552,7 +552,7 @@ func createV40(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV40 t if inf.Config.TrafficVaultEnabled && ds.Protocol != nil && (*ds.Protocol == tc.DSProtocolHTTPS || *ds.Protocol == tc.DSProtocolHTTPAndHTTPS || *ds.Protocol == tc.DSProtocolHTTPToHTTPS) { err, errCode := GeneratePlaceholderSelfSignedCert(dsV40, inf, r.Context()) if err != nil || errCode != http.StatusOK { - return nil, http.StatusInternalServerError, nil, fmt.Errorf("creating self signed default cert: %v", err) + return nil, errCode, nil, fmt.Errorf("creating self signed default cert: %v", err) } } @@ -1137,7 +1137,7 @@ func updateV40(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV40 * if inf.Config.TrafficVaultEnabled && ds.Protocol != nil && (*ds.Protocol == tc.DSProtocolHTTPS || *ds.Protocol == tc.DSProtocolHTTPAndHTTPS || *ds.Protocol == tc.DSProtocolHTTPToHTTPS) { err, errCode := GeneratePlaceholderSelfSignedCert(*dsV40, inf, r.Context()) if err != nil || errCode != http.StatusOK { - return nil, http.StatusInternalServerError, nil, fmt.Errorf("creating self signed default cert: %v", err) + return nil, errCode, nil, fmt.Errorf("creating self signed default cert: %v", err) } } diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go b/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go index adb04e0c9a..d1b1f2e224 100644 --- a/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go +++ b/traffic_ops/traffic_ops_golang/deliveryservice/sslkeys.go @@ -23,7 +23,6 @@ import ( "context" "database/sql" "errors" - "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config" "net/http" "strconv" "strings" @@ -31,6 +30,7 @@ import ( "github.com/apache/trafficcontrol/lib/go-tc" "github.com/apache/trafficcontrol/lib/go-util" "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api" + "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config" "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers" "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/tenant" "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault" @@ -117,16 +117,7 @@ func generatePutTrafficVaultSSLKeys(req tc.DeliveryServiceGenSSLKeysReq, tx *sql // GeneratePlaceholderSelfSignedCert generates a self-signed SSL certificate as a placeholder when a new HTTPS // delivery service is created or an HTTP delivery service is updated to use HTTPS. func GeneratePlaceholderSelfSignedCert(ds tc.DeliveryServiceV4, inf *api.APIInfo, context context.Context) (error, int) { - db, err := api.GetDB(context) - if err != nil { - return err, http.StatusInternalServerError - } - tx, err := db.Begin() - if err != nil { - return err, http.StatusInternalServerError - } - defer tx.Commit() - + tx := inf.Tx.Tx tv := inf.Vault _, ok, err := tv.GetDeliveryServiceSSLKeys(*ds.XMLID, "", tx, context) if err != nil { @@ -173,6 +164,10 @@ func GeneratePlaceholderSelfSignedCert(ds tc.DeliveryServiceV4, inf *api.APIInfo if (inf.Config.DefaultCertificateInfo != nil && *inf.Config.DefaultCertificateInfo != config.DefaultCertificateInfo{}) { defaultCertInfo := inf.Config.DefaultCertificateInfo + if err, ok := defaultCertInfo.Validate(); !ok { + return err, http.StatusInternalServerError + } + req.BusinessUnit = &defaultCertInfo.BusinessUnit req.City = &defaultCertInfo.City req.Organization = &defaultCertInfo.Organization