fix: parse cert CN and subject on OpenSSL 3.x
The `certs` plugin extracted a certificate's Common Name and formatted its subject using string assumptions that only held for pre-3.x OpenSSL output, so a certificate with only a Common Name and no Subject Alternative Name reported no hostnames from `certs:report` and was not recognized during nginx config generation, while the subject report retained the `subject=` prefix and used the wrong separators. Normalizing the subject with `-nameopt` before parsing makes the extraction version independent across OpenSSL and LibreSSL.
This commit is contained in:
114
plugins/certs/report_test.go
Normal file
114
plugins/certs/report_test.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package certs
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSubjectCommonName(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{"rfc2253 cn only", "subject=CN=dokku.me", "dokku.me"},
|
||||
{"rfc2253 multi rdn reversed", "subject=CN=node-js-app.dokku.me,OU=Operations,O=Expa,L=San Francisco,ST=California,C=US", "node-js-app.dokku.me"},
|
||||
{"rfc2253 wildcard", "subject=CN=*.dokku.me", "*.dokku.me"},
|
||||
{"openssl 3.x default spaced", "subject=CN = cn-only.example.com", "cn-only.example.com"},
|
||||
{"openssl 3.x default multi rdn", "subject=C=US, ST=California, L=San Francisco, O=Expa, OU=Operations, CN=node-js-app.dokku.me", "node-js-app.dokku.me"},
|
||||
{"legacy compat slash prefix", "subject=/CN=dokku.me", "dokku.me"},
|
||||
{"no subject prefix", "CN=dokku.me", "dokku.me"},
|
||||
{"no common name", "subject=OU=Operations,O=Expa,C=US", ""},
|
||||
{"empty", "", ""},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if got := subjectCommonName(tc.in); got != tc.want {
|
||||
t.Errorf("subjectCommonName(%q) = %q, want %q", tc.in, got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const sanCertText = `Certificate:
|
||||
Data:
|
||||
X509v3 extensions:
|
||||
X509v3 Subject Alternative Name:
|
||||
DNS:www.test.dokku.me, DNS:www.test.app.dokku.me
|
||||
`
|
||||
|
||||
func TestSSLHostnames(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
subject string
|
||||
certText string
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "cn only no san",
|
||||
subject: "subject=CN=dokku.me",
|
||||
want: []string{"dokku.me"},
|
||||
},
|
||||
{
|
||||
name: "cn plus sans sorted",
|
||||
subject: "subject=CN=test.dokku.me",
|
||||
certText: sanCertText,
|
||||
want: []string{"test.dokku.me", "www.test.app.dokku.me", "www.test.dokku.me"},
|
||||
},
|
||||
{
|
||||
name: "sans only no cn",
|
||||
subject: "subject=OU=Operations",
|
||||
certText: sanCertText,
|
||||
want: []string{"www.test.app.dokku.me", "www.test.dokku.me"},
|
||||
},
|
||||
{
|
||||
name: "dedupes cn present in san",
|
||||
subject: "subject=CN=www.test.dokku.me",
|
||||
certText: sanCertText,
|
||||
want: []string{"www.test.app.dokku.me", "www.test.dokku.me"},
|
||||
},
|
||||
{
|
||||
name: "no cn no san",
|
||||
subject: "subject=OU=Operations",
|
||||
want: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if got := sslHostnames(tc.subject, tc.certText); !reflect.DeepEqual(got, tc.want) {
|
||||
t.Errorf("sslHostnames(%q, ...) = %#v, want %#v", tc.subject, got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatSSLSubject(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{"compat single", "subject=/CN=dokku.me", "CN=dokku.me"},
|
||||
{
|
||||
"compat multi rdn",
|
||||
"subject=/C=US/ST=California/L=San Francisco/O=Expa/OU=Operations/CN=node-js-app.dokku.me",
|
||||
"C=US; ST=California; L=San Francisco; O=Expa; OU=Operations; CN=node-js-app.dokku.me",
|
||||
},
|
||||
{
|
||||
"compat wildcard multi rdn",
|
||||
"subject=/OU=Domain Control Validated/OU=PositiveSSL Wildcard/CN=*.dokku.me",
|
||||
"OU=Domain Control Validated; OU=PositiveSSL Wildcard; CN=*.dokku.me",
|
||||
},
|
||||
{"legacy spaced prefix", "subject= /CN=dokku.me", "CN=dokku.me"},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if got := formatSSLSubject(tc.in); got != tc.want {
|
||||
t.Errorf("formatSSLSubject(%q) = %q, want %q", tc.in, got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user