Add cluster_aia_path templating variable (#18493)
* Add cluster_aia_path templating variable Per discussion with maxb, allow using a non-Vault distribution point which may use an insecure transport for RFC 5280 compliance. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> * Address feedback from Max Co-authored-by: Max Bowsher <maxbowsher@gmail.com> Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com> Co-authored-by: Max Bowsher <maxbowsher@gmail.com>
This commit is contained in:
parent
2ab775e60a
commit
38de21468e
|
@ -5971,13 +5971,14 @@ func TestPKI_TemplatedAIAs(t *testing.T) {
|
|||
|
||||
// Setting templated AIAs should succeed.
|
||||
_, err := CBWrite(b, s, "config/cluster", map[string]interface{}{
|
||||
"path": "http://localhost:8200/v1/pki",
|
||||
"path": "http://localhost:8200/v1/pki",
|
||||
"aia_path": "http://localhost:8200/cdn/pki",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
aiaData := map[string]interface{}{
|
||||
"crl_distribution_points": "{{cluster_path}}/issuer/{{issuer_id}}/crl/der",
|
||||
"issuing_certificates": "{{cluster_path}}/issuer/{{issuer_id}}/der",
|
||||
"issuing_certificates": "{{cluster_aia_path}}/issuer/{{issuer_id}}/der",
|
||||
"ocsp_servers": "{{cluster_path}}/ocsp",
|
||||
"enable_templating": true,
|
||||
}
|
||||
|
@ -6023,7 +6024,7 @@ func TestPKI_TemplatedAIAs(t *testing.T) {
|
|||
// Validate the AIA info is correctly templated.
|
||||
cert := parseCert(t, resp.Data["certificate"].(string))
|
||||
require.Equal(t, cert.OCSPServer, []string{"http://localhost:8200/v1/pki/ocsp"})
|
||||
require.Equal(t, cert.IssuingCertificateURL, []string{"http://localhost:8200/v1/pki/issuer/" + issuerId + "/der"})
|
||||
require.Equal(t, cert.IssuingCertificateURL, []string{"http://localhost:8200/cdn/pki/issuer/" + issuerId + "/der"})
|
||||
require.Equal(t, cert.CRLDistributionPoints, []string{"http://localhost:8200/v1/pki/issuer/" + issuerId + "/crl/der"})
|
||||
|
||||
// Modify our issuer to set custom AIAs: these URLs are bad.
|
||||
|
|
|
@ -26,6 +26,16 @@ including standby nodes, and need not always point to the active node.
|
|||
|
||||
For example: https://pr1.vault.example.com:8200/v1/pki`,
|
||||
},
|
||||
"aia_path": {
|
||||
Type: framework.TypeString,
|
||||
Description: `Optional URI to this mount's AIA distribution
|
||||
point; may refer to an external non-Vault responder. This is for resolving AIA
|
||||
URLs and providing the {{cluster_aia_path}} template parameter and will not
|
||||
be used for other purposes. As such, unlike path above, this could safely
|
||||
be an insecure transit mechanism (like HTTP without TLS).
|
||||
|
||||
For example: http://cdn.example.com/pr1/pki`,
|
||||
},
|
||||
},
|
||||
|
||||
Operations: map[logical.Operation]framework.OperationHandler{
|
||||
|
@ -51,7 +61,8 @@ func (b *backend) pathReadCluster(ctx context.Context, req *logical.Request, _ *
|
|||
|
||||
resp := &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"path": cfg.Path,
|
||||
"path": cfg.Path,
|
||||
"aia_path": cfg.AIAPath,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -65,9 +76,18 @@ func (b *backend) pathWriteCluster(ctx context.Context, req *logical.Request, da
|
|||
return nil, err
|
||||
}
|
||||
|
||||
cfg.Path = data.Get("path").(string)
|
||||
if !govalidator.IsURL(cfg.Path) {
|
||||
return nil, fmt.Errorf("invalid, non-URL path given to cluster: %v", cfg.Path)
|
||||
if value, ok := data.GetOk("path"); ok {
|
||||
cfg.Path = value.(string)
|
||||
if !govalidator.IsURL(cfg.Path) {
|
||||
return nil, fmt.Errorf("invalid, non-URL path given to cluster: %v", cfg.Path)
|
||||
}
|
||||
}
|
||||
|
||||
if value, ok := data.GetOk("aia_path"); ok {
|
||||
cfg.AIAPath = value.(string)
|
||||
if !govalidator.IsURL(cfg.AIAPath) {
|
||||
return nil, fmt.Errorf("invalid, non-URL aia_path given to cluster: %v", cfg.AIAPath)
|
||||
}
|
||||
}
|
||||
|
||||
if err := sc.writeClusterConfig(cfg); err != nil {
|
||||
|
@ -76,7 +96,8 @@ func (b *backend) pathWriteCluster(ctx context.Context, req *logical.Request, da
|
|||
|
||||
resp := &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"path": cfg.Path,
|
||||
"path": cfg.Path,
|
||||
"aia_path": cfg.AIAPath,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -35,10 +35,12 @@ for the OCSP servers attribute. See also RFC 5280 Section 4.2.2.1.`,
|
|||
"enable_templating": {
|
||||
Type: framework.TypeBool,
|
||||
Description: `Whether or not to enabling templating of the
|
||||
above AIA fields. When templating is enabled the special values '{{issuer_id}}'
|
||||
and '{{cluster_path}}' are available, but the addresses are not checked for
|
||||
URI validity until issuance time. This requires /config/cluster's path to be
|
||||
set on all PR Secondary clusters.`,
|
||||
above AIA fields. When templating is enabled the special values '{{issuer_id}}',
|
||||
'{{cluster_path}}', and '{{cluster_aia_path}}' are available, but the addresses
|
||||
are not checked for URI validity until issuance time. Using '{{cluster_path}}'
|
||||
requires /config/cluster's 'path' member to be set on all PR Secondary clusters
|
||||
and using '{{cluster_aia_path}}' requires /config/cluster's 'aia_path' member
|
||||
to be set on all PR secondary clusters.`,
|
||||
Default: false,
|
||||
},
|
||||
},
|
||||
|
@ -59,7 +61,7 @@ set on all PR Secondary clusters.`,
|
|||
|
||||
func validateURLs(urls []string) string {
|
||||
for _, curr := range urls {
|
||||
if !govalidator.IsURL(curr) || strings.Contains(curr, "{{issuer_id}}") || strings.Contains(curr, "{{cluster_path}}") {
|
||||
if !govalidator.IsURL(curr) || strings.Contains(curr, "{{issuer_id}}") || strings.Contains(curr, "{{cluster_path}}") || strings.Contains(curr, "{{cluster_aia_path}}") {
|
||||
return curr
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,10 +137,12 @@ for the OCSP servers attribute. See also RFC 5280 Section 4.2.2.1.`,
|
|||
fields["enable_aia_url_templating"] = &framework.FieldSchema{
|
||||
Type: framework.TypeBool,
|
||||
Description: `Whether or not to enabling templating of the
|
||||
above AIA fields. When templating is enabled the special values '{{issuer_id}}'
|
||||
and '{{cluster_path}}' are available, but the addresses are not checked for
|
||||
URL validity until issuance time. This requires /config/cluster's path to be
|
||||
set on all PR Secondary clusters.`,
|
||||
above AIA fields. When templating is enabled the special values '{{issuer_id}}',
|
||||
'{{cluster_path}}', '{{cluster_aia_path}}' are available, but the addresses are
|
||||
not checked for URL validity until issuance time. Using '{{cluster_path}}'
|
||||
requires /config/cluster's 'path' member to be set on all PR Secondary clusters
|
||||
and using '{{cluster_aia_path}}' requires /config/cluster's 'aia_path' member
|
||||
to be set on all PR secondary clusters.`,
|
||||
Default: false,
|
||||
}
|
||||
|
||||
|
|
|
@ -197,7 +197,8 @@ type issuerConfigEntry struct {
|
|||
}
|
||||
|
||||
type clusterConfigEntry struct {
|
||||
Path string `json:"path"`
|
||||
Path string `json:"path"`
|
||||
AIAPath string `json:"aia_path"`
|
||||
}
|
||||
|
||||
type aiaConfigEntry struct {
|
||||
|
@ -232,15 +233,18 @@ func (c *aiaConfigEntry) toURLEntries(sc *storageContext, issuer issuerID) (*cer
|
|||
templated := make([]string, len(*source))
|
||||
for index, uri := range *source {
|
||||
if strings.Contains(uri, "{{cluster_path}}") && len(cfg.Path) == 0 {
|
||||
return nil, fmt.Errorf("unable to template AIA URLs as we lack local cluster address information")
|
||||
return nil, fmt.Errorf("unable to template AIA URLs as we lack local cluster address information (path)")
|
||||
}
|
||||
if strings.Contains(uri, "{{cluster_aia_path}}") && len(cfg.AIAPath) == 0 {
|
||||
return nil, fmt.Errorf("unable to template AIA URLs as we lack local cluster address information (aia_path)")
|
||||
}
|
||||
|
||||
if strings.Contains(uri, "{{issuer_id}}") && len(issuer) == 0 {
|
||||
// Elide issuer AIA info as we lack an issuer_id.
|
||||
return nil, fmt.Errorf("unable to template AIA URLs as we lack an issuer_id for this operation")
|
||||
}
|
||||
|
||||
uri = strings.ReplaceAll(uri, "{{cluster_path}}", cfg.Path)
|
||||
uri = strings.ReplaceAll(uri, "{{cluster_aia_path}}", cfg.AIAPath)
|
||||
uri = strings.ReplaceAll(uri, "{{issuer_id}}", issuer.String())
|
||||
templated[index] = uri
|
||||
}
|
||||
|
|
|
@ -2164,9 +2164,11 @@ do so, import a new issuer and a new `issuer_id` will be assigned.
|
|||
- `enable_aia_url_templating` `(bool: false)` - Specifies that the above AIA
|
||||
URL values (`issuing_certificates`, `crl_distribution_points`, and
|
||||
`ocsp_servers`) should be templated. This replaces the literal value
|
||||
`{{issuer_id}}` with the ID of the issuer doing the issuance, and the
|
||||
`{{issuer_id}}` with the ID of the issuer doing the issuance, the
|
||||
literal value `{{cluster_path}}` with the value of `path` from the
|
||||
cluster-local configuration endpoint `/config/cluster`.
|
||||
cluster-local configuration endpoint `/config/cluster`, and the
|
||||
literal value '{{cluster_aia_path}}' with the value of `aia_path` from
|
||||
the cluster-local configuration endpoint `/config/cluster`.
|
||||
|
||||
~> **Note**: If no cluster-local address is present and templating is used,
|
||||
issuance will fail.
|
||||
|
@ -2941,18 +2943,22 @@ parameter.
|
|||
[RFC 5280 Section 4.2.2.1](https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.2.1)
|
||||
for information about the Authority Information Access field.
|
||||
|
||||
- `enable_templating` `(bool: false)` - Specifies that the above AIA
|
||||
- `enable_aia_url_templating` `(bool: false)` - Specifies that the above AIA
|
||||
URL values (`issuing_certificates`, `crl_distribution_points`, and
|
||||
`ocsp_servers`) should be templated. This replaces the literal value
|
||||
`{{issuer_id}}` with the ID of the issuer doing the issuance, and the
|
||||
`{{issuer_id}}` with the ID of the issuer doing the issuance, the
|
||||
literal value `{{cluster_path}}` with the value of `path` from the
|
||||
cluster-local configuration endpoint `/config/cluster`.
|
||||
cluster-local configuration endpoint `/config/cluster`, and the
|
||||
literal value '{{cluster_aia_path}}' with the value of `aia_path` from
|
||||
the cluster-local configuration endpoint `/config/cluster`.
|
||||
|
||||
For example, the following values can be used globally to ensure all AIA
|
||||
URIs use the cluster-local, per-issuer canonical reference:
|
||||
URIs use the cluster-local, per-issuer canonical reference, but with
|
||||
the issuing CA certificate and CRL distribution points to potentially
|
||||
use an external, non-Vault CDN.
|
||||
|
||||
- `issuing_certificates={{cluster_path}}/issuer/{{issuer_id}}/der`
|
||||
- `crl_distribution_points={{cluster_path}}/issuer/{{issuer_id}}/crl/der`
|
||||
- `issuing_certificates={{cluster_aia_path}}/issuer/{{issuer_id}}/der`
|
||||
- `crl_distribution_points={{cluster_aia_path}}/issuer/{{issuer_id}}/crl/der`
|
||||
- `ocsp_servers={{cluster_path}}/ocsp`
|
||||
|
||||
~> **Note**: If no cluster-local address is present and templating is used,
|
||||
|
@ -3138,10 +3144,15 @@ $ curl \
|
|||
|
||||
This endpoint fetches the cluster-local configuration.
|
||||
|
||||
Presently the only cluster-local config option is `path`, which sets the URL
|
||||
to this mount on a particular performance replication cluster. This is useful
|
||||
for populating `{{cluster_path}}` for AIA URL templating, but may be used for
|
||||
other uses in the future.
|
||||
The cluster-local config has `path`, which sets the URL to this mount on
|
||||
a particular performance replication cluster. This is useful for populating
|
||||
`{{cluster_path}}` during AIA URL templating, but may be used for other
|
||||
values in the future.
|
||||
|
||||
It also has `aia_path`, which allows using a non-Vault, external responder,
|
||||
setting the `{{cluster_aia_path}}` value for AIA URL templating. This is
|
||||
useful for distributing CA and CRL information over an unsecured, non-TLS
|
||||
channel.
|
||||
|
||||
| Method | Path |
|
||||
| :----- | :-------------------- |
|
||||
|
@ -3163,7 +3174,8 @@ $ curl \
|
|||
"renewable": false,
|
||||
"lease_duration": 0,
|
||||
"data": {
|
||||
"path": "<url>"
|
||||
"path": "<url>",
|
||||
"aia_path": "<url>"
|
||||
},
|
||||
"auth": null
|
||||
}
|
||||
|
@ -3173,9 +3185,15 @@ $ curl \
|
|||
|
||||
This endpoint sets cluster-local configuration.
|
||||
|
||||
Presently the only cluster-local config option is `path`, which sets the URL
|
||||
to this mount on a particular performance replication cluster. This is useful
|
||||
for AIA URL templating.
|
||||
The cluster-local config has `path`, which sets the URL to this mount on
|
||||
a particular performance replication cluster. This is useful for populating
|
||||
`{{cluster_path}}` during AIA URL templating, but may be used for other
|
||||
values in the future.
|
||||
|
||||
It also has `aia_path`, which allows using a non-Vault, external responder,
|
||||
setting the `{{cluster_aia_path}}` value for AIA URL templating. This is
|
||||
useful for distributing CA and CRL information over an unsecured, non-TLS
|
||||
channel.
|
||||
|
||||
| Method | Path |
|
||||
| :----- | :-------------------- |
|
||||
|
@ -3187,11 +3205,18 @@ for AIA URL templating.
|
|||
cluster's API mount path, including any namespaces as path components.
|
||||
For example, `https://pr-a.vault.example.com/v1/ns1/pki-root`.
|
||||
|
||||
- `aia_path` `(string: "")` - Specifies the path to this performance replication
|
||||
cluster's AIA distribution point; may refer to an external, non-Vault responder.
|
||||
This is for resolving AIA URLs and providing the `{{cluster_aia_path}}` template
|
||||
parameter and will not be used for other purposes. As such, unlike `path` above,
|
||||
this could safely be an insecure transit mechanism (like HTTP without TLS).
|
||||
|
||||
#### Sample Payload
|
||||
|
||||
```json
|
||||
{
|
||||
"path": ["https://..."]
|
||||
"path": "https://...",
|
||||
"aia_path": "http://..."
|
||||
}
|
||||
```
|
||||
|
||||
|
|
Loading…
Reference in New Issue