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

Commit

Permalink
feat: add support for Alibaba Cloud KMS Secret Manager (#355)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
yagrxu authored May 19, 2020
1 parent 4273598 commit cceb40b
Show file tree
Hide file tree
Showing 11 changed files with 358 additions and 6 deletions.
39 changes: 37 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ A few properties has changed name overtime, we still maintain backwards compatbi

## Backends

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

### AWS Secrets Manager

Expand Down Expand Up @@ -369,10 +369,10 @@ filesFromSecret:
kubernetes-external-secrets supports fetching secrets from [Azure Key vault](https://azure.microsoft.com/en-ca/services/key-vault/)
You will need to set these env vars in the deployment of kubernetes-external-secrets:
- AZURE_TENANT_ID
- AZURE_CLIENT_ID
- AZURE_CLIENT_SECRET
The SP configured will require get and list access policies on the AZURE_KEYVAULT_NAME.
```yml
Expand Down Expand Up @@ -406,6 +406,41 @@ spec:
isBinary: true
```

### Alibaba Cloud KMS Secret Manager

kubernetes-external-secrets supports fetching secrets from [Alibaba Cloud KMS Secret Manager](https://www.alibabacloud.com/help/doc-detail/152001.htm)

create secret by using the [aliyun-cli](https://github.com/aliyun/aliyun-cli) command below:

```bash
# you need to configure aliyun-cli with a valid RAM user and proper permission
aliyun kms CreateSecret --SecretName my_secret --SecretData P@ssw0rd --VersionId 001
```

You will need to set these env vars in the deployment of kubernetes-external-secrets:

- ALICLOUD_ACCESS_KEY_ID
- ALICLOUD_ACCESS_KEY_SECRET
- ALICLOUD_ENDPOINT

```yml
apiVersion: kubernetes-client.io/v1
kind: ExternalSecret
metadata:
name: hello-service
spec:
backendType: alicloudSecretsManager
# optional: specify role to assume using provided access key ID and access key secret when retrieving the data
roleArn: acs:ram::{UID}:role/demo
data:
- key: hello-credentials1
name: password
- key: hello-credentials2
name: username
# Version Stage in Alibaba Cloud KMS Secrets Manager. Optional, default value is ACSCurrent
versionStage: ACSCurrent
```

### GCP Secret Manager

kubernetes-external-secrets supports fetching secrets from [GCP Secret Manager](https://cloud.google.com/solutions/secrets-management)
Expand Down
3 changes: 3 additions & 0 deletions charts/kubernetes-external-secrets/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ The following table lists the configurable parameters of the `kubernetes-externa
| `envVarsFromSecret.AZURE_TENANT_ID` | Set AZURE_TENANT_ID (from a secret) in Deployment Pod | |
| `envVarsFromSecret.AZURE_CLIENT_ID` | Set AZURE_CLIENT_ID (from a secret) in Deployment Pod | |
| `envVarsFromSecret.AZURE_CLIENT_SECRET` | Set AZURE_CLIENT_SECRET (from a secret) in Deployment Pod | |
| `envVarsFromSecret.ALICLOUD_ENDPOINT` | Set ALICLOUD_ENDPOINT for KMS Service in Deployment Pod | |
| `envVarsFromSecret.ALICLOUD_ACCESS_KEY_ID` | Set ALICLOUD_ACCESS_KEY_ID (from a secret) in Deployment Pod | |
| `envVarsFromSecret.ALICLOUD_ACCESS_KEY_SECRET` | Set ALICLOUD_ACCESS_KEY_SECRET (from a secret) in Deployment Pod | |
| `image.repository` | kubernetes-external-secrets Image name | `godaddy/kubernetes-external-secrets` |
| `image.tag` | kubernetes-external-secrets Image tag | `3.2.0` |
| `image.pullPolicy` | Image pull policy | `IfNotPresent` |
Expand Down
9 changes: 9 additions & 0 deletions charts/kubernetes-external-secrets/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ env:
# AWS_SECRET_ACCESS_KEY:
# secretKeyRef: aws-credentials
# key: key
# ALICLOUD_ENDPOINT:
# secretKeyRef: alicloud-credentials
# key: endpoint
# ALICLOUD_ACCESS_KEY_ID:
# secretKeyRef: alicloud-credentials
# key: id
# ALICLOUD_ACCESS_KEY_SECRET:
# secretKeyRef: alicloud-credentials
# key: secret
# AZURE_TENANT_ID:
# secretKeyRef: azure-credentials
# key: tenantid
Expand Down
15 changes: 15 additions & 0 deletions config/alicloud-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict'

// Alibaba Cloud expects the following four environment variables:
// - ALICLOUD_ENDPOINT: endpoint URL http(s)://kms.{regionID}.aliyuncs.com or http(s)://kms-vpc.{regionID}.aliyuncs.com
// - ALICLOUD_ACCESS_KEY_ID: The access key ID
// - ALICLOUD_ACCESS_KEY_SECRET: The access key secret

module.exports = {
credential: {
accessKeyId: process.env.ALICLOUD_ACCESS_KEY_ID,
accessKeySecret: process.env.ALICLOUD_ACCESS_KEY_SECRET,
endpoint: process.env.ALICLOUD_ENDPOINT,
type: 'access_key'
}
}
10 changes: 9 additions & 1 deletion config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const path = require('path')

const awsConfig = require('./aws-config')
const azureConfig = require('./azure-config')
const alicloudConfig = require('./alicloud-config')
const gcpConfig = require('./gcp-config')
const envConfig = require('./environment')
const CustomResourceManager = require('../lib/custom-resource-manager')
Expand All @@ -18,6 +19,7 @@ const SystemManagerBackend = require('../lib/backends/system-manager-backend')
const VaultBackend = require('../lib/backends/vault-backend')
const AzureKeyVaultBackend = require('../lib/backends/azure-keyvault-backend')
const GCPSecretsManagerBackend = require('../lib/backends/gcp-secrets-manager-backend')
const AliCloudSecretsManagerBackend = require('../lib/backends/alicloud-secrets-manager-backend')

// Get document, or throw exception on error
// eslint-disable-next-line security/detect-non-literal-fs-filename
Expand Down Expand Up @@ -60,13 +62,19 @@ const gcpSecretsManagerBackend = new GCPSecretsManagerBackend({
client: gcpConfig.gcpSecretsManager(),
logger
})
const alicloudSecretsManagerBackend = new AliCloudSecretsManagerBackend({
credential: alicloudConfig.credential,
logger
})
const backends = {
// when adding a new backend, make sure to change the CRD property too
secretsManager: secretsManagerBackend,
systemManager: systemManagerBackend,
vault: vaultBackend,
azureKeyVault: azureKeyVaultBackend,
gcpSecretsManager: gcpSecretsManagerBackend
gcpSecretsManager: gcpSecretsManagerBackend,
alicloudSecretsManager: alicloudSecretsManagerBackend

}

// backwards compatibility
Expand Down
5 changes: 5 additions & 0 deletions crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ spec:
- vault
- azureKeyVault
- gcpSecretsManager
- alicloudSecretsManager
vaultRole:
type: string
vaultMountPoint:
Expand Down Expand Up @@ -103,6 +104,10 @@ spec:
backendType:
enum:
- gcpSecretsManager
- properties:
backendType:
enum:
- alicloudSecretsManager
anyOf:
- required:
- data
Expand Down
9 changes: 9 additions & 0 deletions examples/alicloudsecretsmanager-example.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: kubernetes-client.io/v1
kind: ExternalSecret
metadata:
name: secretsmanager-example
spec:
backendType: alicloudSecretsManager
data:
- key: akey
name: password
51 changes: 51 additions & 0 deletions lib/backends/alicloud-secrets-manager-backend.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
'use strict'

const KVBackend = require('./kv-backend')
const { default: Client, GetSecretValueRequest } = require('@alicloud/kms20160120')
/** Secrets Manager backend class. */
class AliCloudSecretsManagerBackend extends KVBackend {
/**
* Create Secrets manager backend.
* @param {Object} logger - Logger for logging stuff.
* @param {Object} credential - Secrets manager credential.
*/
constructor ({ logger, credential }) {
super({ logger })
this._credential = credential
}

_getClient ({ specOptions: { roleArn } }) {
const config = {
endpoint: this._credential.endpoint,
accessKeyId: this._credential.accessKeyId,
accessKeySecret: this._credential.accessKeySecret,
type: this._credential.type
}
if (roleArn) {
config.type = 'ram_role_arn'
config.roleArn = roleArn
}
return new Client(config)
}

/**
* Get secret property value from Alibaba Cloud KMS Secrets Manager.
* @param {string} key - Key used to store secret property value in Alibaba Cloud KMS Secrets Manager.
* @returns {Promise} Promise object representing secret property value.
*/

async _get ({ key, specOptions: { roleArn }, keyOptions: { versionStage } }) {
this._logger.info(`fetching secret ${key} on version stage ${versionStage} from AliCloud Secret Manager using role ${roleArn}`)
const getSecretValueRequest = new GetSecretValueRequest({
secretName: key,
versionStage: versionStage
})

const client = this._getClient({ specOptions: { roleArn } })
const value = await client.getSecretValue(getSecretValueRequest)

return value.secretData.toString('utf-8')
}
}

module.exports = AliCloudSecretsManagerBackend
45 changes: 45 additions & 0 deletions lib/backends/alicloud-secrets-manager-backend.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/* eslint-env mocha */
'use strict'

const { expect } = require('chai')
const sinon = require('sinon')

const AliCloudSecretsManagerBackend = require('./alicloud-secrets-manager-backend')

describe('AliCloudSecretsManagerBackend', () => {
let loggerMock
let clientMock
let aliCloudSecretsManagerBackend

const password = 'fakeSecretPropertyValue'
const secret = {
secretData: password
}
const key = 'password'

beforeEach(() => {
loggerMock = sinon.mock()
loggerMock.info = sinon.stub()
clientMock = sinon.mock()
clientMock.getSecretValue = sinon.stub().returns(secret)

aliCloudSecretsManagerBackend = new AliCloudSecretsManagerBackend({
credential: null,
logger: loggerMock
})
aliCloudSecretsManagerBackend._getClient = sinon.stub().returns(clientMock)
})

describe('_get', () => {
it('returns secret property value', async () => {
const specOptions = {}
const keyOptions = {}
const secretPropertyValue = await aliCloudSecretsManagerBackend._get({
key: key,
specOptions,
keyOptions
})
expect(secretPropertyValue).equals(password)
})
})
})
Loading

0 comments on commit cceb40b

Please sign in to comment.