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:
Alexander Scheel 2023-01-10 09:51:37 -05:00 committed by GitHub
parent 2ab775e60a
commit 38de21468e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 92 additions and 37 deletions

View File

@ -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.

View File

@ -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,
},
}

View File

@ -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
}
}

View File

@ -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,
}

View File

@ -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
}

View File

@ -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://..."
}
```