Skip to content

Commit 38930d8

Browse files
committed
BUG/MEDIUM: runtime: get the real storage name of certificates
When calling the runtime command "show ssl cert", the returned field "Filename" is not always the filename of the certificate. If you pass in an alias, "Filename" will be the alias, not the corresponding filename. So how can we obtain the true storage name of a certificate? Recent versions of HAProxy now return 2 new fields for that: "Crt filename" and "Key filename". This patch uses the "Crt filename" as the storage name when it is provided, and falls back to "Filename". Note that storing the private key in a separate file is not supported by client-native and the other projects depending on it. The function `ShowCertificate` will return an error if "Crt filename" and "Key filename" are different.
1 parent 2481ec7 commit 38930d8

2 files changed

Lines changed: 91 additions & 3 deletions

File tree

runtime/certs.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package runtime
22

33
import (
4+
"errors"
45
"fmt"
56
"strings"
67
"time"
@@ -66,7 +67,7 @@ func (s *SingleRuntime) parseCert(line string) *models.SslCertificate {
6667

6768
// parseCertEntry parses one entry in one CrtList file and returns it structured
6869
// example:
69-
// Filename: /etc/ssl/cert-2.pem
70+
// Filename: /etc/ssl/cert-2.pem <= this is just the same name you gave as certificate name, could be an alias
7071
// Status: Used
7172
// Serial: 0D933C1B1089BF660AE5253A245BB388
7273
// notBefore: Sep 9 00:00:00 2020 GMT
@@ -78,13 +79,18 @@ func (s *SingleRuntime) parseCert(line string) *models.SslCertificate {
7879
// Issuer: /C=US/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA
7980
// Chain Subject: /C=US/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA
8081
// Chain Issuer: /C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root CA
82+
// *** undocumented, only in recent versions, "Key filename" is optional ***
83+
// Crt filename: /etc/ssl/cert-2.pem
84+
// Key filename: /etc/ssl/cert-2.key
8185
func parseCertEntry(response string) (*models.SslCertificate, error) {
8286
response = strings.TrimSpace(response)
8387
if response == "" {
8488
return nil, native_errors.ErrNotFound
8589
}
8690

8791
c := &models.SslCertificate{}
92+
var crtFilename string
93+
var keyFilename string
8894

8995
for _, line := range strings.Split(response, "\n") {
9096
key, val, found := strings.Cut(line, ": ")
@@ -118,9 +124,27 @@ func parseCertEntry(response string) (*models.SslCertificate, error) {
118124
c.ChainSubject = val
119125
case "Chain Issuer":
120126
c.ChainIssuer = val
127+
case "Crt filename":
128+
crtFilename = val
129+
case "Key filename":
130+
keyFilename = val
121131
}
122132
}
123133

134+
if crtFilename != "" {
135+
c.StorageName = crtFilename
136+
}
137+
138+
// We currently do not support storing the key in a separate file.
139+
if keyFilename != "" && keyFilename != crtFilename {
140+
return nil, fmt.Errorf("failed to parse certificate info for %s: storing the private key in a separate file is not supported", c.StorageName)
141+
}
142+
143+
// This should be impossible.
144+
if c.StorageName == "" {
145+
return nil, errors.New("failed to parse certificate info: empty filename")
146+
}
147+
124148
return c, nil
125149
}
126150

runtime/certs_test.go

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ func TestSingleRuntime_ShowCertEntry(t *testing.T) {
189189
name: "Simple show certs, should return a cert",
190190
fields: fields{socketPath: haProxy.Addr().String()},
191191
args: args{
192-
storageName: "/etc/ssl/cert-0.pem",
192+
storageName: "cert-0",
193193
},
194194
want: &models.SslCertificate{
195195
StorageName: "/etc/ssl/cert-0.pem",
@@ -206,7 +206,69 @@ func TestSingleRuntime_ShowCertEntry(t *testing.T) {
206206
ChainIssuer: "/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root CA",
207207
},
208208
socketResponse: map[string]string{
209-
"show ssl cert /etc/ssl/cert-0.pem\n": ` Filename: /etc/ssl/cert-0.pem
209+
"show ssl cert cert-0\n": ` Filename: cert-0
210+
Status: Used
211+
Serial: 0D933C1B1089BF660AE5253A245BB388
212+
notBefore: Sep 9 00:00:00 2020 GMT
213+
notAfter: Sep 14 12:00:00 2021 GMT
214+
Subject Alternative Name: DNS:*.platform.domain.com, DNS:uaa.platform.domain.com
215+
Algorithm: RSA4096
216+
SHA1 FingerPrint: 59242F1838BDEF3E7DAFC83FFE4DD6C03B88805C
217+
Subject: /C=DE/ST=Baden-Württemberg/L=Walldorf/O=ORG SE/CN=*.platform.domain.com
218+
Issuer: /C=US/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA
219+
Chain Subject: /C=US/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA
220+
Chain Issuer: /C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root CA
221+
Crt filename: /etc/ssl/cert-0.pem
222+
Key filename: /etc/ssl/cert-0.pem
223+
`,
224+
},
225+
},
226+
{
227+
name: "A certificate without a 'Crt filename'",
228+
fields: fields{socketPath: haProxy.Addr().String()},
229+
args: args{
230+
storageName: "cert-1",
231+
},
232+
want: &models.SslCertificate{
233+
StorageName: "cert-1",
234+
Status: "Used",
235+
Serial: "0D933C1B1089BF660AE5253A245BB388",
236+
NotBefore: (*strfmt.DateTime)(&notBefore),
237+
NotAfter: (*strfmt.DateTime)(&notAfter),
238+
SubjectAlternativeNames: "DNS:*.platform.domain.com, DNS:uaa.platform.domain.com",
239+
Algorithm: "RSA4096",
240+
Sha1FingerPrint: "59242F1838BDEF3E7DAFC83FFE4DD6C03B88805C",
241+
Subject: "/C=DE/ST=Baden-Württemberg/L=Walldorf/O=ORG SE/CN=*.platform.domain.com",
242+
Issuers: "/C=US/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA",
243+
ChainSubject: "/C=US/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA",
244+
ChainIssuer: "/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root CA",
245+
},
246+
socketResponse: map[string]string{
247+
"show ssl cert cert-1\n": ` Filename: cert-1
248+
Status: Used
249+
Serial: 0D933C1B1089BF660AE5253A245BB388
250+
notBefore: Sep 9 00:00:00 2020 GMT
251+
notAfter: Sep 14 12:00:00 2021 GMT
252+
Subject Alternative Name: DNS:*.platform.domain.com, DNS:uaa.platform.domain.com
253+
Algorithm: RSA4096
254+
SHA1 FingerPrint: 59242F1838BDEF3E7DAFC83FFE4DD6C03B88805C
255+
Subject: /C=DE/ST=Baden-Württemberg/L=Walldorf/O=ORG SE/CN=*.platform.domain.com
256+
Issuer: /C=US/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA
257+
Chain Subject: /C=US/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA
258+
Chain Issuer: /C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root CA
259+
`,
260+
},
261+
},
262+
{
263+
name: "A certificate with a separate key file",
264+
fields: fields{socketPath: haProxy.Addr().String()},
265+
args: args{
266+
storageName: "cert-2",
267+
},
268+
want: nil,
269+
wantErr: true,
270+
socketResponse: map[string]string{
271+
"show ssl cert cert-2\n": ` Filename: cert-2
210272
Status: Used
211273
Serial: 0D933C1B1089BF660AE5253A245BB388
212274
notBefore: Sep 9 00:00:00 2020 GMT
@@ -218,6 +280,8 @@ func TestSingleRuntime_ShowCertEntry(t *testing.T) {
218280
Issuer: /C=US/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA
219281
Chain Subject: /C=US/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA
220282
Chain Issuer: /C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root CA
283+
Crt filename: /etc/ssl/cert-2.crt
284+
Key filename: /etc/ssl/cert-2.key
221285
`,
222286
},
223287
},

0 commit comments

Comments
 (0)