-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Change storage of PKI entries from colons to hyphens #2575
Changes from 9 commits
dba2de5
c269fe1
4bf51ca
ced4c88
a5ddaab
7fdf4ac
38a01b8
74965a8
8c03765
96bcd50
29e5ce6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -185,32 +185,72 @@ func fetchCAInfo(req *logical.Request) (*caInfoBundle, error) { | |
// separate pathing for CA, CRL, and revoked certificates. | ||
func fetchCertBySerial(req *logical.Request, prefix, serial string) (*logical.StorageEntry, error) { | ||
var path string | ||
var err error | ||
var certEntry *logical.StorageEntry | ||
|
||
hyphenSerial := strings.Replace(strings.ToLower(serial), ":", "-", -1) | ||
colonSerial := strings.Replace(strings.ToLower(serial), "-", ":", -1) | ||
|
||
switch { | ||
// Revoked goes first as otherwise ca/crl get hardcoded paths which fail if | ||
// we actually want revocation info | ||
case strings.HasPrefix(prefix, "revoked/"): | ||
path = "revoked/" + strings.Replace(strings.ToLower(serial), "-", ":", -1) | ||
path = "revoked/" + hyphenSerial | ||
case serial == "ca": | ||
path = "ca" | ||
case serial == "crl": | ||
path = "crl" | ||
default: | ||
path = "certs/" + strings.Replace(strings.ToLower(serial), "-", ":", -1) | ||
path = "certs/" + hyphenSerial | ||
} | ||
|
||
certEntry, err := req.Storage.Get(path) | ||
certEntry, err = req.Storage.Get(path) | ||
if err != nil { | ||
return nil, errutil.InternalError{Err: fmt.Sprintf("error fetching certificate %s: %s", serial, err)} | ||
} | ||
if certEntry == nil { | ||
if certEntry != nil { | ||
if certEntry.Value == nil || len(certEntry.Value) == 0 { | ||
return nil, errutil.InternalError{Err: fmt.Sprintf("returned certificate bytes for serial %s were empty", serial)} | ||
} | ||
return certEntry, nil | ||
} | ||
|
||
// No point checking these, no old/new style colons/hyphens | ||
if path == "ca" || path == "crl" { | ||
return nil, nil | ||
} | ||
|
||
// Save the desired path | ||
desiredPath := path | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This path is no longer needed. You can use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh nevermind. That does not work. That was also one of the reason I wanted to move away from the multiple replacements... Maybe it makes sense to create the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, |
||
|
||
// If we get here we need to check for old-style paths using colons | ||
switch { | ||
case strings.HasPrefix(prefix, "revoked/"): | ||
path = "revoked/" + colonSerial | ||
default: | ||
path = "certs/" + colonSerial | ||
} | ||
|
||
certEntry, err = req.Storage.Get(path) | ||
if err != nil { | ||
return nil, errutil.InternalError{Err: fmt.Sprintf("error fetching certificate %s: %s", serial, err)} | ||
} | ||
if certEntry == nil { | ||
return nil, nil | ||
} | ||
if certEntry.Value == nil || len(certEntry.Value) == 0 { | ||
return nil, errutil.InternalError{Err: fmt.Sprintf("returned certificate bytes for serial %s were empty", serial)} | ||
} | ||
|
||
// Update old-style paths to new-style paths | ||
certEntry.Key = desiredPath | ||
if err = req.Storage.Put(certEntry); err != nil { | ||
return nil, errutil.InternalError{Err: fmt.Sprintf("error saving certificate with serial %s to new location", serial)} | ||
} | ||
if err = req.Storage.Delete(path); err != nil { | ||
return nil, errutil.InternalError{Err: fmt.Sprintf("error deleting certificate with serial %s from old location", serial)} | ||
} | ||
|
||
return certEntry, nil | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
package pki | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"strings" | ||
|
||
"github.com/hashicorp/vault/logical" | ||
) | ||
|
||
func TestPki_FetchCertBySerial(t *testing.T) { | ||
storage := &logical.InmemStorage{} | ||
|
||
cases := map[string]struct { | ||
Req *logical.Request | ||
Prefix string | ||
Serial string | ||
}{ | ||
"valid cert": { | ||
&logical.Request{ | ||
Storage: storage, | ||
}, | ||
"certs/", | ||
"00:00:00:00:00:00:00:00", | ||
}, | ||
"revoked cert": { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we add some cases that do not need the upgrade? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://github.com/hashicorp/vault/pull/2575/files/74965a87af47099b859552fdf2674038228a2c2e#diff-d9cca2f9a1b12f082084eaa700fff503R72 handles the cases for valid/revoked certs where the underlying path is already hyphenated so that there is no need to update the paths. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oops, missed that. |
||
&logical.Request{ | ||
Storage: storage, | ||
}, | ||
"revoked/", | ||
"11:11:11:11:11:11:11:11", | ||
}, | ||
} | ||
|
||
// Test for colon-based paths in storage | ||
for name, tc := range cases { | ||
storageKey := fmt.Sprintf("%s%s", tc.Prefix, tc.Serial) | ||
err := storage.Put(&logical.StorageEntry{ | ||
Key: storageKey, | ||
Value: []byte("some data"), | ||
}) | ||
if err != nil { | ||
t.Fatalf("error writing to storage on %s colon-based storage path: %s", name, err) | ||
} | ||
|
||
certEntry, err := fetchCertBySerial(tc.Req, tc.Prefix, tc.Serial) | ||
if err != nil { | ||
t.Fatalf("error on %s for colon-based storage path: %s", name, err) | ||
} | ||
|
||
// Check for non-nil on valid/revoked certs | ||
if certEntry == nil { | ||
t.Fatalf("nil on %s for colon-based storage path", name) | ||
} | ||
|
||
// Ensure that cert serials are converted/updated after fetch | ||
expectedKey := fmt.Sprintf("%s%s", tc.Prefix, strings.Replace(strings.ToLower(tc.Serial), ":", "-", -1)) | ||
se, err := storage.Get(expectedKey) | ||
if err != nil { | ||
t.Fatalf("error on %s for colon-based storage path:%s", name, err) | ||
} | ||
if strings.Compare(expectedKey, se.Key) != 0 { | ||
t.Fatalf("expected: %s, got: %s", expectedKey, certEntry.Key) | ||
} | ||
} | ||
|
||
// Reset storage | ||
storage = &logical.InmemStorage{} | ||
|
||
// Test for hyphen-base paths in storage | ||
for name, tc := range cases { | ||
storageKey := fmt.Sprintf("%s%s", tc.Prefix, strings.Replace(strings.ToLower(tc.Serial), ":", "-", -1)) | ||
err := storage.Put(&logical.StorageEntry{ | ||
Key: storageKey, | ||
Value: []byte("some data"), | ||
}) | ||
if err != nil { | ||
t.Fatalf("error writing to storage on %s hyphen-based storage path: %s", name, err) | ||
} | ||
|
||
certEntry, err := fetchCertBySerial(tc.Req, tc.Prefix, tc.Serial) | ||
if err != nil || certEntry == nil { | ||
t.Fatalf("error on %s for hyphen-based storage path: err: %v, entry: %v", name, err, certEntry) | ||
} | ||
} | ||
|
||
noConvCases := map[string]struct { | ||
Req *logical.Request | ||
Prefix string | ||
Serial string | ||
}{ | ||
"ca": { | ||
&logical.Request{ | ||
Storage: storage, | ||
}, | ||
"", | ||
"ca", | ||
}, | ||
"crl": { | ||
&logical.Request{ | ||
Storage: storage, | ||
}, | ||
"", | ||
"crl", | ||
}, | ||
} | ||
|
||
// Test for ca and crl case | ||
for name, tc := range noConvCases { | ||
err := storage.Put(&logical.StorageEntry{ | ||
Key: tc.Serial, | ||
Value: []byte("some data"), | ||
}) | ||
if err != nil { | ||
t.Fatalf("error writing to storage on %s: %s", name, err) | ||
} | ||
|
||
certEntry, err := fetchCertBySerial(tc.Req, tc.Prefix, tc.Serial) | ||
if err != nil || certEntry == nil { | ||
t.Fatalf("error on %s: err: %v, entry: %v", name, err, certEntry) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ package pki | |
import ( | ||
"encoding/base64" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/hashicorp/vault/helper/certutil" | ||
"github.com/hashicorp/vault/helper/errutil" | ||
|
@@ -196,7 +197,7 @@ func (b *backend) pathSetSignedIntermediate( | |
return nil, err | ||
} | ||
|
||
entry.Key = "certs/" + cb.SerialNumber | ||
entry.Key = "certs/" + strings.ToLower(strings.Replace(cb.SerialNumber, ":", "-", -1)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we have this creation of |
||
entry.Value = inputBundle.CertificateBytes | ||
err = req.Storage.Put(entry) | ||
if err != nil { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this comment makes sense any more. We should keep the check with
path
for clarity, or update the comment to say why return iflegacyPath
is empty.