|
| 1 | +[[gcp-secret-manager-backend]] |
| 2 | += Google Secret Manager Backend |
| 3 | + |
| 4 | +Spring Cloud Config Server can use link:https://cloud.google.com/secret-manager[Google Cloud Secret Manager] as an `EnvironmentRepository`. |
| 5 | +Secrets are exposed as a `PropertySource` whose entries are built from secret payloads. |
| 6 | + |
| 7 | +== Enabling the backend |
| 8 | + |
| 9 | +The repository is registered when all of the following hold: |
| 10 | + |
| 11 | +* The Spring profile `secret-manager` is active. |
| 12 | +* `com.google.cloud.secretmanager.v1.SecretManagerServiceClient` is on the classpath (typically by adding `google-cloud-secretmanager`). |
| 13 | + |
| 14 | +[source,xml,indent=0] |
| 15 | +.pom.xml |
| 16 | +---- |
| 17 | +<dependency> |
| 18 | + <groupId>com.google.cloud</groupId> |
| 19 | + <artifactId>google-cloud-secretmanager</artifactId> |
| 20 | +</dependency> |
| 21 | +---- |
| 22 | + |
| 23 | +If you use `spring-cloud-config-server` without its optional Google libraries, add the artifacts your build needs explicitly. |
| 24 | +The permission check described below uses the Cloud Resource Manager API; include `google-api-services-cloudresourcemanager` and `google-auth-library-oauth2-http` when you rely on that behavior. |
| 25 | + |
| 26 | +[source,yaml] |
| 27 | +---- |
| 28 | +spring: |
| 29 | + profiles: |
| 30 | + active: secret-manager |
| 31 | + cloud: |
| 32 | + config: |
| 33 | + server: |
| 34 | + gcp-secret-manager: |
| 35 | + order: 0 |
| 36 | + application-label: application |
| 37 | + profile-label: profile |
| 38 | + service-account: /path/to/service-account.json |
| 39 | + token-mandatory: true |
| 40 | + version: 1 |
| 41 | +---- |
| 42 | + |
| 43 | +Configuration properties are bound under `spring.cloud.config.server.gcp-secret-manager`: |
| 44 | + |
| 45 | +|=== |
| 46 | +| Property | Default | Description |
| 47 | + |
| 48 | +| `order` |
| 49 | +| Same as other environment repositories |
| 50 | +| Order in a composite repository (`org.springframework.core.Ordered`). |
| 51 | + |
| 52 | +| `application-label` |
| 53 | +| `application` |
| 54 | +| Secret Manager label key used to match the Config Server `\{application}` name (see <<gcp-secret-manager-mapping>>). |
| 55 | + |
| 56 | +| `profile-label` |
| 57 | +| `profile` |
| 58 | +| Secret Manager label key used to match each active profile (see <<gcp-secret-manager-mapping>>). |
| 59 | + |
| 60 | +| `service-account` |
| 61 | +| _empty_ |
| 62 | +| Path to a Google Cloud service account JSON key file. If set, the Secret Manager client uses these credentials. If unset, the client uses https://cloud.google.com/docs/authentication/application-default-credentials[Application Default Credentials] (for example the metadata server on Google Compute Engine). |
| 63 | + |
| 64 | +| `token-mandatory` |
| 65 | +| `true` |
| 66 | +| When `true`, secrets are only loaded if <<gcp-secret-manager-permission-check>> succeeds. When `false`, that check is skipped and secrets are loaded using only the server-side Secret Manager credentials. |
| 67 | + |
| 68 | +| `version` |
| 69 | +| `1` |
| 70 | +| API access strategy version (only `1` is supported). |
| 71 | + |
| 72 | +|=== |
| 73 | + |
| 74 | +[[gcp-secret-manager-mapping]] |
| 75 | +== Mapping applications and profiles |
| 76 | + |
| 77 | +The server lists secrets in the Google Cloud project (see <<gcp-secret-manager-project>>) and filters them for each Config Client request. |
| 78 | + |
| 79 | +* **Label-based matching (default):** A secret is included for a given `\{application}` and profile when its Secret Manager labels match, case-insensitively, on the configured keys (`application-label` and `profile-label`). If a label is missing on the secret, the code treats it as the default string `application` or `profile` respectively (same keys as the property names), so unlabeled secrets align with those defaults. |
| 80 | + |
| 81 | +* **Prefix-based matching:** If the HTTP request includes a non-empty `X-Secret-Prefix` header, secrets whose *short* name (the segment after `projects/.../secrets/`) starts with that prefix are also included. The prefix is stripped from the property key. This path can be combined with label filtering logic in the same loop (see `GoogleSecretManagerEnvironmentRepository`). |
| 82 | + |
| 83 | +The repository always ensures the `default` profile is considered before additional profiles (similar to other backends): if the client does not send `default` first, the server prepends it. |
| 84 | + |
| 85 | +For each matching secret, the property **value** is the payload of the **latest enabled** secret version, where “latest” is determined by comparing numeric version IDs (not the Secret Manager “latest” alias). |
| 86 | + |
| 87 | +Property sources are named `gsm:\{application}-\{profile}`. |
| 88 | + |
| 89 | +The Config Server `\{label}` path segment is accepted on the API but **this backend does not use it** to select secrets; version selection is based on secret versions as described above. |
| 90 | + |
| 91 | +[[gcp-secret-manager-project]] |
| 92 | +== Resolving the Google Cloud project |
| 93 | + |
| 94 | +Secret Manager API calls are scoped to a project ID. The server obtains it in this order: |
| 95 | + |
| 96 | +. The `X-Project-ID` HTTP header on the incoming Config Client request, if present. |
| 97 | +. Otherwise, a GET to `http://metadata.google.internal/computeMetadata/v1/project/project-id` with header `Metadata-Flavor: Google` (works when the Config Server runs on Google Compute Engine or similar). |
| 98 | + |
| 99 | +If neither source is available (for example local development without metadata), configure clients to send `X-Project-ID` or run the server where metadata is available. |
| 100 | + |
| 101 | +[[gcp-secret-manager-credentials]] |
| 102 | +== Credentials for listing and reading secrets |
| 103 | + |
| 104 | +Listing secrets and accessing secret values uses `SecretManagerServiceClient` only: |
| 105 | + |
| 106 | +* With `service-account` set: credentials from that JSON file. |
| 107 | +* Without it: Application Default Credentials. |
| 108 | + |
| 109 | +The OAuth access token in `X-Config-Token` is **not** passed to the Secret Manager client. Those credentials are always the server’s (or the key file’s), independent of the client token. |
| 110 | + |
| 111 | +[[gcp-secret-manager-permission-check]] |
| 112 | +== Optional permission check (`token-mandatory`) |
| 113 | + |
| 114 | +When `token-mandatory` is `true` (default), before loading any secrets the server calls `checkRemotePermissions()`: |
| 115 | + |
| 116 | +. It reads the access token from the `X-Config-Token` HTTP header (required for this check; a missing or invalid header causes the check to fail). |
| 117 | +. It uses the Cloud Resource Manager REST API `projects.testIamPermissions` for the target project with the permission `secretmanager.versions.access`. |
| 118 | +. If that permission is reported for the token, the check passes and secrets are loaded. If not, or if the API call fails, the check fails and **no** GSM-backed property sources are added for that request. |
| 119 | + |
| 120 | +So the token in `X-Config-Token` acts as a **gate** for whether GSM data is returned, while **reading** secrets still uses <<gcp-secret-manager-credentials,the server’s Secret Manager credentials>>. The principal that passes IAM testing and the principal used for Secret Manager API calls can differ. |
| 121 | + |
| 122 | +When `token-mandatory` is `false`, this remote check is not performed and secrets are loaded solely subject to server credentials and Secret Manager data access. |
| 123 | + |
| 124 | +== HTTP headers (Config Client to Config Server) |
| 125 | + |
| 126 | +|=== |
| 127 | +| Header | Required when | Purpose |
| 128 | + |
| 129 | +| `X-Config-Token` |
| 130 | +| `token-mandatory` is `true` for the permission check |
| 131 | +| Bearer-style access token used only for Cloud Resource Manager `testIamPermissions`. |
| 132 | + |
| 133 | +| `X-Project-ID` |
| 134 | +| Recommended when not running on GCP with metadata |
| 135 | +| Google Cloud project ID for Secret Manager and IAM checks. |
| 136 | + |
| 137 | +| `X-Secret-Prefix` |
| 138 | +| Optional |
| 139 | +| Restricts which secrets contribute properties when matching by name prefix (see <<gcp-secret-manager-mapping>>). |
| 140 | + |
| 141 | +|=== |
| 142 | + |
| 143 | +Header names are case-insensitive per HTTP; the implementation uses `X-Config-Token`, `X-Project-ID`, and `X-Secret-Prefix`. |
0 commit comments