Skip to content
代码片段 群组 项目
提交 d97875ea 编辑于 作者: Marius Bobin's avatar Marius Bobin
浏览文件

Merge branch '356986-aw-add-token-sub-keyword' into 'master'

No related branches found
No related tags found
无相关合并请求
...@@ -607,53 +607,65 @@ ...@@ -607,53 +607,65 @@
"secrets": { "secrets": {
"type": "object", "type": "object",
"markdownDescription": "Defines secrets to be injected as environment variables. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#secrets).", "markdownDescription": "Defines secrets to be injected as environment variables. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#secrets).",
"additionalProperties": { "patternProperties": {
"type": "object", ".*": {
"description": "Environment variable name", "type": "object",
"properties": { "properties": {
"vault": { "vault": {
"oneOf": [ "oneOf": [
{ {
"type": "string", "type": "string",
"description": "The secret to be fetched from Vault (e.g. 'production/db/password@ops' translates to secret 'ops/data/production/db', field `password`)" "markdownDescription": "The secret to be fetched from Vault (e.g. 'production/db/password@ops' translates to secret 'ops/data/production/db', field `password`). [Learn More](https://docs.gitlab.com/ee/ci/yaml/#secretsvault)"
}, },
{ {
"type": "object", "type": "object",
"properties": { "properties": {
"engine": { "engine": {
"type": "object", "type": "object",
"properties": { "properties": {
"name": { "name": {
"type": "string" "type": "string"
},
"path": {
"type": "string"
}
}, },
"path": { "required": [
"type": "string" "name",
} "path"
]
}, },
"required": [ "path": {
"name", "type": "string"
"path" },
] "field": {
}, "type": "string"
"path": { }
"type": "string"
}, },
"field": { "required": [
"type": "string" "engine",
} "path",
}, "field"
"required": [ ],
"engine", "additionalProperties": false
"path", }
"field" ]
] },
} "file": {
] "type": "boolean",
} "default": true,
}, "markdownDescription": "Configures the secret to be stored as either a file or variable type CI/CD variable. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#secretsfile)"
"required": [ },
"vault" "token": {
] "type": "string",
"description": "Specifies the JWT variable that should be used to authenticate with Hashicorp Vault."
}
},
"required": [
"vault"
],
"additionalProperties": false
}
} }
}, },
"before_script": { "before_script": {
......
...@@ -40,9 +40,7 @@ def vault_jwt(secret) ...@@ -40,9 +40,7 @@ def vault_jwt(secret)
def id_token_var(secret) def id_token_var(secret)
return unless id_tokens? return unless id_tokens?
token = secret['token'] || id_tokens.each_key.first secret['token'] || "$#{id_tokens.each_key.first}"
"${#{token}}"
end end
end end
end end
......
...@@ -12,7 +12,7 @@ class Secret < ::Gitlab::Config::Entry::Node ...@@ -12,7 +12,7 @@ class Secret < ::Gitlab::Config::Entry::Node
include ::Gitlab::Config::Entry::Attributable include ::Gitlab::Config::Entry::Attributable
REQUIRED_KEYS = %i[vault].freeze REQUIRED_KEYS = %i[vault].freeze
ALLOWED_KEYS = (REQUIRED_KEYS + %i[file]).freeze ALLOWED_KEYS = (REQUIRED_KEYS + %i[file token]).freeze
attributes ALLOWED_KEYS attributes ALLOWED_KEYS
...@@ -21,6 +21,15 @@ class Secret < ::Gitlab::Config::Entry::Node ...@@ -21,6 +21,15 @@ class Secret < ::Gitlab::Config::Entry::Node
validations do validations do
validates :config, allowed_keys: ALLOWED_KEYS, required_keys: REQUIRED_KEYS validates :config, allowed_keys: ALLOWED_KEYS, required_keys: REQUIRED_KEYS
validates :token, type: String, allow_nil: true
end
def value
{
vault: vault_value,
file: file_value,
token: token
}.compact
end end
end end
end end
......
...@@ -53,6 +53,40 @@ ...@@ -53,6 +53,40 @@
it_behaves_like 'configures secrets' it_behaves_like 'configures secrets'
end end
context 'when `token` is defined' do
let(:config) do
{
vault: {
engine: { name: 'kv-v2', path: 'kv-v2' },
path: 'production/db',
field: 'password'
},
token: '$TEST_ID_TOKEN'
}
end
describe '#value' do
it 'returns secret configuration' do
expect(entry.value).to eq(
{
vault: {
engine: { name: 'kv-v2', path: 'kv-v2' },
path: 'production/db',
field: 'password'
},
token: '$TEST_ID_TOKEN'
}
)
end
end
describe '#valid?' do
it 'is valid' do
expect(entry).to be_valid
end
end
end
end end
end end
......
...@@ -127,7 +127,7 @@ ...@@ -127,7 +127,7 @@
it 'adds the first ID token to the Vault server payload' do it 'adds the first ID token to the Vault server payload' do
jwt = presenter.secrets_configuration.dig('DATABASE_PASSWORD', 'vault', 'server', 'auth', 'data', 'jwt') jwt = presenter.secrets_configuration.dig('DATABASE_PASSWORD', 'vault', 'server', 'auth', 'data', 'jwt')
expect(jwt).to eq('${VAULT_ID_TOKEN_1}') expect(jwt).to eq('$VAULT_ID_TOKEN_1')
end end
context 'when the token variable is specified for the vault secret' do context 'when the token variable is specified for the vault secret' do
...@@ -135,7 +135,7 @@ ...@@ -135,7 +135,7 @@
{ {
DATABASE_PASSWORD: { DATABASE_PASSWORD: {
file: true, file: true,
token: 'VAULT_ID_TOKEN_2', token: '$VAULT_ID_TOKEN_2',
vault: { vault: {
engine: { name: 'kv-v2', path: 'kv-v2' }, engine: { name: 'kv-v2', path: 'kv-v2' },
path: 'production/db', path: 'production/db',
...@@ -148,7 +148,7 @@ ...@@ -148,7 +148,7 @@
it 'uses the specified token variable' do it 'uses the specified token variable' do
jwt = presenter.secrets_configuration.dig('DATABASE_PASSWORD', 'vault', 'server', 'auth', 'data', 'jwt') jwt = presenter.secrets_configuration.dig('DATABASE_PASSWORD', 'vault', 'server', 'auth', 'data', 'jwt')
expect(jwt).to eq('${VAULT_ID_TOKEN_2}') expect(jwt).to eq('$VAULT_ID_TOKEN_2')
end end
end end
end end
......
...@@ -155,6 +155,7 @@ ...@@ -155,6 +155,7 @@
secrets: secrets:
DATABASE_PASSWORD: DATABASE_PASSWORD:
vault: production/db/password vault: production/db/password
token: $ID_TOKEN
YAML YAML
end end
...@@ -172,7 +173,8 @@ ...@@ -172,7 +173,8 @@
'engine' => { 'name' => 'kv-v2', 'path' => 'kv-v2' }, 'engine' => { 'name' => 'kv-v2', 'path' => 'kv-v2' },
'path' => 'production/db', 'path' => 'production/db',
'field' => 'password' 'field' => 'password'
} },
'token' => '$ID_TOKEN'
} }
}) })
end end
......
...@@ -32,6 +32,7 @@ import VariablesYaml from './yaml_tests/positive_tests/variables.yml'; ...@@ -32,6 +32,7 @@ import VariablesYaml from './yaml_tests/positive_tests/variables.yml';
import JobWhenYaml from './yaml_tests/positive_tests/job_when.yml'; import JobWhenYaml from './yaml_tests/positive_tests/job_when.yml';
import IdTokensYaml from './yaml_tests/positive_tests/id_tokens.yml'; import IdTokensYaml from './yaml_tests/positive_tests/id_tokens.yml';
import HooksYaml from './yaml_tests/positive_tests/hooks.yml'; import HooksYaml from './yaml_tests/positive_tests/hooks.yml';
import SecretsYaml from './yaml_tests/positive_tests/secrets.yml';
// YAML NEGATIVE TEST // YAML NEGATIVE TEST
import ArtifactsNegativeYaml from './yaml_tests/negative_tests/artifacts.yml'; import ArtifactsNegativeYaml from './yaml_tests/negative_tests/artifacts.yml';
...@@ -50,6 +51,7 @@ import VariablesInvalidSyntaxDescYaml from './yaml_tests/negative_tests/variable ...@@ -50,6 +51,7 @@ import VariablesInvalidSyntaxDescYaml from './yaml_tests/negative_tests/variable
import VariablesWrongSyntaxUsageExpand from './yaml_tests/negative_tests/variables/wrong_syntax_usage_expand.yml'; import VariablesWrongSyntaxUsageExpand from './yaml_tests/negative_tests/variables/wrong_syntax_usage_expand.yml';
import IdTokensNegativeYaml from './yaml_tests/negative_tests/id_tokens.yml'; import IdTokensNegativeYaml from './yaml_tests/negative_tests/id_tokens.yml';
import HooksNegative from './yaml_tests/negative_tests/hooks.yml'; import HooksNegative from './yaml_tests/negative_tests/hooks.yml';
import SecretsNegativeYaml from './yaml_tests/negative_tests/secrets.yml';
const ajv = new Ajv({ const ajv = new Ajv({
strictTypes: false, strictTypes: false,
...@@ -87,6 +89,7 @@ describe('positive tests', () => { ...@@ -87,6 +89,7 @@ describe('positive tests', () => {
VariablesYaml, VariablesYaml,
ProjectPathYaml, ProjectPathYaml,
IdTokensYaml, IdTokensYaml,
SecretsYaml,
}), }),
)('schema validates %s', (_, input) => { )('schema validates %s', (_, input) => {
// We construct a new "JSON" from each main key that is inside a // We construct a new "JSON" from each main key that is inside a
...@@ -122,6 +125,7 @@ describe('negative tests', () => { ...@@ -122,6 +125,7 @@ describe('negative tests', () => {
ProjectPathIncludeLeadSlashYaml, ProjectPathIncludeLeadSlashYaml,
ProjectPathIncludeNoSlashYaml, ProjectPathIncludeNoSlashYaml,
ProjectPathIncludeTailSlashYaml, ProjectPathIncludeTailSlashYaml,
SecretsNegativeYaml,
TriggerNegative, TriggerNegative,
HooksNegative, HooksNegative,
}), }),
......
job_with_secrets_without_vault:
script:
- echo $TEST_DB_PASSWORD
secrets:
TEST_DB_PASSWORD:
token: $TEST_TOKEN
job_with_secrets_with_extra_properties:
script:
- echo $TEST_DB_PASSWORD
secrets:
TEST_DB_PASSWORD:
vault: test/db/password
extra_prop: TEST
job_with_secrets_with_invalid_vault_property:
script:
- echo $TEST_DB_PASSWORD
secrets:
TEST_DB_PASSWORD:
vault:
invalid: TEST
job_with_secrets_with_missing_required_vault_property:
script:
- echo $TEST_DB_PASSWORD
secrets:
TEST_DB_PASSWORD:
vault:
path: gitlab
job_with_secrets_with_missing_required_engine_property:
script:
- echo $TEST_DB_PASSWORD
secrets:
TEST_DB_PASSWORD:
vault:
engine:
path: kv
valid_job_with_secrets:
script:
- echo $TEST_DB_PASSWORD
secrets:
TEST_DB_PASSWORD:
vault: test/db/password
valid_job_with_secrets_and_token:
script:
- echo $TEST_DB_PASSWORD
secrets:
TEST_DB_PASSWORD:
vault: test/db/password
token: $TEST_TOKEN
valid_job_with_secrets_with_every_vault_keyword:
script:
- echo $TEST_DB_PASSWORD
secrets:
TEST_DB_PASSWORD:
vault:
engine:
name: test-engine
path: test
path: test/db
field: password
file: true
token: $TEST_TOKEN
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册