mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-02 11:38:02 +00:00
Enforce proper URL in ACME headers (#20357)
See RFC 8555 Section 6.4. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
This commit is contained in:
@@ -389,7 +389,7 @@ func saveAuthorizationAtPath(sc *storageContext, path string, authz *ACMEAuthori
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *acmeState) ParseRequestParams(ac *acmeContext, data *framework.FieldData) (*jwsCtx, map[string]interface{}, error) {
|
func (a *acmeState) ParseRequestParams(ac *acmeContext, req *logical.Request, data *framework.FieldData) (*jwsCtx, map[string]interface{}, error) {
|
||||||
var c jwsCtx
|
var c jwsCtx
|
||||||
var m map[string]interface{}
|
var m map[string]interface{}
|
||||||
|
|
||||||
@@ -415,6 +415,22 @@ func (a *acmeState) ParseRequestParams(ac *acmeContext, data *framework.FieldDat
|
|||||||
return nil, nil, fmt.Errorf("invalid or reused nonce: %w", ErrBadNonce)
|
return nil, nil, fmt.Errorf("invalid or reused nonce: %w", ErrBadNonce)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the path is incorrect, reject the request.
|
||||||
|
//
|
||||||
|
// See RFC 8555 Section 6.4. Request URL Integrity:
|
||||||
|
//
|
||||||
|
// > As noted in Section 6.2, all ACME request objects carry a "url"
|
||||||
|
// > header parameter in their protected header. ... On receiving such
|
||||||
|
// > an object in an HTTP request, the server MUST compare the "url"
|
||||||
|
// > header parameter to the request URL. If the two do not match,
|
||||||
|
// > then the server MUST reject the request as unauthorized.
|
||||||
|
if len(c.Url) == 0 {
|
||||||
|
return nil, nil, fmt.Errorf("missing required parameter 'url' in 'protected': %w", ErrMalformed)
|
||||||
|
}
|
||||||
|
if ac.clusterUrl.JoinPath(req.Path).String() != c.Url {
|
||||||
|
return nil, nil, fmt.Errorf("invalid value for 'url' in 'protected': got '%v' expected '%v': %w", c.Url, ac.clusterUrl.JoinPath(req.Path).String(), ErrUnauthorized)
|
||||||
|
}
|
||||||
|
|
||||||
rawPayloadBase64, ok := data.GetOk("payload")
|
rawPayloadBase64, ok := data.GetOk("payload")
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil, fmt.Errorf("missing required field 'payload': %w", ErrMalformed)
|
return nil, nil, fmt.Errorf("missing required field 'payload': %w", ErrMalformed)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import (
|
|||||||
type acmeContext struct {
|
type acmeContext struct {
|
||||||
// baseUrl is the combination of the configured cluster local URL and the acmePath up to /acme/
|
// baseUrl is the combination of the configured cluster local URL and the acmePath up to /acme/
|
||||||
baseUrl *url.URL
|
baseUrl *url.URL
|
||||||
|
clusterUrl *url.URL
|
||||||
sc *storageContext
|
sc *storageContext
|
||||||
role *roleEntry
|
role *roleEntry
|
||||||
issuer *issuerEntry
|
issuer *issuerEntry
|
||||||
@@ -53,7 +54,7 @@ func (b *backend) acmeWrapper(op acmeOperation) framework.OperationFunc {
|
|||||||
return nil, fmt.Errorf("%w: Can not perform ACME operations until migration has completed", ErrServerInternal)
|
return nil, fmt.Errorf("%w: Can not perform ACME operations until migration has completed", ErrServerInternal)
|
||||||
}
|
}
|
||||||
|
|
||||||
acmeBaseUrl, err := getAcmeBaseUrl(sc, r.Path)
|
acmeBaseUrl, clusterBase, err := getAcmeBaseUrl(sc, r.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -65,6 +66,7 @@ func (b *backend) acmeWrapper(op acmeOperation) framework.OperationFunc {
|
|||||||
|
|
||||||
acmeCtx := &acmeContext{
|
acmeCtx := &acmeContext{
|
||||||
baseUrl: acmeBaseUrl,
|
baseUrl: acmeBaseUrl,
|
||||||
|
clusterUrl: clusterBase,
|
||||||
sc: sc,
|
sc: sc,
|
||||||
role: role,
|
role: role,
|
||||||
issuer: issuer,
|
issuer: issuer,
|
||||||
@@ -144,7 +146,7 @@ func getAcmeRoleAndIssuer(sc *storageContext, data *framework.FieldData) (*roleE
|
|||||||
|
|
||||||
func (b *backend) acmeParsedWrapper(op acmeParsedOperation) framework.OperationFunc {
|
func (b *backend) acmeParsedWrapper(op acmeParsedOperation) framework.OperationFunc {
|
||||||
return b.acmeWrapper(func(acmeCtx *acmeContext, r *logical.Request, fields *framework.FieldData) (*logical.Response, error) {
|
return b.acmeWrapper(func(acmeCtx *acmeContext, r *logical.Request, fields *framework.FieldData) (*logical.Response, error) {
|
||||||
user, data, err := b.acmeState.ParseRequestParams(acmeCtx, fields)
|
user, data, err := b.acmeState.ParseRequestParams(acmeCtx, r, fields)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -256,19 +258,19 @@ func buildAcmeFrameworkPaths(b *backend, patternFunc func(b *backend, pattern st
|
|||||||
return patterns
|
return patterns
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAcmeBaseUrl(sc *storageContext, path string) (*url.URL, error) {
|
func getAcmeBaseUrl(sc *storageContext, path string) (*url.URL, *url.URL, error) {
|
||||||
cfg, err := sc.getClusterConfig()
|
cfg, err := sc.getClusterConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed loading cluster config: %w", err)
|
return nil, nil, fmt.Errorf("failed loading cluster config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Path == "" {
|
if cfg.Path == "" {
|
||||||
return nil, fmt.Errorf("ACME feature requires local cluster path configuration to be set: %w", ErrServerInternal)
|
return nil, nil, fmt.Errorf("ACME feature requires local cluster path configuration to be set: %w", ErrServerInternal)
|
||||||
}
|
}
|
||||||
|
|
||||||
baseUrl, err := url.Parse(cfg.Path)
|
baseUrl, err := url.Parse(cfg.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("ACME feature a proper URL configured in local cluster path: %w", ErrServerInternal)
|
return nil, nil, fmt.Errorf("ACME feature a proper URL configured in local cluster path: %w", ErrServerInternal)
|
||||||
}
|
}
|
||||||
|
|
||||||
directoryPrefix := ""
|
directoryPrefix := ""
|
||||||
@@ -277,5 +279,5 @@ func getAcmeBaseUrl(sc *storageContext, path string) (*url.URL, error) {
|
|||||||
directoryPrefix = path[0:lastIndex]
|
directoryPrefix = path[0:lastIndex]
|
||||||
}
|
}
|
||||||
|
|
||||||
return baseUrl.JoinPath(directoryPrefix, "/acme/"), nil
|
return baseUrl.JoinPath(directoryPrefix, "/acme/"), baseUrl, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user