Repository: godaddy/kubernetes-external-secrets
Pull request #312 (2020-03-27 03:50:45)

feat: add GCP support

silasbw: Same as https://github.com/godaddy/kubernetes-external-secrets/pull/288 (but with conflicts resolved).  Thanks @jdamata :clinking_glasses: !

Patch:
diff --git a/README.md b/README.md
index bc24cdc..38ad9d4 100644
--- a/README.md
+++ b/README.md
@@ -389,6 +389,27 @@ spec:
       property: value
 ```
 
+### GCP Secret Manager
+
+kubernetes-external-secrets supports fetching secrets from [GCP Secret Manager](https://cloud.google.com/solutions/secrets-management)
+
+A service account is required to grant the controller access to pull secrets. Instructions are her: [Enable Workload Identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity#enable_workload_identity_on_a_new_cluster)
+
+Alternatively you can create and mount a kubernetes secret containing google service account credentials and set the GOOGLE_APPLICATION_CREDENTIALS env variable.
+
+```yml
+apiVersion: kubernetes-client.io/v1
+kind: ExternalSecret
+metadata:
+  name: gcp-secrets-manager-example
+spec:
+  backendType: gcpSecretsManager
+  data:
+    - key: projects/111122223333/secrets/my-secret/versions/latest
+      name: password
+      property: value
+```
+
 ## Metrics
 
 kubernetes-external-secrets exposes the following metrics over a prometheus endpoint:
diff --git a/charts/kubernetes-external-secrets/values.yaml b/charts/kubernetes-external-secrets/values.yaml
index a37c4f2..b42e668 100644
--- a/charts/kubernetes-external-secrets/values.yaml
+++ b/charts/kubernetes-external-secrets/values.yaml
@@ -9,6 +9,7 @@ env:
   LOG_LEVEL: info
   METRICS_PORT: 3001
   VAULT_ADDR: http://127.0.0.1:8200
+# GOOGLE_APPLICATION_CREDENTIALS: /app/gcp-creds.json
 
 # Create environment variables from existing k8s secrets
 # envVarsFromSecret:
@@ -30,9 +31,9 @@ env:
 
 # Create files from existing k8s secrets
 # filesFromSecret:
-#   examplefile:
-#     secret: secretname
-#     mountPath: /a/mount/point/
+#   gcp-creds:
+#     secret: gcp-creds
+#     mountPath: /app/gcp-creds.json
 
 rbac:
   # Specifies whether RBAC resources should be created
diff --git a/config/gcp-config.js b/config/gcp-config.js
new file mode 100644
index 0000000..83bcbf5
--- /dev/null
+++ b/config/gcp-config.js
@@ -0,0 +1,15 @@
+'use strict'
+
+const { SecretManagerServiceClient } = require('@google-cloud/secret-manager')
+
+// First, ADC checks to see if the environment variable GOOGLE_APPLICATION_CREDENTIALS is set.
+// If the variable is set, ADC uses the service account file that the variable points to
+// If the environment variable isn't set, ADC uses the default service account that the Kubernetes Engine
+// provides, for applications that run on those services
+
+module.exports = {
+  gcpSecretsManager: () => {
+    const client = new SecretManagerServiceClient()
+    return client
+  }
+}
diff --git a/config/index.js b/config/index.js
index 9c2b29a..96fb16f 100644
--- a/config/index.js
+++ b/config/index.js
@@ -10,12 +10,14 @@ const path = require('path')
 
 const awsConfig = require('./aws-config')
 const azureConfig = require('./azure-config')
+const gcpConfig = require('./gcp-config')
 const envConfig = require('./environment')
 const CustomResourceManager = require('../lib/custom-resource-manager')
 const SecretsManagerBackend = require('../lib/backends/secrets-manager-backend')
 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')
 
 // Get document, or throw exception on error
 // eslint-disable-next-line security/detect-non-literal-fs-filename
@@ -54,12 +56,17 @@ const azureKeyVaultBackend = new AzureKeyVaultBackend({
   credential: azureConfig.azureKeyVault(),
   logger
 })
+const gcpSecretsManagerBackend = new GCPSecretsManagerBackend({
+  client: gcpConfig.gcpSecretsManager(),
+  logger
+})
 const backends = {
   // when adding a new backend, make sure to change the CRD property too
   secretsManager: secretsManagerBackend,
   systemManager: systemManagerBackend,
   vault: vaultBackend,
-  azureKeyVault: azureKeyVaultBackend
+  azureKeyVault: azureKeyVaultBackend,
+  gcpSecretsManager: gcpSecretsManagerBackend
 }
 
 // backwards compatibility
diff --git a/crd.yaml b/crd.yaml
index 30467df..46fba6e 100644
--- a/crd.yaml
+++ b/crd.yaml
@@ -43,6 +43,7 @@ spec:
                 - systemManager
                 - vault
                 - azureKeyVault
+                - gcpSecretsManager
             vaultRole:
               type: string
             vaultMountPoint:
@@ -92,6 +93,10 @@ spec:
                     - azureKeyVault
               required:
                 - keyVaultName
+            - properties:
+                backendType:
+                  enum:
+                    - gcpSecretsManager
           anyOf:
             - required:
               - data
diff --git a/examples/gcpsecretsmanager-example copy.yaml b/examples/gcpsecretsmanager-example copy.yaml
new file mode 100644
index 0000000..0319050
--- /dev/null
+++ b/examples/gcpsecretsmanager-example copy.yaml
@@ -0,0 +1,10 @@
+apiVersion: kubernetes-client.io/v1
+kind: ExternalSecret
+metadata:
+  name: gcp-secrets-manager-example
+spec:
+  backendType: gcpSecretsManager
+  data:
+    - key: projects/111122223333/secrets/my-secret/versions/latest
+      name: password
+      property: value
diff --git a/lib/backends/gcp-secrets-manager-backend.js b/lib/backends/gcp-secrets-manager-backend.js
new file mode 100644
index 0000000..9bdf9e7
--- /dev/null
+++ b/lib/backends/gcp-secrets-manager-backend.js
@@ -0,0 +1,32 @@
+'use strict'
+
+const KVBackend = require('./kv-backend')
+
+/** GCP Secrets Manager backend class. */
+class GCPSecretsManagerBackend extends KVBackend {
+  /**
+   * Create Secrets manager backend.
+   * @param {Object} logger - Logger for logging stuff.
+   * @param {Object} client - Secrets manager client.
+   */
+  constructor ({ logger, client }) {
+    super({ logger })
+    this._client = client
+  }
+
+  /**
+   * Get secret property value from GCP Secrets Manager.
+   * @param {string} key - Key used to store secret property value in GCP Secrets Manager.
+   * @returns {Promise} Promise object representing secret property value.
+   */
+  async _get ({ key }) {
+    this._logger.info(`fetching secret ${key} from GCP Secret Manager`)
+    const version = await this._client.accessSecretVersion({
+      name: key
+    })
+    const secret = { value: version[0].payload.data.toString('utf8') }
+    return JSON.stringify(secret)
+  }
+}
+
+module.exports = GCPSecretsManagerBackend
diff --git a/package-lock.json b/package-lock.json
index 20efda9..33c8756 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -307,6 +307,31 @@
         "to-fast-properties": "^2.0.0"
       }
     },
+    "@google-cloud/secret-manager": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@google-cloud/secret-manager/-/secret-manager-1.2.1.tgz",
+      "integrity": "sha512-nYfurXYAFi09/xHImqB9erQ4qtWsYTWBcOO5SDnJ3BJ2x7n4c02Qgy6wA7v7nHBY0BDwBKySI+Y5tbtGatOQOA==",
+      "requires": {
+        "google-gax": "^1.12.0"
+      }
+    },
+    "@grpc/grpc-js": {
+      "version": "0.6.17",
+      "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-0.6.17.tgz",
+      "integrity": "sha512-NDEhTBTo0b1/3YzD46wkm1hIPIyuZptoVRiuqF9nuWK+47Di5gQbNipTtjY7eXT1qRwrdFvMfo1utVMS3bifxg==",
+      "requires": {
+        "semver": "^6.2.0"
+      }
+    },
+    "@grpc/proto-loader": {
+      "version": "0.5.3",
+      "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.5.3.tgz",
+      "integrity": "sha512-8qvUtGg77G2ZT2HqdqYoM/OY97gQd/0crSG34xNmZ4ZOsv3aQT/FQV9QfZPazTGna6MIoyUd+u6AxsoZjJ/VMQ==",
+      "requires": {
+        "lodash.camelcase": "^4.3.0",
+        "protobufjs": "^6.8.6"
+      }
+    },
     "@istanbuljs/load-nyc-config": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz",
@@ -423,6 +448,60 @@
       "resolved": "https://registry.npmjs.org/@opentelemetry/types/-/types-0.2.0.tgz",
       "integrity": "sha512-GtwNB6BNDdsIPAYEdpp3JnOGO/3AJxjPvny53s3HERBdXSJTGQw8IRhiaTEX0b3w9P8+FwFZde4k+qkjn67aVw=="
     },
+    "@protobufjs/aspromise": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+      "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78="
+    },
+    "@protobufjs/base64": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+      "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
+    },
+    "@protobufjs/codegen": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+      "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
+    },
+    "@protobufjs/eventemitter": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+      "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A="
+    },
+    "@protobufjs/fetch": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+      "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=",
+      "requires": {
+        "@protobufjs/aspromise": "^1.1.1",
+        "@protobufjs/inquire": "^1.1.0"
+      }
+    },
+    "@protobufjs/float": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+      "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E="
+    },
+    "@protobufjs/inquire": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+      "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik="
+    },
+    "@protobufjs/path": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+      "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0="
+    },
+    "@protobufjs/pool": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+      "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q="
+    },
+    "@protobufjs/utf8": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+      "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA="
+    },
     "@sindresorhus/is": {
       "version": "0.7.0",
       "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz",
@@ -475,11 +554,24 @@
       "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
       "dev": true
     },
+    "@types/fs-extra": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.0.tgz",
+      "integrity": "sha512-UoOfVEzAUpeSPmjm7h1uk5MH6KZma2z2O7a75onTGjnNvAvMVrPzPL/vBbT65iIGHWj6rokwfmYcmxmlSf2uwg==",
+      "requires": {
+        "@types/node": "*"
+      }
+    },
     "@types/js-yaml": {
       "version": "3.12.1",
       "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.12.1.tgz",
       "integrity": "sha512-SGGAhXLHDx+PK4YLNcNGa6goPf9XRWQNAUUbffkwVGGXIxmDKWyGGL4inzq2sPmExu431Ekb9aEMn9BkPqEYFA=="
     },
+    "@types/long": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
+      "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w=="
+    },
     "@types/node": {
       "version": "10.17.5",
       "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.5.tgz",
@@ -558,6 +650,14 @@
       "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
       "dev": true
     },
+    "abort-controller": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+      "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+      "requires": {
+        "event-target-shim": "^5.0.0"
+      }
+    },
     "accepts": {
       "version": "1.3.7",
       "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
@@ -579,6 +679,14 @@
       "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==",
       "dev": true
     },
+    "agent-base": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.0.tgz",
+      "integrity": "sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==",
+      "requires": {
+        "debug": "4"
+      }
+    },
     "aggregate-error": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-1.0.0.tgz",
@@ -858,6 +966,11 @@
         "tweetnacl": "^0.14.3"
       }
     },
+    "bignumber.js": {
+      "version": "7.2.1",
+      "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz",
+      "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ=="
+    },
     "binary-extensions": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz",
@@ -1825,7 +1938,6 @@
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
       "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
-      "dev": true,
       "requires": {
         "ms": "^2.1.1"
       }
@@ -2035,6 +2147,17 @@
       "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
       "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
     },
+    "duplexify": {
+      "version": "3.7.1",
+      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
+      "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
+      "requires": {
+        "end-of-stream": "^1.0.0",
+        "inherits": "^2.0.1",
+        "readable-stream": "^2.0.0",
+        "stream-shift": "^1.0.0"
+      }
+    },
     "ecc-jsbn": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
@@ -2447,6 +2570,11 @@
       "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
       "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
     },
+    "event-target-shim": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+      "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
+    },
     "events": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
@@ -2605,6 +2733,11 @@
       "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz",
       "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA=="
     },
+    "fast-text-encoding": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.0.tgz",
+      "integrity": "sha512-R9bHCvweUxxwkDwhjav5vxpFvdPGlVngtqmx4pIZfSUhM/Q4NiIUHB456BAf+Q1Nwu3HEZYONtu+Rya+af4jiQ=="
+    },
     "figures": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/figures/-/figures-3.1.0.tgz",
@@ -2933,6 +3066,34 @@
       "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
       "dev": true
     },
+    "gaxios": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-2.3.1.tgz",
+      "integrity": "sha512-DQOesWEx59/bm63lTX0uHDDXpGTW9oKqNsoigwCoRe2lOb5rFqxzHjLTa6aqEBecLcz69dHLw7rbS068z1fvIQ==",
+      "requires": {
+        "abort-controller": "^3.0.0",
+        "extend": "^3.0.2",
+        "https-proxy-agent": "^5.0.0",
+        "is-stream": "^2.0.0",
+        "node-fetch": "^2.3.0"
+      },
+      "dependencies": {
+        "is-stream": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
+          "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw=="
+        }
+      }
+    },
+    "gcp-metadata": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-3.3.1.tgz",
+      "integrity": "sha512-RrASg1HaVAxoB9Q/8sYfJ++v9PMiiqIgOrOxZeagMgS4osZtICT1lKBx2uvzYgwetxj8i6K99Z0iuKMg7WraTg==",
+      "requires": {
+        "gaxios": "^2.1.0",
+        "json-bigint": "^0.3.0"
+      }
+    },
     "get-caller-file": {
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
@@ -3397,6 +3558,85 @@
       "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
       "dev": true
     },
+    "google-auth-library": {
+      "version": "5.10.0",
+      "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.10.0.tgz",
+      "integrity": "sha512-Kfa0GDYYzaRGtvegI64c+oF8Adv3ZW8hWfOegiu53/h4etBdsdXI3uox/TKlyIHKFu8/YhpKv3Z30sJH986YKA==",
+      "requires": {
+        "arrify": "^2.0.0",
+        "base64-js": "^1.3.0",
+        "ecdsa-sig-formatter": "^1.0.11",
+        "fast-text-encoding": "^1.0.0",
+        "gaxios": "^2.1.0",
+        "gcp-metadata": "^3.3.0",
+        "gtoken": "^4.1.0",
+        "jws": "^4.0.0",
+        "lru-cache": "^5.0.0"
+      },
+      "dependencies": {
+        "arrify": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
+          "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug=="
+        },
+        "jwa": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
+          "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
+          "requires": {
+            "buffer-equal-constant-time": "1.0.1",
+            "ecdsa-sig-formatter": "1.0.11",
+            "safe-buffer": "^5.0.1"
+          }
+        },
+        "jws": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
+          "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+          "requires": {
+            "jwa": "^2.0.0",
+            "safe-buffer": "^5.0.1"
+          }
+        }
+      }
+    },
+    "google-gax": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-1.14.1.tgz",
+      "integrity": "sha512-lAvILUMnXL+BVSSlbzwpGzs3ZP2r+b1l44zeDTRWceejDgyZORKdPEEhtUw49x9CVwxpPx02+v0yktqnRhUD1A==",
+      "requires": {
+        "@grpc/grpc-js": "^0.6.12",
+        "@grpc/proto-loader": "^0.5.1",
+        "@types/fs-extra": "^8.0.1",
+        "@types/long": "^4.0.0",
+        "abort-controller": "^3.0.0",
+        "duplexify": "^3.6.0",
+        "google-auth-library": "^5.0.0",
+        "is-stream-ended": "^0.1.4",
+        "lodash.at": "^4.6.0",
+        "lodash.has": "^4.5.2",
+        "node-fetch": "^2.6.0",
+        "protobufjs": "^6.8.8",
+        "retry-request": "^4.0.0",
+        "semver": "^6.0.0",
+        "walkdir": "^0.4.0"
+      }
+    },
+    "google-p12-pem": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.4.tgz",
+      "integrity": "sha512-S4blHBQWZRnEW44OcR7TL9WR+QCqByRvhNDZ/uuQfpxywfupikf/miba8js1jZi6ZOGv5slgSuoshCWh6EMDzg==",
+      "requires": {
+        "node-forge": "^0.9.0"
+      },
+      "dependencies": {
+        "node-forge": {
+          "version": "0.9.1",
+          "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.1.tgz",
+          "integrity": "sha512-G6RlQt5Sb4GMBzXvhfkeFmbqR6MzhtnT7VTHuLadjkii3rdYHNdw0m8zA4BTxVIh68FicCQ2NSUANpsqkr9jvQ=="
+        }
+      }
+    },
     "got": {
       "version": "8.3.2",
       "resolved": "https://registry.npmjs.org/got/-/got-8.3.2.tgz",
@@ -3440,6 +3680,43 @@
       "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
       "dev": true
     },
+    "gtoken": {
+      "version": "4.1.4",
+      "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.1.4.tgz",
+      "integrity": "sha512-VxirzD0SWoFUo5p8RDP8Jt2AGyOmyYcT/pOUgDKJCK+iSw0TMqwrVfY37RXTNmoKwrzmDHSk0GMT9FsgVmnVSA==",
+      "requires": {
+        "gaxios": "^2.1.0",
+        "google-p12-pem": "^2.0.0",
+        "jws": "^4.0.0",
+        "mime": "^2.2.0"
+      },
+      "dependencies": {
+        "jwa": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
+          "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
+          "requires": {
+            "buffer-equal-constant-time": "1.0.1",
+            "ecdsa-sig-formatter": "1.0.11",
+            "safe-buffer": "^5.0.1"
+          }
+        },
+        "jws": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
+          "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+          "requires": {
+            "jwa": "^2.0.0",
+            "safe-buffer": "^5.0.1"
+          }
+        },
+        "mime": {
+          "version": "2.4.4",
+          "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
+          "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA=="
+        }
+      }
+    },
     "handlebars": {
       "version": "4.5.3",
       "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz",
@@ -3576,6 +3853,15 @@
         "sshpk": "^1.7.0"
       }
     },
+    "https-proxy-agent": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
+      "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
+      "requires": {
+        "agent-base": "6",
+        "debug": "4"
+      }
+    },
     "iconv-lite": {
       "version": "0.4.24",
       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -3853,6 +4139,11 @@
       "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
       "dev": true
     },
+    "is-stream-ended": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz",
+      "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw=="
+    },
     "is-symbol": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
@@ -4131,6 +4422,14 @@
       "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
       "dev": true
     },
+    "json-bigint": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz",
+      "integrity": "sha1-DM2RLEuCcNBfBW+9E4FLU9OCWx4=",
+      "requires": {
+        "bignumber.js": "^7.0.0"
+      }
+    },
     "json-buffer": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
@@ -4332,6 +4631,16 @@
       "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=",
       "dev": true
     },
+    "lodash.at": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/lodash.at/-/lodash.at-4.6.0.tgz",
+      "integrity": "sha1-k83OZk8KGZTqM9181A4jr9EbD/g="
+    },
+    "lodash.camelcase": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+      "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
+    },
     "lodash.clonedeep": {
       "version": "4.5.0",
       "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
@@ -4349,6 +4658,11 @@
       "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=",
       "dev": true
     },
+    "lodash.has": {
+      "version": "4.5.2",
+      "resolved": "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz",
+      "integrity": "sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI="
+    },
     "lodash.ismatch": {
       "version": "4.4.0",
       "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz",
@@ -5697,6 +6011,26 @@
         "tdigest": "^0.1.1"
       }
     },
+    "protobufjs": {
+      "version": "6.8.8",
+      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz",
+      "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==",
+      "requires": {
+        "@protobufjs/aspromise": "^1.1.2",
+        "@protobufjs/base64": "^1.1.2",
+        "@protobufjs/codegen": "^2.0.4",
+        "@protobufjs/eventemitter": "^1.1.0",
+        "@protobufjs/fetch": "^1.1.0",
+        "@protobufjs/float": "^1.0.2",
+        "@protobufjs/inquire": "^1.1.0",
+        "@protobufjs/path": "^1.1.2",
+        "@protobufjs/pool": "^1.1.0",
+        "@protobufjs/utf8": "^1.1.0",
+        "@types/long": "^4.0.0",
+        "@types/node": "^10.1.0",
+        "long": "^4.0.0"
+      }
+    },
     "proxy-addr": {
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
@@ -6023,6 +6357,15 @@
       "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
       "dev": true
     },
+    "retry-request": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.1.1.tgz",
+      "integrity": "sha512-BINDzVtLI2BDukjWmjAIRZ0oglnCAkpP2vQjM3jdLhmT62h0xnQgciPwBRDAvHqpkPT2Wo1XuUyLyn6nbGrZQQ==",
+      "requires": {
+        "debug": "^4.1.1",
+        "through2": "^3.0.1"
+      }
+    },
     "rimraf": {
       "version": "2.6.3",
       "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
@@ -6077,8 +6420,7 @@
     "semver": {
       "version": "6.3.0",
       "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
-      "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
-      "dev": true
+      "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
     },
     "semver-diff": {
       "version": "2.1.0",
@@ -6604,6 +6946,11 @@
       "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
       "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
     },
+    "stream-shift": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
+      "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ=="
+    },
     "strict-uri-encode": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
@@ -6860,7 +7207,6 @@
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz",
       "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==",
-      "dev": true,
       "requires": {
         "readable-stream": "2 || 3"
       }
@@ -7195,6 +7541,11 @@
         "extsprintf": "^1.2.0"
       }
     },
+    "walkdir": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.4.1.tgz",
+      "integrity": "sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ=="
+    },
     "which": {
       "version": "1.3.1",
       "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
diff --git a/package.json b/package.json
index 5eb77fa..bbfe349 100644
--- a/package.json
+++ b/package.json
@@ -32,6 +32,7 @@
     "@azure/identity": "^1.0.2",
     "@azure/keyvault-secrets": "^4.0.2",
     "aws-sdk": "^2.628.0",
+    "@google-cloud/secret-manager": "^1.2.1",
     "express": "^4.17.1",
     "js-yaml": "^3.13.1",
     "json-stream": "^1.0.0",


Test patch:
diff --git a/lib/backends/gcp-secrets-manager-backend.test.js b/lib/backends/gcp-secrets-manager-backend.test.js
new file mode 100644
index 0000000..5c25c2e
--- /dev/null
+++ b/lib/backends/gcp-secrets-manager-backend.test.js
@@ -0,0 +1,37 @@
+/* eslint-env mocha */
+'use strict'
+
+const { expect } = require('chai')
+const sinon = require('sinon')
+
+const GCPSecretsManagerBackend = require('./gcp-secrets-manager-backend')
+
+describe('GCPSecretsManagerBackend', () => {
+  let loggerMock
+  let clientMock
+  let gcpSecretsManagerBackend
+  const key = 'password'
+  const version = [{ name: 'projects/111122223333/secrets/password/versions/1', payload: { data: Buffer.from('test', 'utf8') } }, null, null]
+  const secret = '{"value":"test"}'
+
+  beforeEach(() => {
+    loggerMock = sinon.mock()
+    loggerMock.info = sinon.stub()
+    clientMock = sinon.mock()
+    clientMock.accessSecretVersion = sinon.stub().returns(version)
+
+    gcpSecretsManagerBackend = new GCPSecretsManagerBackend({
+      logger: loggerMock,
+      client: clientMock
+    })
+  })
+
+  describe('_get', () => {
+    it('returns secret property value', async () => {
+      const secretPropertyValue = await gcpSecretsManagerBackend._get({
+        key: key
+      })
+      expect(secretPropertyValue).equals(secret)
+    })
+  })
+})