Skip to content

Commit 4deceff

Browse files
oliwerGopher Bot
authored andcommitted
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 99a8d14 commit 4deceff

File tree

2 files changed

+92
-3
lines changed

2 files changed

+92
-3
lines changed

runtime/certs.go

Lines changed: 26 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"
@@ -65,7 +66,7 @@ func (s *SingleRuntime) parseCert(line string) *models.SslCertificate {
6566

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

8690
c := &models.SslCertificate{}
91+
var crtFilename string
92+
var keyFilename string
93+
8794
strings.SplitSeq(response, "\n")(func(line string) bool {
8895
key, val, found := strings.Cut(line, ": ")
8996
if !found {
@@ -116,10 +123,28 @@ func parseCertEntry(response string) (*models.SslCertificate, error) {
116123
c.ChainSubject = val
117124
case "Chain Issuer":
118125
c.ChainIssuer = val
126+
case "Crt filename":
127+
crtFilename = val
128+
case "Key filename":
129+
keyFilename = val
119130
}
120131
return true
121132
})
122133

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+
123148
return c, nil
124149
}
125150

runtime/certs_test.go

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

0 commit comments

Comments
 (0)