Skip to content

Commit 6a737c8

Browse files
authored
Merge pull request #4613 from hashicorp/b-cert-whitespace
config: accept CA PEM files with extra whitespace
2 parents 6bd5852 + 556adad commit 6a737c8

File tree

4 files changed

+115
-46
lines changed

4 files changed

+115
-46
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ IMPROVEMENTS:
1515

1616

1717
BUG FIXES:
18+
* core: Fix treating valid PEM files as invalid [[GH-4613](https://github.com/hashicorp/nomad/issues/4613)]
1819
* core: Reset queued allocation summary to zero when job stopped [[GH-4414](https://github.com/hashicorp/nomad/issues/4414)]
1920
* core: Fix panic due to missing synchronization in delayed evaluations heap [[GH-4632](https://github.com/hashicorp/nomad/issues/4632)]
2021
* client: Fix migrating ephemeral disks when TLS is enabled [[GH-4648](https://github.com/hashicorp/nomad/issues/4648)]

helper/tlsutil/config.go

+4-29
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"crypto/rsa"
66
"crypto/tls"
77
"crypto/x509"
8-
"encoding/pem"
98
"fmt"
109
"io/ioutil"
1110
"net"
@@ -194,35 +193,11 @@ func (c *Config) AppendCA(pool *x509.CertPool) error {
194193
return fmt.Errorf("Failed to read CA file: %v", err)
195194
}
196195

197-
block, rest := pem.Decode(data)
198-
if err := validateCertificate(block); err != nil {
199-
return err
200-
}
201-
202-
for len(rest) > 0 {
203-
block, rest = pem.Decode(rest)
204-
if err := validateCertificate(block); err != nil {
205-
return err
206-
}
207-
}
208-
196+
// Read certificates and return an error if no valid certificates were
197+
// found. Unfortunately it is very difficult to return meaningful
198+
// errors as PEM files are extremely permissive.
209199
if !pool.AppendCertsFromPEM(data) {
210-
return fmt.Errorf("Failed to add any CA certificates")
211-
}
212-
213-
return nil
214-
}
215-
216-
// validateCertificate checks to ensure a certificate is valid. If it is not,
217-
// return a descriptive error of why the certificate is invalid.
218-
func validateCertificate(block *pem.Block) error {
219-
if block == nil {
220-
return fmt.Errorf("Failed to decode CA file from pem format")
221-
}
222-
223-
// Parse the certificate to ensure that it is properly formatted
224-
if _, err := x509.ParseCertificates(block.Bytes); err != nil {
225-
return fmt.Errorf("Failed to parse CA file: %v", err)
200+
return fmt.Errorf("Failed to parse any valid certificates in CA file: %s", c.CAFile)
226201
}
227202

228203
return nil

helper/tlsutil/config_test.go

+95-17
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,6 @@ func TestConfig_AppendCA_Valid(t *testing.T) {
5151
func TestConfig_AppendCA_Valid_MultipleCerts(t *testing.T) {
5252
require := require.New(t)
5353

54-
tmpCAFile, err := ioutil.TempFile("/tmp", "test_ca_file")
55-
require.Nil(err)
56-
defer os.Remove(tmpCAFile.Name())
57-
5854
certs := `
5955
-----BEGIN CERTIFICATE-----
6056
MIICMzCCAdqgAwIBAgIUNZ9L86Xp9EuDH0/qyAesh599LXQwCgYIKoZIzj0EAwIw
@@ -71,6 +67,61 @@ AQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUOjVq/BectnhcKn6EHUD4NJFm
7167
Ln2ZUe8CIDsQswBQS7URbqnKYDye2Y4befJkr4fmhhmMQb2ex9A4
7268
-----END CERTIFICATE-----
7369
-----BEGIN CERTIFICATE-----
70+
MIICNTCCAZagAwIBAgIRANjgoh5iVZI26+Hz/K65G0UwCgYIKoZIzj0EAwQwNjEb
71+
MBkGA1UEChMSSGFzaGlDb3JwIFRyYWluaW5nMRcwFQYDVQQDEw5zZXJ2aWNlLmNv
72+
bnN1bDAeFw0xODA4MjMxNzM0NTBaFw0xODA5MjIxNzM0NTBaMDYxGzAZBgNVBAoT
73+
Ekhhc2hpQ29ycCBUcmFpbmluZzEXMBUGA1UEAxMOc2VydmljZS5jb25zdWwwgZsw
74+
EAYHKoZIzj0CAQYFK4EEACMDgYYABAGjC4sWsOfirS/DQ9/e7PdQeJwlOjziiOx/
75+
CALjS6ryEDkZPqRqMuoFXfudAmfdk6tl8AT1IKMVcgiQU5jkm7fliwFIk48uh+n2
76+
obqZjwDyM76VYBVSYi6i3BPXown1ivIMJNQS1txnWZLZHsv+WxbHydS+GNOAwKDK
77+
KsXj9dEhd36pvaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w
78+
HQYDVR0OBBYEFIk3oG2hu0FxueW4e7fL+FdMOquBMAoGCCqGSM49BAMEA4GMADCB
79+
iAJCAPIPwPyk+8Ymj7Zlvb5qIUQg+UxoacAeJtFZrJ8xQjro0YjsM33O86rAfw+x
80+
sWWGul4Ews93KFBXvhbKCwb0F0PhAkIAh2z7COsKcQzvBoIy+Kx92+9j/sUjlzzl
81+
TttDu+g2VdbcBwVDZ49X2Md6OY2N3G8Irdlj+n+mCQJaHwVt52DRzz0=
82+
-----END CERTIFICATE-----
83+
`
84+
85+
tmpCAFile, err := ioutil.TempFile("/tmp", "test_ca_file")
86+
require.NoError(err)
87+
defer os.Remove(tmpCAFile.Name())
88+
89+
_, err = tmpCAFile.Write([]byte(certs))
90+
require.NoError(err)
91+
tmpCAFile.Close()
92+
93+
conf := &Config{
94+
CAFile: tmpCAFile.Name(),
95+
}
96+
pool := x509.NewCertPool()
97+
require.NoError(conf.AppendCA(pool))
98+
99+
require.Len(pool.Subjects(), 2)
100+
}
101+
102+
// TestConfig_AppendCA_Valid_Whitespace asserts that a PEM file containing
103+
// trailing whitespace is valid.
104+
func TestConfig_AppendCA_Valid_Whitespace(t *testing.T) {
105+
require := require.New(t)
106+
107+
const cacertWhitespace = "./testdata/ca-whitespace.pem"
108+
conf := &Config{
109+
CAFile: cacertWhitespace,
110+
}
111+
pool := x509.NewCertPool()
112+
require.NoError(conf.AppendCA(pool))
113+
114+
require.Len(pool.Subjects(), 1)
115+
}
116+
117+
// TestConfig_AppendCA_Invalid_MultipleCerts_Whitespace asserts that a PEM file
118+
// containing non-PEM data between certificate blocks is still valid.
119+
func TestConfig_AppendCA_Valid_MultipleCerts_ExtraData(t *testing.T) {
120+
require := require.New(t)
121+
122+
certs := `
123+
Did you know...
124+
-----BEGIN CERTIFICATE-----
74125
MIICMzCCAdqgAwIBAgIUNZ9L86Xp9EuDH0/qyAesh599LXQwCgYIKoZIzj0EAwIw
75126
eDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh
76127
biBGcmFuY2lzY28xEjAQBgNVBAoTCUhhc2hpQ29ycDEOMAwGA1UECxMFTm9tYWQx
@@ -83,27 +134,50 @@ uNdZJZWSi4Q/4HojM5FTSBqYxNgSrmY/o3oQrCPlo0IwQDAOBgNVHQ8BAf8EBAMC
83134
AQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUOjVq/BectnhcKn6EHUD4NJFm
84135
/UAwCgYIKoZIzj0EAwIDRwAwRAIgTemDJGSGtcQPXLWKiQNw4SKO9wAPhn/WoKW4
85136
Ln2ZUe8CIDsQswBQS7URbqnKYDye2Y4befJkr4fmhhmMQb2ex9A4
86-
-----END CERTIFICATE-----`
137+
-----END CERTIFICATE-----
138+
139+
...PEM parsers don't care about data...
140+
141+
-----BEGIN CERTIFICATE-----
142+
MIICNTCCAZagAwIBAgIRANjgoh5iVZI26+Hz/K65G0UwCgYIKoZIzj0EAwQwNjEb
143+
MBkGA1UEChMSSGFzaGlDb3JwIFRyYWluaW5nMRcwFQYDVQQDEw5zZXJ2aWNlLmNv
144+
bnN1bDAeFw0xODA4MjMxNzM0NTBaFw0xODA5MjIxNzM0NTBaMDYxGzAZBgNVBAoT
145+
Ekhhc2hpQ29ycCBUcmFpbmluZzEXMBUGA1UEAxMOc2VydmljZS5jb25zdWwwgZsw
146+
EAYHKoZIzj0CAQYFK4EEACMDgYYABAGjC4sWsOfirS/DQ9/e7PdQeJwlOjziiOx/
147+
CALjS6ryEDkZPqRqMuoFXfudAmfdk6tl8AT1IKMVcgiQU5jkm7fliwFIk48uh+n2
148+
obqZjwDyM76VYBVSYi6i3BPXown1ivIMJNQS1txnWZLZHsv+WxbHydS+GNOAwKDK
149+
KsXj9dEhd36pvaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w
150+
HQYDVR0OBBYEFIk3oG2hu0FxueW4e7fL+FdMOquBMAoGCCqGSM49BAMEA4GMADCB
151+
iAJCAPIPwPyk+8Ymj7Zlvb5qIUQg+UxoacAeJtFZrJ8xQjro0YjsM33O86rAfw+x
152+
sWWGul4Ews93KFBXvhbKCwb0F0PhAkIAh2z7COsKcQzvBoIy+Kx92+9j/sUjlzzl
153+
TttDu+g2VdbcBwVDZ49X2Md6OY2N3G8Irdlj+n+mCQJaHwVt52DRzz0=
154+
-----END CERTIFICATE-----
155+
156+
...outside of -----XXX----- blocks?
157+
`
87158

159+
tmpCAFile, err := ioutil.TempFile("/tmp", "test_ca_file_extra")
160+
require.NoError(err)
161+
defer os.Remove(tmpCAFile.Name())
88162
_, err = tmpCAFile.Write([]byte(certs))
89-
require.Nil(err)
163+
require.NoError(err)
164+
tmpCAFile.Close()
90165

91166
conf := &Config{
92167
CAFile: tmpCAFile.Name(),
93168
}
94169
pool := x509.NewCertPool()
95170
err = conf.AppendCA(pool)
96171

97-
require.Nil(err)
172+
require.NoError(err)
173+
require.Len(pool.Subjects(), 2)
98174
}
99175

100-
func TestConfig_AppendCA_InValid_MultipleCerts(t *testing.T) {
176+
// TestConfig_AppendCA_Invalid_MultipleCerts asserts only the valid certificate
177+
// is returned.
178+
func TestConfig_AppendCA_Invalid_MultipleCerts(t *testing.T) {
101179
require := require.New(t)
102180

103-
tmpCAFile, err := ioutil.TempFile("/tmp", "test_ca_file")
104-
require.Nil(err)
105-
defer os.Remove(tmpCAFile.Name())
106-
107181
certs := `
108182
-----BEGIN CERTIFICATE-----
109183
MIICMzCCAdqgAwIBAgIUNZ9L86Xp9EuDH0/qyAesh599LXQwCgYIKoZIzj0EAwIw
@@ -123,16 +197,20 @@ Ln2ZUe8CIDsQswBQS7URbqnKYDye2Y4befJkr4fmhhmMQb2ex9A4
123197
Invalid
124198
-----END CERTIFICATE-----`
125199

200+
tmpCAFile, err := ioutil.TempFile("/tmp", "test_ca_file")
201+
require.NoError(err)
202+
defer os.Remove(tmpCAFile.Name())
126203
_, err = tmpCAFile.Write([]byte(certs))
127-
require.Nil(err)
204+
require.NoError(err)
205+
tmpCAFile.Close()
128206

129207
conf := &Config{
130208
CAFile: tmpCAFile.Name(),
131209
}
132210
pool := x509.NewCertPool()
133-
err = conf.AppendCA(pool)
211+
require.NoError(conf.AppendCA(pool))
134212

135-
require.NotNil(err)
213+
require.Len(pool.Subjects(), 1)
136214
}
137215

138216
func TestConfig_AppendCA_Invalid(t *testing.T) {
@@ -160,8 +238,8 @@ func TestConfig_AppendCA_Invalid(t *testing.T) {
160238
}
161239
pool := x509.NewCertPool()
162240
err = conf.AppendCA(pool)
163-
require.NotNil(err)
164-
require.Contains(err.Error(), "Failed to decode CA file from pem format")
241+
require.Error(err)
242+
require.Contains(err.Error(), "Failed to parse any valid certificates in CA file:")
165243
require.Equal(len(pool.Subjects()), 0)
166244
}
167245
}
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIICNTCCAZagAwIBAgIRANjgoh5iVZI26+Hz/K65G0UwCgYIKoZIzj0EAwQwNjEb
3+
MBkGA1UEChMSSGFzaGlDb3JwIFRyYWluaW5nMRcwFQYDVQQDEw5zZXJ2aWNlLmNv
4+
bnN1bDAeFw0xODA4MjMxNzM0NTBaFw0xODA5MjIxNzM0NTBaMDYxGzAZBgNVBAoT
5+
Ekhhc2hpQ29ycCBUcmFpbmluZzEXMBUGA1UEAxMOc2VydmljZS5jb25zdWwwgZsw
6+
EAYHKoZIzj0CAQYFK4EEACMDgYYABAGjC4sWsOfirS/DQ9/e7PdQeJwlOjziiOx/
7+
CALjS6ryEDkZPqRqMuoFXfudAmfdk6tl8AT1IKMVcgiQU5jkm7fliwFIk48uh+n2
8+
obqZjwDyM76VYBVSYi6i3BPXown1ivIMJNQS1txnWZLZHsv+WxbHydS+GNOAwKDK
9+
KsXj9dEhd36pvaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w
10+
HQYDVR0OBBYEFIk3oG2hu0FxueW4e7fL+FdMOquBMAoGCCqGSM49BAMEA4GMADCB
11+
iAJCAPIPwPyk+8Ymj7Zlvb5qIUQg+UxoacAeJtFZrJ8xQjro0YjsM33O86rAfw+x
12+
sWWGul4Ews93KFBXvhbKCwb0F0PhAkIAh2z7COsKcQzvBoIy+Kx92+9j/sUjlzzl
13+
TttDu+g2VdbcBwVDZ49X2Md6OY2N3G8Irdlj+n+mCQJaHwVt52DRzz0=
14+
-----END CERTIFICATE-----
15+

0 commit comments

Comments
 (0)