mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-03 03:58:01 +00:00
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:
@@ -5971,13 +5971,14 @@ func TestPKI_TemplatedAIAs(t *testing.T) {
|
|||||||
|
|
||||||
// Setting templated AIAs should succeed.
|
// Setting templated AIAs should succeed.
|
||||||
_, err := CBWrite(b, s, "config/cluster", map[string]interface{}{
|
_, 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)
|
require.NoError(t, err)
|
||||||
|
|
||||||
aiaData := map[string]interface{}{
|
aiaData := map[string]interface{}{
|
||||||
"crl_distribution_points": "{{cluster_path}}/issuer/{{issuer_id}}/crl/der",
|
"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",
|
"ocsp_servers": "{{cluster_path}}/ocsp",
|
||||||
"enable_templating": true,
|
"enable_templating": true,
|
||||||
}
|
}
|
||||||
@@ -6023,7 +6024,7 @@ func TestPKI_TemplatedAIAs(t *testing.T) {
|
|||||||
// Validate the AIA info is correctly templated.
|
// Validate the AIA info is correctly templated.
|
||||||
cert := parseCert(t, resp.Data["certificate"].(string))
|
cert := parseCert(t, resp.Data["certificate"].(string))
|
||||||
require.Equal(t, cert.OCSPServer, []string{"http://localhost:8200/v1/pki/ocsp"})
|
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"})
|
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.
|
// 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`,
|
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{
|
Operations: map[logical.Operation]framework.OperationHandler{
|
||||||
@@ -51,7 +61,8 @@ func (b *backend) pathReadCluster(ctx context.Context, req *logical.Request, _ *
|
|||||||
|
|
||||||
resp := &logical.Response{
|
resp := &logical.Response{
|
||||||
Data: map[string]interface{}{
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.Path = data.Get("path").(string)
|
if value, ok := data.GetOk("path"); ok {
|
||||||
if !govalidator.IsURL(cfg.Path) {
|
cfg.Path = value.(string)
|
||||||
return nil, fmt.Errorf("invalid, non-URL path given to cluster: %v", cfg.Path)
|
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 {
|
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{
|
resp := &logical.Response{
|
||||||
Data: map[string]interface{}{
|
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": {
|
"enable_templating": {
|
||||||
Type: framework.TypeBool,
|
Type: framework.TypeBool,
|
||||||
Description: `Whether or not to enabling templating of the
|
Description: `Whether or not to enabling templating of the
|
||||||
above AIA fields. When templating is enabled the special values '{{issuer_id}}'
|
above AIA fields. When templating is enabled the special values '{{issuer_id}}',
|
||||||
and '{{cluster_path}}' are available, but the addresses are not checked for
|
'{{cluster_path}}', and '{{cluster_aia_path}}' are available, but the addresses
|
||||||
URI validity until issuance time. This requires /config/cluster's path to be
|
are not checked for URI validity until issuance time. Using '{{cluster_path}}'
|
||||||
set on all PR Secondary clusters.`,
|
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,
|
Default: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -59,7 +61,7 @@ set on all PR Secondary clusters.`,
|
|||||||
|
|
||||||
func validateURLs(urls []string) string {
|
func validateURLs(urls []string) string {
|
||||||
for _, curr := range urls {
|
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
|
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{
|
fields["enable_aia_url_templating"] = &framework.FieldSchema{
|
||||||
Type: framework.TypeBool,
|
Type: framework.TypeBool,
|
||||||
Description: `Whether or not to enabling templating of the
|
Description: `Whether or not to enabling templating of the
|
||||||
above AIA fields. When templating is enabled the special values '{{issuer_id}}'
|
above AIA fields. When templating is enabled the special values '{{issuer_id}}',
|
||||||
and '{{cluster_path}}' are available, but the addresses are not checked for
|
'{{cluster_path}}', '{{cluster_aia_path}}' are available, but the addresses are
|
||||||
URL validity until issuance time. This requires /config/cluster's path to be
|
not checked for URL validity until issuance time. Using '{{cluster_path}}'
|
||||||
set on all PR Secondary clusters.`,
|
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,
|
Default: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -197,7 +197,8 @@ type issuerConfigEntry struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type clusterConfigEntry struct {
|
type clusterConfigEntry struct {
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
|
AIAPath string `json:"aia_path"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type aiaConfigEntry struct {
|
type aiaConfigEntry struct {
|
||||||
@@ -232,15 +233,18 @@ func (c *aiaConfigEntry) toURLEntries(sc *storageContext, issuer issuerID) (*cer
|
|||||||
templated := make([]string, len(*source))
|
templated := make([]string, len(*source))
|
||||||
for index, uri := range *source {
|
for index, uri := range *source {
|
||||||
if strings.Contains(uri, "{{cluster_path}}") && len(cfg.Path) == 0 {
|
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 {
|
if strings.Contains(uri, "{{issuer_id}}") && len(issuer) == 0 {
|
||||||
// Elide issuer AIA info as we lack an issuer_id.
|
// 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")
|
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_path}}", cfg.Path)
|
||||||
|
uri = strings.ReplaceAll(uri, "{{cluster_aia_path}}", cfg.AIAPath)
|
||||||
uri = strings.ReplaceAll(uri, "{{issuer_id}}", issuer.String())
|
uri = strings.ReplaceAll(uri, "{{issuer_id}}", issuer.String())
|
||||||
templated[index] = uri
|
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
|
- `enable_aia_url_templating` `(bool: false)` - Specifies that the above AIA
|
||||||
URL values (`issuing_certificates`, `crl_distribution_points`, and
|
URL values (`issuing_certificates`, `crl_distribution_points`, and
|
||||||
`ocsp_servers`) should be templated. This replaces the literal value
|
`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
|
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,
|
~> **Note**: If no cluster-local address is present and templating is used,
|
||||||
issuance will fail.
|
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)
|
[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.
|
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
|
URL values (`issuing_certificates`, `crl_distribution_points`, and
|
||||||
`ocsp_servers`) should be templated. This replaces the literal value
|
`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
|
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
|
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`
|
- `issuing_certificates={{cluster_aia_path}}/issuer/{{issuer_id}}/der`
|
||||||
- `crl_distribution_points={{cluster_path}}/issuer/{{issuer_id}}/crl/der`
|
- `crl_distribution_points={{cluster_aia_path}}/issuer/{{issuer_id}}/crl/der`
|
||||||
- `ocsp_servers={{cluster_path}}/ocsp`
|
- `ocsp_servers={{cluster_path}}/ocsp`
|
||||||
|
|
||||||
~> **Note**: If no cluster-local address is present and templating is used,
|
~> **Note**: If no cluster-local address is present and templating is used,
|
||||||
@@ -3138,10 +3144,15 @@ $ curl \
|
|||||||
|
|
||||||
This endpoint fetches the cluster-local configuration.
|
This endpoint fetches the cluster-local configuration.
|
||||||
|
|
||||||
Presently the only cluster-local config option is `path`, which sets the URL
|
The cluster-local config has `path`, which sets the URL to this mount on
|
||||||
to this mount on a particular performance replication cluster. This is useful
|
a particular performance replication cluster. This is useful for populating
|
||||||
for populating `{{cluster_path}}` for AIA URL templating, but may be used for
|
`{{cluster_path}}` during AIA URL templating, but may be used for other
|
||||||
other uses in the future.
|
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 |
|
| Method | Path |
|
||||||
| :----- | :-------------------- |
|
| :----- | :-------------------- |
|
||||||
@@ -3163,7 +3174,8 @@ $ curl \
|
|||||||
"renewable": false,
|
"renewable": false,
|
||||||
"lease_duration": 0,
|
"lease_duration": 0,
|
||||||
"data": {
|
"data": {
|
||||||
"path": "<url>"
|
"path": "<url>",
|
||||||
|
"aia_path": "<url>"
|
||||||
},
|
},
|
||||||
"auth": null
|
"auth": null
|
||||||
}
|
}
|
||||||
@@ -3173,9 +3185,15 @@ $ curl \
|
|||||||
|
|
||||||
This endpoint sets cluster-local configuration.
|
This endpoint sets cluster-local configuration.
|
||||||
|
|
||||||
Presently the only cluster-local config option is `path`, which sets the URL
|
The cluster-local config has `path`, which sets the URL to this mount on
|
||||||
to this mount on a particular performance replication cluster. This is useful
|
a particular performance replication cluster. This is useful for populating
|
||||||
for AIA URL templating.
|
`{{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 |
|
| Method | Path |
|
||||||
| :----- | :-------------------- |
|
| :----- | :-------------------- |
|
||||||
@@ -3187,11 +3205,18 @@ for AIA URL templating.
|
|||||||
cluster's API mount path, including any namespaces as path components.
|
cluster's API mount path, including any namespaces as path components.
|
||||||
For example, `https://pr-a.vault.example.com/v1/ns1/pki-root`.
|
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
|
#### Sample Payload
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"path": ["https://..."]
|
"path": "https://...",
|
||||||
|
"aia_path": "http://..."
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user