Skip to content
This repository was archived by the owner on Jul 26, 2022. It is now read-only.

Commit cceb40b

Browse files
authored
feat: add support for Alibaba Cloud KMS Secret Manager (#355)
* add support for Alibaba Cloud KMS Secret Manager with current version only the followings are supported: 1. 'ACSCurrent' for version stage is the default value and recommented. 2. AccessKeyId + AccessKeySecret or STS (assume a provided role name using the provided AccessKeyId and AccessKeySecret). ECS_RAM_ROLE and Kube2Ram are not supported yet. 3. secret value as plaintext - parsing secret value as a json object not yet support
1 parent 4273598 commit cceb40b

11 files changed

+358
-6
lines changed

README.md

+37-2
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ A few properties has changed name overtime, we still maintain backwards compatbi
238238

239239
## Backends
240240

241-
kubernetes-external-secrets supports AWS Secrets Manager, AWS System Manager, Hashicorp Vault, Azure Key Vault and Google Secret Manager.
241+
kubernetes-external-secrets supports AWS Secrets Manager, AWS System Manager, Hashicorp Vault, Azure Key Vault, Google Secret Manager and Alibaba Cloud KMS Secret Manager.
242242

243243
### AWS Secrets Manager
244244

@@ -369,10 +369,10 @@ filesFromSecret:
369369
kubernetes-external-secrets supports fetching secrets from [Azure Key vault](https://azure.microsoft.com/en-ca/services/key-vault/)
370370
371371
You will need to set these env vars in the deployment of kubernetes-external-secrets:
372+
372373
- AZURE_TENANT_ID
373374
- AZURE_CLIENT_ID
374375
- AZURE_CLIENT_SECRET
375-
376376
The SP configured will require get and list access policies on the AZURE_KEYVAULT_NAME.
377377
378378
```yml
@@ -406,6 +406,41 @@ spec:
406406
isBinary: true
407407
```
408408

409+
### Alibaba Cloud KMS Secret Manager
410+
411+
kubernetes-external-secrets supports fetching secrets from [Alibaba Cloud KMS Secret Manager](https://www.alibabacloud.com/help/doc-detail/152001.htm)
412+
413+
create secret by using the [aliyun-cli](https://github.com/aliyun/aliyun-cli) command below:
414+
415+
```bash
416+
# you need to configure aliyun-cli with a valid RAM user and proper permission
417+
aliyun kms CreateSecret --SecretName my_secret --SecretData P@ssw0rd --VersionId 001
418+
```
419+
420+
You will need to set these env vars in the deployment of kubernetes-external-secrets:
421+
422+
- ALICLOUD_ACCESS_KEY_ID
423+
- ALICLOUD_ACCESS_KEY_SECRET
424+
- ALICLOUD_ENDPOINT
425+
426+
```yml
427+
apiVersion: kubernetes-client.io/v1
428+
kind: ExternalSecret
429+
metadata:
430+
name: hello-service
431+
spec:
432+
backendType: alicloudSecretsManager
433+
# optional: specify role to assume using provided access key ID and access key secret when retrieving the data
434+
roleArn: acs:ram::{UID}:role/demo
435+
data:
436+
- key: hello-credentials1
437+
name: password
438+
- key: hello-credentials2
439+
name: username
440+
# Version Stage in Alibaba Cloud KMS Secrets Manager. Optional, default value is ACSCurrent
441+
versionStage: ACSCurrent
442+
```
443+
409444
### GCP Secret Manager
410445

411446
kubernetes-external-secrets supports fetching secrets from [GCP Secret Manager](https://cloud.google.com/solutions/secrets-management)

charts/kubernetes-external-secrets/README.md

+3
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ The following table lists the configurable parameters of the `kubernetes-externa
5555
| `envVarsFromSecret.AZURE_TENANT_ID` | Set AZURE_TENANT_ID (from a secret) in Deployment Pod | |
5656
| `envVarsFromSecret.AZURE_CLIENT_ID` | Set AZURE_CLIENT_ID (from a secret) in Deployment Pod | |
5757
| `envVarsFromSecret.AZURE_CLIENT_SECRET` | Set AZURE_CLIENT_SECRET (from a secret) in Deployment Pod | |
58+
| `envVarsFromSecret.ALICLOUD_ENDPOINT` | Set ALICLOUD_ENDPOINT for KMS Service in Deployment Pod | |
59+
| `envVarsFromSecret.ALICLOUD_ACCESS_KEY_ID` | Set ALICLOUD_ACCESS_KEY_ID (from a secret) in Deployment Pod | |
60+
| `envVarsFromSecret.ALICLOUD_ACCESS_KEY_SECRET` | Set ALICLOUD_ACCESS_KEY_SECRET (from a secret) in Deployment Pod | |
5861
| `image.repository` | kubernetes-external-secrets Image name | `godaddy/kubernetes-external-secrets` |
5962
| `image.tag` | kubernetes-external-secrets Image tag | `3.2.0` |
6063
| `image.pullPolicy` | Image pull policy | `IfNotPresent` |

charts/kubernetes-external-secrets/values.yaml

+9
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ env:
1919
# AWS_SECRET_ACCESS_KEY:
2020
# secretKeyRef: aws-credentials
2121
# key: key
22+
# ALICLOUD_ENDPOINT:
23+
# secretKeyRef: alicloud-credentials
24+
# key: endpoint
25+
# ALICLOUD_ACCESS_KEY_ID:
26+
# secretKeyRef: alicloud-credentials
27+
# key: id
28+
# ALICLOUD_ACCESS_KEY_SECRET:
29+
# secretKeyRef: alicloud-credentials
30+
# key: secret
2231
# AZURE_TENANT_ID:
2332
# secretKeyRef: azure-credentials
2433
# key: tenantid

config/alicloud-config.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
'use strict'
2+
3+
// Alibaba Cloud expects the following four environment variables:
4+
// - ALICLOUD_ENDPOINT: endpoint URL http(s)://kms.{regionID}.aliyuncs.com or http(s)://kms-vpc.{regionID}.aliyuncs.com
5+
// - ALICLOUD_ACCESS_KEY_ID: The access key ID
6+
// - ALICLOUD_ACCESS_KEY_SECRET: The access key secret
7+
8+
module.exports = {
9+
credential: {
10+
accessKeyId: process.env.ALICLOUD_ACCESS_KEY_ID,
11+
accessKeySecret: process.env.ALICLOUD_ACCESS_KEY_SECRET,
12+
endpoint: process.env.ALICLOUD_ENDPOINT,
13+
type: 'access_key'
14+
}
15+
}

config/index.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const path = require('path')
1010

1111
const awsConfig = require('./aws-config')
1212
const azureConfig = require('./azure-config')
13+
const alicloudConfig = require('./alicloud-config')
1314
const gcpConfig = require('./gcp-config')
1415
const envConfig = require('./environment')
1516
const CustomResourceManager = require('../lib/custom-resource-manager')
@@ -18,6 +19,7 @@ const SystemManagerBackend = require('../lib/backends/system-manager-backend')
1819
const VaultBackend = require('../lib/backends/vault-backend')
1920
const AzureKeyVaultBackend = require('../lib/backends/azure-keyvault-backend')
2021
const GCPSecretsManagerBackend = require('../lib/backends/gcp-secrets-manager-backend')
22+
const AliCloudSecretsManagerBackend = require('../lib/backends/alicloud-secrets-manager-backend')
2123

2224
// Get document, or throw exception on error
2325
// eslint-disable-next-line security/detect-non-literal-fs-filename
@@ -60,13 +62,19 @@ const gcpSecretsManagerBackend = new GCPSecretsManagerBackend({
6062
client: gcpConfig.gcpSecretsManager(),
6163
logger
6264
})
65+
const alicloudSecretsManagerBackend = new AliCloudSecretsManagerBackend({
66+
credential: alicloudConfig.credential,
67+
logger
68+
})
6369
const backends = {
6470
// when adding a new backend, make sure to change the CRD property too
6571
secretsManager: secretsManagerBackend,
6672
systemManager: systemManagerBackend,
6773
vault: vaultBackend,
6874
azureKeyVault: azureKeyVaultBackend,
69-
gcpSecretsManager: gcpSecretsManagerBackend
75+
gcpSecretsManager: gcpSecretsManagerBackend,
76+
alicloudSecretsManager: alicloudSecretsManagerBackend
77+
7078
}
7179

7280
// backwards compatibility

crd.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ spec:
4444
- vault
4545
- azureKeyVault
4646
- gcpSecretsManager
47+
- alicloudSecretsManager
4748
vaultRole:
4849
type: string
4950
vaultMountPoint:
@@ -103,6 +104,10 @@ spec:
103104
backendType:
104105
enum:
105106
- gcpSecretsManager
107+
- properties:
108+
backendType:
109+
enum:
110+
- alicloudSecretsManager
106111
anyOf:
107112
- required:
108113
- data
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
apiVersion: kubernetes-client.io/v1
2+
kind: ExternalSecret
3+
metadata:
4+
name: secretsmanager-example
5+
spec:
6+
backendType: alicloudSecretsManager
7+
data:
8+
- key: akey
9+
name: password
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
'use strict'
2+
3+
const KVBackend = require('./kv-backend')
4+
const { default: Client, GetSecretValueRequest } = require('@alicloud/kms20160120')
5+
/** Secrets Manager backend class. */
6+
class AliCloudSecretsManagerBackend extends KVBackend {
7+
/**
8+
* Create Secrets manager backend.
9+
* @param {Object} logger - Logger for logging stuff.
10+
* @param {Object} credential - Secrets manager credential.
11+
*/
12+
constructor ({ logger, credential }) {
13+
super({ logger })
14+
this._credential = credential
15+
}
16+
17+
_getClient ({ specOptions: { roleArn } }) {
18+
const config = {
19+
endpoint: this._credential.endpoint,
20+
accessKeyId: this._credential.accessKeyId,
21+
accessKeySecret: this._credential.accessKeySecret,
22+
type: this._credential.type
23+
}
24+
if (roleArn) {
25+
config.type = 'ram_role_arn'
26+
config.roleArn = roleArn
27+
}
28+
return new Client(config)
29+
}
30+
31+
/**
32+
* Get secret property value from Alibaba Cloud KMS Secrets Manager.
33+
* @param {string} key - Key used to store secret property value in Alibaba Cloud KMS Secrets Manager.
34+
* @returns {Promise} Promise object representing secret property value.
35+
*/
36+
37+
async _get ({ key, specOptions: { roleArn }, keyOptions: { versionStage } }) {
38+
this._logger.info(`fetching secret ${key} on version stage ${versionStage} from AliCloud Secret Manager using role ${roleArn}`)
39+
const getSecretValueRequest = new GetSecretValueRequest({
40+
secretName: key,
41+
versionStage: versionStage
42+
})
43+
44+
const client = this._getClient({ specOptions: { roleArn } })
45+
const value = await client.getSecretValue(getSecretValueRequest)
46+
47+
return value.secretData.toString('utf-8')
48+
}
49+
}
50+
51+
module.exports = AliCloudSecretsManagerBackend
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/* eslint-env mocha */
2+
'use strict'
3+
4+
const { expect } = require('chai')
5+
const sinon = require('sinon')
6+
7+
const AliCloudSecretsManagerBackend = require('./alicloud-secrets-manager-backend')
8+
9+
describe('AliCloudSecretsManagerBackend', () => {
10+
let loggerMock
11+
let clientMock
12+
let aliCloudSecretsManagerBackend
13+
14+
const password = 'fakeSecretPropertyValue'
15+
const secret = {
16+
secretData: password
17+
}
18+
const key = 'password'
19+
20+
beforeEach(() => {
21+
loggerMock = sinon.mock()
22+
loggerMock.info = sinon.stub()
23+
clientMock = sinon.mock()
24+
clientMock.getSecretValue = sinon.stub().returns(secret)
25+
26+
aliCloudSecretsManagerBackend = new AliCloudSecretsManagerBackend({
27+
credential: null,
28+
logger: loggerMock
29+
})
30+
aliCloudSecretsManagerBackend._getClient = sinon.stub().returns(clientMock)
31+
})
32+
33+
describe('_get', () => {
34+
it('returns secret property value', async () => {
35+
const specOptions = {}
36+
const keyOptions = {}
37+
const secretPropertyValue = await aliCloudSecretsManagerBackend._get({
38+
key: key,
39+
specOptions,
40+
keyOptions
41+
})
42+
expect(secretPropertyValue).equals(password)
43+
})
44+
})
45+
})

0 commit comments

Comments
 (0)