Skip to content
This repository was archived by the owner on Nov 24, 2025. It is now read-only.

Commit 9516de3

Browse files
updated database and added script to pull expiration info from cert data (#6337)
* updated database and added script to pull expiration info from cert data * updates per comments * fixed print error * added check for 2 commas in id and removed "optional" labels * moved fill_expiration_and_provider files to tools
1 parent 8a86c6e commit 9516de3

4 files changed

Lines changed: 264 additions & 0 deletions

File tree

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
package main
2+
3+
/*
4+
* Licensed to the Apache Software Foundation (ASF) under one or more
5+
* contributor license agreements. See the NOTICE file distributed with this
6+
* work for additional information regarding copyright ownership. The ASF
7+
* licenses this file to you under the Apache License, Version 2.0 (the
8+
* "License"); you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16+
* License for the specific language governing permissions and limitations under
17+
* the License.
18+
*/
19+
20+
import (
21+
"crypto/x509"
22+
"database/sql"
23+
"encoding/base64"
24+
"encoding/json"
25+
"encoding/pem"
26+
"errors"
27+
"flag"
28+
"fmt"
29+
"github.com/apache/trafficcontrol/lib/go-tc"
30+
"github.com/apache/trafficcontrol/lib/go-util"
31+
"github.com/jmoiron/sqlx"
32+
"io/ioutil"
33+
"os"
34+
"strings"
35+
"time"
36+
37+
_ "github.com/lib/pq"
38+
)
39+
40+
const PROPERTIES_FILE = "./fill_expiration_and_provider_conf.json"
41+
42+
func main() {
43+
aesKeyLocation := flag.String("aes-key", "/opt/traffic_ops/app/conf/aes.key", "The file path for the previous base64 encoded AES key. Default is /opt/traffic_ops/app/conf/aes.key.")
44+
cfg := flag.String("cfg", PROPERTIES_FILE, "The path for the configuration file. Default is "+PROPERTIES_FILE+".")
45+
help := flag.Bool("help", false, "Print usage information and exit.")
46+
flag.Parse()
47+
48+
if *help {
49+
flag.Usage()
50+
os.Exit(0)
51+
}
52+
53+
aesKey, err := readKey(*aesKeyLocation)
54+
if err != nil {
55+
die("reading previous-key: " + err.Error())
56+
}
57+
58+
dbConfBytes, err := ioutil.ReadFile(*cfg)
59+
if err != nil {
60+
die("reading db conf '" + *cfg + "': " + err.Error())
61+
}
62+
63+
pgCfg := Config{}
64+
err = json.Unmarshal(dbConfBytes, &pgCfg)
65+
if err != nil {
66+
die("unmarshalling '" + *cfg + "': " + err.Error())
67+
}
68+
69+
sslStr := "require"
70+
if !pgCfg.SSL {
71+
sslStr = "disable"
72+
}
73+
74+
db, err := sqlx.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s&fallback_application_name=trafficvault", pgCfg.User, pgCfg.Password, pgCfg.Hostname, pgCfg.Port, pgCfg.DBName, sslStr))
75+
if err != nil {
76+
die("opening database: " + err.Error())
77+
}
78+
79+
tx, err := db.Begin()
80+
if err != nil {
81+
die(fmt.Sprintf("transaction begin failed %v %v ", err, tx))
82+
}
83+
defer tx.Commit()
84+
85+
rows, err := tx.Query("SELECT deliveryservice, cdn, version, data, provider, expiration FROM sslkey")
86+
if err != nil {
87+
die("querying: " + err.Error())
88+
}
89+
defer rows.Close()
90+
91+
type expiryAndProvider struct {
92+
Provider string
93+
Expiration time.Time
94+
}
95+
sslKeyMap := map[string]expiryAndProvider{}
96+
97+
for rows.Next() {
98+
var ds string
99+
var cdn string
100+
var version string
101+
var encryptedSslKeys []byte
102+
provider := sql.NullString{}
103+
var expiration time.Time
104+
if err = rows.Scan(&ds, &cdn, &version, &encryptedSslKeys, &provider, &expiration); err != nil {
105+
die("getting SSL Keys: " + err.Error())
106+
}
107+
id := strings.Join([]string{ds, cdn, version}, ", ")
108+
jsonKeys, err := util.AESDecrypt(encryptedSslKeys, aesKey)
109+
if err != nil {
110+
die("reading SSL Keys: " + err.Error())
111+
}
112+
113+
sslKey := tc.DeliveryServiceSSLKeysV15{}
114+
err = json.Unmarshal([]byte(jsonKeys), &sslKey)
115+
if err != nil {
116+
die("unmarshalling ssl keys: " + err.Error())
117+
}
118+
119+
parsedCert := sslKey.Certificate
120+
err = Base64DecodeCertificate(&parsedCert)
121+
if err != nil {
122+
die("getting SSL keys for ID '" + id + "': " + err.Error())
123+
}
124+
125+
block, _ := pem.Decode([]byte(parsedCert.Crt))
126+
if block == nil {
127+
die("Error decoding cert to parse expiration")
128+
}
129+
130+
x509cert, err := x509.ParseCertificate(block.Bytes)
131+
if err != nil {
132+
die("Error parsing cert to get expiration - " + err.Error())
133+
}
134+
135+
sslKeyMap[id] = expiryAndProvider{
136+
Provider: sslKey.AuthType,
137+
Expiration: x509cert.NotAfter,
138+
}
139+
}
140+
141+
for id, info := range sslKeyMap {
142+
if strings.Count(id, ",") != 2 {
143+
die("found id that does not contain 2 commas: " + id)
144+
}
145+
idParts := strings.Split(id, ", ")
146+
if len(idParts) != 3 {
147+
die(fmt.Sprintf("expected cert id string (ds, cdn, version) to have 3 parts but found %d in %s", len(idParts), idParts))
148+
}
149+
ds := idParts[0]
150+
cdn := idParts[1]
151+
version := idParts[2]
152+
res, err := tx.Exec(`UPDATE sslkey SET provider = $1, expiration = $2 WHERE deliveryservice = $3 AND cdn = $4 AND version = $5`, info.Provider, info.Expiration, ds, cdn, version)
153+
if err != nil {
154+
die(fmt.Sprintf("updating SSL Keys for %s, %s", id, err))
155+
}
156+
rowsAffected, err := res.RowsAffected()
157+
if err != nil {
158+
die(fmt.Sprintf("determining rows affected for expiration and provider in SSL Keys: %s: %s", id, err.Error()))
159+
}
160+
if rowsAffected == 0 {
161+
die(fmt.Sprintf("no rows updated for expiration and provider in SSL Keys for %s", id))
162+
}
163+
}
164+
}
165+
166+
type Config struct {
167+
DBName string `json:"dbname"`
168+
Hostname string `json:"hostname"`
169+
User string `json:"user"`
170+
Password string `json:"password"`
171+
Port int `json:"port"`
172+
SSL bool `json:"ssl"`
173+
}
174+
175+
func readKey(keyLocation string) ([]byte, error) {
176+
var keyBase64 string
177+
keyBase64Bytes, err := ioutil.ReadFile(keyLocation)
178+
if err != nil {
179+
return []byte{}, fmt.Errorf("reading file '"+keyLocation+"': %s", err)
180+
}
181+
keyBase64 = string(keyBase64Bytes)
182+
183+
key, err := base64.StdEncoding.DecodeString(keyBase64)
184+
if err != nil {
185+
return []byte{}, fmt.Errorf("AES key cannot be decoded from base64: %s", err)
186+
}
187+
188+
// verify the key works
189+
if err = util.ValidateAESKey(key); err != nil {
190+
return []byte{}, err
191+
}
192+
193+
return key, nil
194+
}
195+
196+
func die(message string) {
197+
fmt.Fprintln(os.Stderr, message)
198+
os.Exit(1)
199+
}
200+
201+
func Base64DecodeCertificate(cert *tc.DeliveryServiceSSLKeysCertificate) error {
202+
csrDec, err := base64.StdEncoding.DecodeString(cert.CSR)
203+
if err != nil {
204+
return errors.New("base64 decoding csr: " + err.Error())
205+
}
206+
cert.CSR = string(csrDec)
207+
crtDec, err := base64.StdEncoding.DecodeString(cert.Crt)
208+
if err != nil {
209+
return errors.New("base64 decoding crt: " + err.Error())
210+
}
211+
cert.Crt = string(crtDec)
212+
keyDec, err := base64.StdEncoding.DecodeString(cert.Key)
213+
if err != nil {
214+
return errors.New("base64 decoding key: " + err.Error())
215+
}
216+
cert.Key = string(keyDec)
217+
return nil
218+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"dbname": "",
3+
"hostname": "",
4+
"user": "",
5+
"password": "",
6+
"port": 5432,
7+
"ssl": false
8+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with this
4+
* work for additional information regarding copyright ownership. The ASF
5+
* licenses this file to you under the Apache License, Version 2.0 (the
6+
* "License"); you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
* License for the specific language governing permissions and limitations under
15+
* the License.
16+
*/
17+
18+
ALTER TABLE sslkey DROP COLUMN provider;
19+
ALTER TABLE sslkey DROP COLUMN expiration;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with this
4+
* work for additional information regarding copyright ownership. The ASF
5+
* licenses this file to you under the Apache License, Version 2.0 (the
6+
* "License"); you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
* License for the specific language governing permissions and limitations under
15+
* the License.
16+
*/
17+
18+
ALTER TABLE sslkey ADD COLUMN IF NOT EXISTS provider text;
19+
ALTER TABLE sslkey ADD COLUMN IF NOT EXISTS expiration timestamp with time zone NOT NULL DEFAULT now();

0 commit comments

Comments
 (0)