This is an automated email from the ASF dual-hosted git repository.
ocket8888 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 9669824 Cert Expirations Traffic Ops Endpoint (#6338)
9669824 is described below
commit 9669824f6ce329ba021d5b7ab11ca3f146e9dc04
Author: mattjackson220 <[email protected]>
AuthorDate: Thu Dec 16 13:12:48 2021 -0700
Cert Expirations Traffic Ops Endpoint (#6338)
* updated database and added script to pull expiration info from cert data
* added API endpoint to show SSL cert expiration information
* added int check on days parameter
* fixed days param check
* removed old files from previous PR
* updated per comments
* added trailing / ability to new endpoint
* updated per comments
* removed DS ID from API response
* updated per comments
* added migration to make provider not NULL
* updated TO for non-null provider
---
docs/source/api/v4/sslkey_expirations.rst | 90 ++++++++++++++++++++++
lib/go-tc/deliveryservice_ssl_keys.go | 9 +++
...2021121011532000_set_provider_not_null.down.sql | 20 +++++
.../2021121011532000_set_provider_not_null.up.sql | 23 ++++++
.../deliveryservice/autorenewcerts.go | 2 +-
.../traffic_ops_golang/deliveryservice/keys.go | 32 +++++++-
traffic_ops/traffic_ops_golang/routing/routes.go | 3 +
.../trafficvault/backends/disabled/disabled.go | 4 +
.../trafficvault/backends/postgres/postgres.go | 69 ++++++++++++++++-
.../trafficvault/backends/riaksvc/riak.go | 4 +
.../trafficvault/trafficvault.go | 2 +
11 files changed, 253 insertions(+), 5 deletions(-)
diff --git a/docs/source/api/v4/sslkey_expirations.rst
b/docs/source/api/v4/sslkey_expirations.rst
new file mode 100644
index 0000000..f299c87
--- /dev/null
+++ b/docs/source/api/v4/sslkey_expirations.rst
@@ -0,0 +1,90 @@
+..
+..
+.. Licensed under the Apache License, Version 2.0 (the "License");
+.. you may not use this file except in compliance with the License.
+.. You may obtain a copy of the License at
+..
+.. http://www.apache.org/licenses/LICENSE-2.0
+..
+.. Unless required by applicable law or agreed to in writing, software
+.. distributed under the License is distributed on an "AS IS" BASIS,
+.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+.. See the License for the specific language governing permissions and
+.. limitations under the License.
+..
+
+.. _to-api-sslkey_expirations:
+
+**********************
+``sslkey_expirations``
+**********************
+
+``GET``
+=======
+Retrieves SSL certificate expiration information.
+
+:Auth. Required: Yes
+:Roles Required: "admin"
+:Permissions Required: ACME:READ
+:Response Type: Array
+
+Request Structure
+-----------------
+.. table:: Request Query Parameters
+
+
+-------------------+----------+--------------------------------------------------------------------------------------------------------+
+ | Name | Required | Description
|
+
+===================+==========+========================================================================================================+
+ | days | no | Return only the expiration information
for SSL certificates expiring in the next given number of days. |
+
+-------------------+----------+--------------------------------------------------------------------------------------------------------+
+
+.. code-block:: http
+ :caption: Request Example
+
+ GET /api/4.0/sslkey_expirations?days=30 HTTP/1.1
+ Host: trafficops.infra.ciab.test
+ Accept: */*
+ Connection: keep-alive
+ Cookie: mojolicious=...
+
+Response Structure
+------------------
+:deliveryservice: The :ref:`ds-xmlid` for the :term:`Delivery Service`
corresponding to this SSL certificate.
+:cdn: The ID for the :abbr:`CDN (Content Delivery Network)`
corresponding to this SSL certificate.
+:provider: The provider of this SSL certificate, generally the name
of the Certificate Authority or the :abbr:`ACME (Automatic Certificate
Management Environment)` account.
+:expiration: The expiration date of this SSL certificate.
+:federated: A boolean indicating if this SSL certificate is use in a
federated :term:`Delivery Service`.
+
+.. code-block:: http
+ :caption: Response Example
+
+ HTTP/1.1 200 OK
+ Access-Control-Allow-Credentials: true
+ Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type,
Accept, Set-Cookie, Cookie
+ Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE
+ Access-Control-Allow-Origin: *
+ Content-Encoding: gzip
+ Content-Type: application/json
+ Permissions-Policy: interest-cohort=()
+ Set-Cookie: mojolicious=...; Path=/; Expires=Mon, 07 Jun 2021 22:52:20
GMT; Max-Age=3600; HttpOnly
+ Vary: Accept-Encoding
+ X-Server-Name: traffic_ops_golang/
+ Date: Mon, 07 Jun 2021 21:52:20 GMT
+ Content-Length: 384
+
+ { "response": [
+ {
+ "deliveryservice": "foo1",
+ "cdn": "cdn1",
+ "provider": "Self Signed",
+ "expiration": "2022-08-02T15:38:06-06:00",
+ "federated": false
+ },
+ {
+ "deliveryservice": "foo2",
+ "cdn": "cdn2",
+ "provider": "Lets Encrypt",
+ "expiration": "2022-07-12T12:14:00-06:00",
+ "federated": true
+ }
+ ]}
diff --git a/lib/go-tc/deliveryservice_ssl_keys.go
b/lib/go-tc/deliveryservice_ssl_keys.go
index f5acef4..e40173f 100644
--- a/lib/go-tc/deliveryservice_ssl_keys.go
+++ b/lib/go-tc/deliveryservice_ssl_keys.go
@@ -93,6 +93,15 @@ type DeliveryServiceSSLKeysV15 struct {
Expiration time.Time `json:"expiration,omitempty"`
}
+// SSLKeyExpirationInformation contains information about an SSL key's
expiration.
+type SSLKeyExpirationInformation struct {
+ DeliveryService string `json:"deliveryservice"`
+ CDN string `json:"cdn"`
+ Provider string `json:"provider"`
+ Expiration time.Time `json:"expiration"`
+ Federated bool `json:"federated"`
+}
+
// SSLKeyRequestFields contain metadata information for generating SSL keys for
// Delivery Services through the Traffic Ops API. Specifically, they contain
// everything except the manner in which the generated certificates should be
diff --git
a/traffic_ops/app/db/trafficvault/migrations/2021121011532000_set_provider_not_null.down.sql
b/traffic_ops/app/db/trafficvault/migrations/2021121011532000_set_provider_not_null.down.sql
new file mode 100644
index 0000000..82fa54b
--- /dev/null
+++
b/traffic_ops/app/db/trafficvault/migrations/2021121011532000_set_provider_not_null.down.sql
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+
+ALTER TABLE ONLY sslkey
+ALTER COLUMN provider
+DROP NOT NULL;
diff --git
a/traffic_ops/app/db/trafficvault/migrations/2021121011532000_set_provider_not_null.up.sql
b/traffic_ops/app/db/trafficvault/migrations/2021121011532000_set_provider_not_null.up.sql
new file mode 100644
index 0000000..ac75a24
--- /dev/null
+++
b/traffic_ops/app/db/trafficvault/migrations/2021121011532000_set_provider_not_null.up.sql
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+
+UPDATE sslkey
+SET provider = COALESCE(provider, '');
+
+ALTER TABLE ONLY sslkey
+ALTER COLUMN provider
+SET NOT NULL;
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/autorenewcerts.go
b/traffic_ops/traffic_ops_golang/deliveryservice/autorenewcerts.go
index 2791c71..85aa3cf 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 :=
parseExpirationAndSansFromCert([]byte(keyObj.Certificate.Crt), keyObj.Hostname)
+ 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 82af2dd..c834f55 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/keys.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/keys.go
@@ -136,6 +136,33 @@ func AddSSLKeys(w http.ResponseWriter, r *http.Request) {
api.WriteResp(w, r, "Successfully added ssl keys for
"+*req.DeliveryService)
}
+// GetSSlKeyExpirationInformation gets expiration information for all SSL
certificates.
+func GetSSlKeyExpirationInformation(w http.ResponseWriter, r *http.Request) {
+ inf, userErr, sysErr, errCode := api.NewInfo(r, nil, []string{"days"})
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+ return
+ }
+ defer inf.Close()
+ if !inf.Config.TrafficVaultEnabled {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, errors.New("getting SSL keys expiration information from Traffic Vault:
Traffic Vault is not configured"))
+ return
+ }
+
+ daysParam := 0
+ if days, ok := inf.IntParams["days"]; ok {
+ daysParam = days
+ }
+
+ expirationInfos, err := inf.Vault.GetExpirationInformation(inf.Tx.Tx,
r.Context(), daysParam)
+ if err != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, errors.New("getting SSL keys expiration information from Traffic Vault:
"+err.Error()))
+ return
+ }
+
+ api.WriteResp(w, r, expirationInfos)
+}
+
// 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)
@@ -201,7 +228,7 @@ func getSslKeys(inf *api.APIInfo, ctx context.Context)
(tc.DeliveryServiceSSLKey
}
if keyObj.Certificate.Crt != "" && keyObj.Expiration.IsZero() {
- exp, sans, err :=
parseExpirationAndSansFromCert([]byte(parsedCert.Crt), keyObj.Hostname)
+ exp, sans, err :=
ParseExpirationAndSansFromCert([]byte(parsedCert.Crt), keyObj.Hostname)
if err != nil {
return tc.DeliveryServiceSSLKeysV4{},
errors.New(xmlID + ": " + err.Error())
}
@@ -213,7 +240,8 @@ func getSslKeys(inf *api.APIInfo, ctx context.Context)
(tc.DeliveryServiceSSLKey
return keyObj, nil
}
-func parseExpirationAndSansFromCert(cert []byte, commonName string)
(time.Time, []string, error) {
+// ParseExpirationAndSansFromCert returns the expiration and SANs from a
certificate.
+func ParseExpirationAndSansFromCert(cert []byte, commonName string)
(time.Time, []string, error) {
block, _ := pem.Decode(cert)
if block == nil {
return time.Time{}, []string{}, errors.New("Error decoding cert
to parse expiration")
diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go
b/traffic_ops/traffic_ops_golang/routing/routes.go
index c6112c2..710220d 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: 0}, http.MethodGet,
`sslkey_expirations/?$`, deliveryservice.GetSSlKeyExpirationInformation,
auth.PrivLevelAdmin, []string{"SSL-KEY-EXPIRATION:READ"}, Authenticated, nil,
41357729075},
+
// CDN lock
{api.Version{Major: 4, Minor: 0}, http.MethodGet,
`cdn_locks/?$`, cdn_lock.Read, auth.PrivLevelReadOnly, []string{"CDN:READ"},
Authenticated, nil, 4134390561},
{api.Version{Major: 4, Minor: 0}, http.MethodPost,
`cdn_locks/?$`, cdn_lock.Create, auth.PrivLevelOperations,
[]string{"CDN-LOCK:CREATE", "CDN:READ"}, Authenticated, nil, 4134390562},
diff --git
a/traffic_ops/traffic_ops_golang/trafficvault/backends/disabled/disabled.go
b/traffic_ops/traffic_ops_golang/trafficvault/backends/disabled/disabled.go
index d18d68b..c79f566 100644
--- a/traffic_ops/traffic_ops_golang/trafficvault/backends/disabled/disabled.go
+++ b/traffic_ops/traffic_ops_golang/trafficvault/backends/disabled/disabled.go
@@ -45,6 +45,10 @@ func (d *Disabled) GetDeliveryServiceSSLKeys(xmlID string,
version string, tx *s
return tc.DeliveryServiceSSLKeysV15{}, false, disabledErr
}
+func (d *Disabled) GetExpirationInformation(tx *sql.Tx, ctx context.Context,
days int) ([]tc.SSLKeyExpirationInformation, error) {
+ return []tc.SSLKeyExpirationInformation{}, disabledErr
+}
+
func (d *Disabled) PutDeliveryServiceSSLKeys(key tc.DeliveryServiceSSLKeys, tx
*sql.Tx, ctx context.Context) error {
return disabledErr
}
diff --git
a/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres/postgres.go
b/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres/postgres.go
index 47bd2dd..b03b48d 100644
--- a/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres/postgres.go
+++ b/traffic_ops/traffic_ops_golang/trafficvault/backends/postgres/postgres.go
@@ -33,6 +33,7 @@ import (
"github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/lib/go-tc/tovalidate"
"github.com/apache/trafficcontrol/lib/go-util"
+
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/deliveryservice"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/trafficvault"
validation "github.com/go-ozzo/ozzo-validation"
@@ -156,6 +157,61 @@ func (p *Postgres) GetDeliveryServiceSSLKeys(xmlID string,
version string, tx *s
return sslKey, true, nil
}
+// GetExpirationInformation returns the expiration information for all SSL
Keys.
+func (p *Postgres) GetExpirationInformation(tx *sql.Tx, ctx context.Context,
days int) ([]tc.SSLKeyExpirationInformation, error) {
+ tvTx, dbCtx, cancelFunc, err := p.beginTransaction(ctx)
+ if err != nil {
+ return []tc.SSLKeyExpirationInformation{}, err
+ }
+ defer p.commitTransaction(tvTx, dbCtx, cancelFunc)
+
+ var fedList []string
+ fedRows, err := tx.Query("SELECT DISTINCT(ds.xml_id) FROM
federation_deliveryservice AS fd JOIN deliveryservice AS ds ON ds.id =
fd.deliveryservice")
+ if err != nil {
+ return []tc.SSLKeyExpirationInformation{}, err
+ }
+ defer fedRows.Close()
+
+ for fedRows.Next() {
+ var fedString string
+ if err = fedRows.Scan(&fedString); err != nil {
+ return []tc.SSLKeyExpirationInformation{}, err
+ }
+ fedList = append(fedList, fedString)
+ }
+
+ query := "SELECT deliveryservice, cdn, provider, expiration FROM sslkey
WHERE version='latest'"
+
+ if days != 0 {
+ query = query + fmt.Sprintf(" AND expiration <= (now() + '%d
days'::interval)", days)
+ }
+
+ expirationInfos := []tc.SSLKeyExpirationInformation{}
+
+ rows, err := tvTx.Query(query)
+ if err != nil {
+ return []tc.SSLKeyExpirationInformation{}, err
+ }
+ defer rows.Close()
+
+ for rows.Next() {
+ var expirationInfo tc.SSLKeyExpirationInformation
+ if err = rows.Scan(&expirationInfo.DeliveryService,
&expirationInfo.CDN, &expirationInfo.Provider, &expirationInfo.Expiration); err
!= nil {
+ return []tc.SSLKeyExpirationInformation{}, err
+ }
+ expirationInfo.Federated = false
+ for _, fed := range fedList {
+ if fed == expirationInfo.DeliveryService {
+ expirationInfo.Federated = true
+ }
+ }
+
+ expirationInfos = append(expirationInfos, expirationInfo)
+ }
+
+ return expirationInfos, nil
+}
+
// PutDeliveryServiceSSLKeys stores the given SSL keys for a delivery service.
func (p *Postgres) PutDeliveryServiceSSLKeys(key tc.DeliveryServiceSSLKeys, tx
*sql.Tx, ctx context.Context) error {
tvTx, dbCtx, cancelFunc, err := p.beginTransaction(ctx)
@@ -178,11 +234,20 @@ func (p *Postgres) PutDeliveryServiceSSLKeys(key
tc.DeliveryServiceSSLKeys, tx *
encryptedKey, err := util.AESEncrypt(keyJSON, p.aesKey)
if err != nil {
- return errors.New("encrypting keys: " + err.Error())
+ return fmt.Errorf("encrypting keys: %w", err)
+ }
+
+ err = deliveryservice.Base64DecodeCertificate(&key.Certificate)
+ if err != nil {
+ return fmt.Errorf("decoding SSL keys, %w", err)
+ }
+ expiration, _, err :=
deliveryservice.ParseExpirationAndSansFromCert([]byte(key.Certificate.Crt),
key.Hostname)
+ if err != nil {
+ return fmt.Errorf("parsing expiration from certificate: %w",
err)
}
// insert the new ssl keys now
- res, err := tvTx.Exec("INSERT INTO sslkey (deliveryservice, data, cdn,
version) VALUES ($1, $2, $3, $4), ($5, $6, $7, $8)", key.DeliveryService,
encryptedKey, key.CDN, strconv.FormatInt(int64(key.Version), 10),
key.DeliveryService, encryptedKey, key.CDN, latestVersion)
+ res, err := tvTx.Exec("INSERT INTO sslkey (deliveryservice, data, cdn,
version, provider, expiration) VALUES ($1, $2, $3, $4, $5, $6), ($7, $8, $9,
$10, $11, $12)", key.DeliveryService, encryptedKey, key.CDN,
strconv.FormatInt(int64(key.Version), 10), key.AuthType, expiration,
key.DeliveryService, encryptedKey, key.CDN, latestVersion, key.AuthType,
expiration)
if err != nil {
e := checkErrWithContext("Traffic Vault PostgreSQL: executing
INSERT SSL Key query", err, ctx.Err())
return e
diff --git
a/traffic_ops/traffic_ops_golang/trafficvault/backends/riaksvc/riak.go
b/traffic_ops/traffic_ops_golang/trafficvault/backends/riaksvc/riak.go
index e7fb923..0daa48b 100644
--- a/traffic_ops/traffic_ops_golang/trafficvault/backends/riaksvc/riak.go
+++ b/traffic_ops/traffic_ops_golang/trafficvault/backends/riaksvc/riak.go
@@ -44,6 +44,10 @@ func (r *Riak) GetDeliveryServiceSSLKeys(xmlID string,
version string, tx *sql.T
return getDeliveryServiceSSLKeysObjV15(xmlID, version, tx,
&r.cfg.AuthOptions, &r.cfg.Port)
}
+func (r *Riak) GetExpirationInformation(tx *sql.Tx, ctx context.Context, days
int) ([]tc.SSLKeyExpirationInformation, error) {
+ return []tc.SSLKeyExpirationInformation{}, errors.New("Not implemented
for this Traffic Vault backend.")
+}
+
func (r *Riak) PutDeliveryServiceSSLKeys(key tc.DeliveryServiceSSLKeys, tx
*sql.Tx, ctx context.Context) error {
return putDeliveryServiceSSLKeysObj(key, tx, &r.cfg.AuthOptions,
&r.cfg.Port)
}
diff --git a/traffic_ops/traffic_ops_golang/trafficvault/trafficvault.go
b/traffic_ops/traffic_ops_golang/trafficvault/trafficvault.go
index 08031d8..9b58a90 100644
--- a/traffic_ops/traffic_ops_golang/trafficvault/trafficvault.go
+++ b/traffic_ops/traffic_ops_golang/trafficvault/trafficvault.go
@@ -42,6 +42,8 @@ type TrafficVault interface {
// the delivery service identified by the given xmlID. If version is
empty,
// the implementation should return the latest version.
GetDeliveryServiceSSLKeys(xmlID string, version string, tx *sql.Tx, ctx
context.Context) (tc.DeliveryServiceSSLKeysV15, bool, error)
+ // GetExpirationInformation retrieves the SSL key expiration
information for all delivery services.
+ GetExpirationInformation(tx *sql.Tx, ctx context.Context, days int)
([]tc.SSLKeyExpirationInformation, error)
// PutDeliveryServiceSSLKeys stores the given SSL keys for a delivery
service.
PutDeliveryServiceSSLKeys(key tc.DeliveryServiceSSLKeys, tx *sql.Tx,
ctx context.Context) error
// DeleteDeliveryServiceSSLKeys removes the SSL keys of the given
version (or latest