Concourse var_sources and related schema definitions

This commit is contained in:
aboyko
2023-01-17 20:15:27 -05:00
parent 3447adfc9e
commit 1812584fb6
29 changed files with 294 additions and 2 deletions

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* Copyright (c) 2016, 2023 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -502,6 +502,63 @@ public class PipelineYmlSchema implements YamlSchema {
YType t_group_name_def= f.yatomic("Group Name")
.parseWith(ValueParsers.NE_STRING);
/**
* VAR_SOURCE definition
*/
YBeanType t_var_source_type = f.ybean("VarSource");
addProp(t_var_source_type, "name", t_ne_string).isPrimary(true);
addProp(t_var_source_type, "type", f.yenum("VarSourceTypes", "vault", "ssm", "dummy", "secretmanager")).isRequired(true);
addProp(t_var_source_type, "config", f.contextAware("VarSourceConfig", (dc) -> {
YamlPath path = dc.getPath();
if (path != null) {
YamlFileAST root = asts.getSafeAst(dc.getDocument());
if (root!=null) {
String value = NodeUtil.asScalar(path.dropLast().append(YamlPathSegment.valueAt("type")).traverseToNode(root));
switch (value) {
case "vault":
YBeanType vaultConfig = f.ybean("VaultConfig");
addProp(vaultConfig, "uri", t_ne_string).isPrimary(true);
addProp(vaultConfig, "ca_cert", t_string);
addProp(vaultConfig, "path_prefix", t_string);
addProp(vaultConfig, "lookup_templates", f.yseq(t_string));
addProp(vaultConfig, "shared_path", t_string);
addProp(vaultConfig, "namespace", t_string);
addProp(vaultConfig, "client_cert", t_string);
addProp(vaultConfig, "client_key", t_string);
addProp(vaultConfig, "client_name", t_string);
addProp(vaultConfig, "insecure_skip_verify", t_boolean);
addProp(vaultConfig, "client_token", t_string);
addProp(vaultConfig, "auth_backend", t_string);
addProp(vaultConfig, "auth_params", f.ymap(t_ne_string, t_string));
addProp(vaultConfig, "auth_max_ttl", t_duration);
addProp(vaultConfig, "auth_retry_max", t_duration);
addProp(vaultConfig, "auth_retry_initial", t_duration);
return vaultConfig;
case "ssm":
YBeanType ssmConfig = f.ybean("SSMConfig");
addProp(ssmConfig, "region", t_string).isPrimary(true);
return ssmConfig;
case "dummy":
YBeanType dummyConfig = f.ybean("DummyConfig");
addProp(dummyConfig, "vars", t_any).isPrimary(true);
return dummyConfig;
case "secretmanager":
YBeanType smcConfig = f.ybean("SecretManagerConfig");
addProp(smcConfig, "aws-secretsmanager-region", t_ne_string).isPrimary(true);
addProp(smcConfig, "aws-secretsmanager-access-key", t_ne_string);
addProp(smcConfig, "aws-secretsmanager-secret-key", t_ne_string);
addProp(smcConfig, "aws-secretsmanager-session-token", t_ne_string);
addProp(smcConfig, "aws-secretsmanager-pipeline-secret-template", t_string);
addProp(smcConfig, "aws-secretsmanager-team-secret-template", t_string);
return smcConfig;
default:
return null;
}
}
}
return null;
}).treatAsBean()).isRequired(true);
AbstractType group = f.ybean("Group");
addProp(group, "name", t_group_name_def).isPrimary(true);
@@ -520,6 +577,7 @@ public class PipelineYmlSchema implements YamlSchema {
AbstractType t_groups = f.yseq(group).require(models::jobAssignmentIsComplete);
addProp(TOPLEVEL_TYPE, "resources", t_resources);
addProp(TOPLEVEL_TYPE, "jobs", t_jobs);
addProp(TOPLEVEL_TYPE, "var_sources", f.yseq(t_var_source_type));
addProp(TOPLEVEL_TYPE, "resource_types", t_resourceTypes);
addProp(TOPLEVEL_TYPE, "groups", t_groups);
addProp(TOPLEVEL_TYPE, "display", t_display);

View File

@@ -0,0 +1 @@
*Required.* A mapping of **var** name to **var** value. An arbitrary object representing key-value definitions for **((vars))**.

View File

@@ -0,0 +1 @@
*Required.* The AWS region to read secrets from.

View File

@@ -0,0 +1 @@
*Optional.* The base path used when attempting to locate a pipeline-level secret.

View File

@@ -0,0 +1 @@
*Required.* The AWS region that requests to Secrets Manager will be sent to.

View File

@@ -0,0 +1 @@
*Optional.* The secret key that corresponds to the access key defined above.

View File

@@ -0,0 +1 @@
*Optional.* The base path used when attempting to locate a team-level secret.

View File

@@ -0,0 +1,5 @@
*Required.* Depending on the chosen `type` corresponding config:
- The **Vault** for configuring a [Vault](https://www.vaultproject.io/) server as a **((var))** source.
- The **SSM** for configuring an [AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/) in a single region as a **((var))** source.
- The **Dummy** for configuring a static map of vars to values. This is really only useful if you have no better alternative for credential management but still have sensitive values that you would like to redact them from build output.
- The **Secret Manager** for configuring integration with [AWS Secrets Manager for credential management](https://concourse-ci.org/aws-asm-credential-manager.html)

View File

@@ -0,0 +1 @@
*Required.* The name of the **((var))** source. This should be short and simple. This name will be referenced **((var))** [syntax](https://concourse-ci.org/vars.html#var-syntax) throughout the config.

View File

@@ -0,0 +1,5 @@
*Required.* Expected one of:
- `vault` type supports configuring a [Vault](https://www.vaultproject.io/) server as a **((var))** source.
- `ssm` type supports configuring an [AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/) in a single region as a **((var))** source.
- `dummy` type supports configuring a static map of vars to values. This is really only useful if you have no better alternative for credential management but still have sensitive values that you would like to redact them from build output.
- `secretmanager` type supports integration with [AWS Secrets Manager for credential management](https://concourse-ci.org/aws-asm-credential-manager.html)

View File

@@ -0,0 +1,3 @@
*Optional.* Authenticate using an auth backend, e.g. cert or approle.
See [Using the approle auth backend](https://concourse-ci.org/vault-credential-manager.html#vault-approle-auth) or [Using the cert auth backend](https://concourse-ci.org/vault-credential-manager.html#vault-cert-auth) for more information.

View File

@@ -0,0 +1 @@
*Optional.* Maximum duration to elapse before forcing the client to log in again.

View File

@@ -0,0 +1,3 @@
*Optional.* A key-value map of parameters to pass during authentication.
See [Using the approle auth backend](https://concourse-ci.org/vault-credential-manager.html#vault-approle-auth) for more information.

View File

@@ -0,0 +1 @@
*Optional.* When retrying during authentication, start with this retry interval. The interval will increase exponentially until `auth_retry_max` is reached.

View File

@@ -0,0 +1 @@
*Optional.* When failing to authenticate, give up after this amount of time.

View File

@@ -0,0 +1 @@
*Optional.* The PEM encoded contents of a CA certificate to use when connecting to the API.

View File

@@ -0,0 +1,3 @@
*Optional.* A PEM encoded client certificate, for use with TLS based auth.
See [Using the cert auth backend](https://concourse-ci.org/vault-credential-manager.html#vault-cert-auth) for more information.

View File

@@ -0,0 +1,3 @@
*Optional.* A PEM encoded client key, for use with TLS based auth.
See [Using the cert auth backend](https://concourse-ci.org/vault-credential-manager.html#vault-cert-auth) for more information.

View File

@@ -0,0 +1 @@
*Optional.* The expected name of the server when connecting through TLS.

View File

@@ -0,0 +1,3 @@
*Optional.* Authenticate via a periodic client token.
See [Using a periodic token](https://concourse-ci.org/vault-credential-manager.html#vault-periodic-token) for more information.

View File

@@ -0,0 +1 @@
*Optional.* Skip TLS validation. Not recommended. Don't do it. No really, don't.

View File

@@ -0,0 +1,5 @@
*Optional.* Default `["/{{.Team}}/{{.Pipeline}}/{{.Secret}}", "/{{.Team}}/{{.Secret}}"]`.
A list of path templates to be expanded in a team and pipeline context subject to the `path_prefix` and `namespace`.
See [Changing the path templates](https://concourse-ci.org/vault-credential-manager.html#vault-lookup-templates) for more information.

View File

@@ -0,0 +1 @@
*Optional.* A [Vault namespace](https://www.vaultproject.io/docs/enterprise/namespaces/index.html) to operate under.

View File

@@ -0,0 +1,3 @@
*Optional.* Default `/concourse`. A prefix under which to look for all credential values.
See [Changing the path prefix](https://concourse-ci.org/vault-credential-manager.html#vault-path-prefix) for more information.

View File

@@ -0,0 +1,3 @@
*Optional.* An additional path under which credentials will be looked up.
See [Configuring a shared path](https://concourse-ci.org/vault-credential-manager.html#vault-shared-path) for more information.

View File

@@ -0,0 +1 @@
*Required.* The URL of the Vault API.

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016, 2022 Pivotal, Inc.
* Copyright (c) 2016, 2023 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -1113,6 +1113,12 @@ public class ConcourseEditorTest {
"resources:\n" +
"- name: $1\n" +
" type: $2<*>"
, // ---------------
"var_sources:\n" +
"- name: $1\n" +
" type: $2\n" +
" config:\n" +
" $3<*>"
);
editor = harness.newEditor("rety<*>");
@@ -4689,6 +4695,7 @@ public class ConcourseEditorTest {
"← groups",
"← jobs",
"← resource_types",
"← var_sources",
"← - Resource Snippet",
// For the 'next job' context
"← - name"
@@ -4949,6 +4956,7 @@ public class ConcourseEditorTest {
"← groups",
"← resource_types",
"← resources",
"← var_sources",
"← - Job Snippet",
"← - name"
);
@@ -5443,6 +5451,7 @@ public class ConcourseEditorTest {
"groups",
"jobs",
"resource_types",
"var_sources",
"→ type",
"- Resource Snippet",
"- name"
@@ -6811,6 +6820,178 @@ public class ConcourseEditorTest {
editor.assertCompletionLabels(c -> c.getLabel().startsWith("cache"), "cache", "cache_from", "cache_tag");
}
}
@Test
void var_source_AttributeHovers() throws Exception {
Editor editor = harness.newEditor(
"var_sources:\n" +
"- name: sts4\n" +
" type: ssm\n" +
" config:\n" +
" region: east\n"
);
editor.assertProblems();
editor.assertHoverContains("name", "The name of the **((var))** source");
editor.assertHoverContains("type", "Expected one of:");
editor.assertHoverContains("config", "Depending on the chosen `type` corresponding config:");
}
@Test
void var_source_AttributeReconcile() throws Exception {
Editor editor = harness.newEditor(
"var_sources:\n" +
"- name: "
);
editor.assertProblems(
"-|[config, type] are required",
"|String should not be empty"
);
editor = harness.newEditor(
"var_sources:\n" +
"- type: blah"
);
editor.assertProblems(
"-|[config, name] are required",
"blah|Valid values are: [dummy, secretmanager, ssm, vault]"
);
}
@Test
void var_source_SSMConfig_Attrs_Hovers() throws Exception {
Editor editor = harness.newEditor(
"var_sources:\n" +
"- name: sts4\n" +
" type: ssm\n" +
" config:\n" +
" region: east\n"
);
editor.assertProblems();
editor.assertHoverContains("region", "The AWS region to read secrets from.");
}
@Test
void var_source_DummyConfig_Attrs_Hovers() throws Exception {
Editor editor = harness.newEditor(
"var_sources:\n" +
"- name: sts4\n" +
" type: dummy\n" +
" config:\n" +
" vars: east\n"
);
editor.assertProblems();
editor.assertHoverContains("vars", "A mapping of **var** name to **var** value.");
}
@Test
void var_source_SecretManagerConfig_Attrs_Hovers() throws Exception {
Editor editor = harness.newEditor(
"var_sources:\n" +
"- name: sts4\n" +
" type: secretmanager\n" +
" config:\n" +
" aws-secretsmanager-access-key: access-key\n" +
" aws-secretsmanager-secret-key: secret-key\n" +
" aws-secretsmanager-session-token: token\n" +
" aws-secretsmanager-region: region\n" +
" aws-secretsmanager-pipeline-secret-template: pipeline\n" +
" aws-secretsmanager-team-secret-template: team\n"
);
editor.assertProblems();
editor.assertHoverContains("aws-secretsmanager-access-key", "A valid AWS access key.");
editor.assertHoverContains("aws-secretsmanager-secret-key", "The secret key that corresponds to the access key defined above.");
editor.assertHoverContains("aws-secretsmanager-session-token", "A valid AWS session token.");
editor.assertHoverContains("aws-secretsmanager-region", "The AWS region that requests to Secrets Manager will be sent to.");
editor.assertHoverContains("aws-secretsmanager-pipeline-secret-template", "The base path used when attempting to locate a pipeline-level secret.");
editor.assertHoverContains("aws-secretsmanager-team-secret-template", "The base path used when attempting to locate a team-level secret.");
}
@Test
void var_source_SecretManagerConfig_Attrs_Reconcile() throws Exception {
Editor editor = harness.newEditor(
"var_sources:\n" +
"- name: sts4\n" +
" type: secretmanager\n" +
" config:\n" +
" aws-secretsmanager-access-key: access-key\n"
);
editor.assertProblems(
"config|'aws-secretsmanager-region' is required"
);
}
@Test
void var_source_VaultConfig_Attrs_Hovers() throws Exception {
Editor editor = harness.newEditor(
"var_sources:\n" +
"- name: sts4\n" +
" type: vault\n" +
" config:\n" +
" uri: some_uri\n" +
" auth_backend: backend\n" +
" auth_max_ttl: 10s\n" +
" auth_params: \n" +
" p1: v1\n" +
" p2: v2\n" +
" auth_retry_initial: 1s\n" +
" auth_retry_max: 60s\n" +
" ca_cert: ca\n" +
" client_cert: client\n" +
" client_key: client-key\n" +
" client_name: client-name\n" +
" client_token: client-token\n" +
" insecure_skip_verify: true\n" +
" lookup_templates:\n" +
" - t1\n" +
" - t2\n" +
" namespace: awesomes\n" +
" path_prefix: prefix\n" +
" shared_path: shared\n"
);
editor.assertProblems();
editor.assertHoverContains("uri", "The URL of the Vault API.");
editor.assertHoverContains("auth_backend", "Authenticate using an auth backend, e.g. cert or approle.");
editor.assertHoverContains("auth_max_ttl", "Maximum duration to elapse before forcing the client to log in again.");
editor.assertHoverContains("auth_params", "A key-value map of parameters to pass during authentication.");
editor.assertHoverContains("auth_retry_initial", "When retrying during authentication, start with this retry interval.");
editor.assertHoverContains("auth_retry_max", "When failing to authenticate, give up after this amount of time.");
editor.assertHoverContains("ca_cert", "The PEM encoded contents of a CA certificate to use when connecting to the API.");
editor.assertHoverContains("client_cert", "A PEM encoded client certificate, for use with TLS based auth.");
editor.assertHoverContains("client_key", "A PEM encoded client key, for use with TLS based auth.");
editor.assertHoverContains("client_name", "The expected name of the server when connecting through TLS.");
editor.assertHoverContains("client_token", "Authenticate via a periodic client token.");
editor.assertHoverContains("insecure_skip_verify", "Skip TLS validation. Not recommended. Don't do it. No really, don't.");
editor.assertHoverContains("lookup_templates", "A list of path templates to be expanded in a team and pipeline context subject to the `path_prefix` and `namespace`.");
editor.assertHoverContains("namespace", "Vault namespace");
editor.assertHoverContains("path_prefix", "A prefix under which to look for all credential values.");
editor.assertHoverContains("shared_path", "An additional path under which credentials will be looked up.");
}
@Test
void var_source_VaultConfig_Attrs_Reconcile() throws Exception {
Editor editor = harness.newEditor(
"var_sources:\n" +
"- name: sts4\n" +
" type: vault\n" +
" config:\n" +
" namespace: awesome\n"
);
editor.assertProblems(
"config|'uri' is required"
);
}
//////////////////////////////////////////////////////////////////////////////