Secret Manager
floci-gcp emulates Google Cloud Secret Manager over gRPC and REST using the real google.cloud.secretmanager.v1 protocol.
Configuration
| Variable | Default | Description |
|---|---|---|
FLOCI_GCP_SERVICES_SECRETMANAGER_ENABLED |
true |
Enable/disable Secret Manager |
Emulator Variable
The GCP Secret Manager SDK uses this variable to route requests to floci-gcp instead of secretmanager.googleapis.com.
IAM enforcement
With FLOCI_GCP_SERVICES_IAM_AUTHORIZATION_MODE=enforce, Floci-issued
service-account credentials are checked on the implemented Secret Manager v1
REST JSON and gRPC methods. Anonymous and external credentials bypass these
checks. The default disabled mode does not enforce policies.
| Methods | Permission target |
|---|---|
| Create and list secrets | Project |
| Get, update, delete, and secret IAM policy methods | Secret |
| Add and list versions | Secret |
| Get, access, enable, disable, and destroy versions | Version, inheriting secret and project policies |
Payload access requires secretmanager.versions.access separately from version
metadata access. testIamPermissions reports only permissions granted to the
caller. Denials return HTTP 403 or gRPC PERMISSION_DENIED.
| Role | Implemented permission scope |
|---|---|
roles/secretmanager.admin |
All implemented secret, version, payload, and policy methods |
roles/secretmanager.editor |
Secret and version management, metadata, and policy reads; excludes payload access and policy writes |
roles/secretmanager.viewer |
Secret and version metadata and policy reads |
roles/secretmanager.secretAccessor |
Version payload access only |
roles/secretmanager.secretVersionAdder |
Add a version to an existing secret |
roles/secretmanager.secretVersionManager |
Add, get, list, enable, disable, and destroy versions; excludes payload access |
Project basic roles contribute their Secret Manager permissions.
Version 3 bindings can condition grants on resource.service, resource.type,
resource.name, and request.time using the supported condition expressions.
Create checks the parent project, so its resource.name is
projects/{project}. Version checks use the full version name.
Regional projects/*/locations/* paths, v1beta1, and rotation methods are
not implemented. Organization and folder policies are not evaluated.
Quick Start
export SECRET_MANAGER_EMULATOR_HOST=localhost:4588
gcloud config set project floci-local
# Create a secret
gcloud secrets create my-secret --replication-policy=automatic
# Add a version
echo -n "my-secret-value" | gcloud secrets versions add my-secret --data-file=-
# Access the latest version
gcloud secrets versions access latest --secret=my-secret
# List secrets
gcloud secrets list
ManagedChannel channel = ManagedChannelBuilder
.forTarget("localhost:4588")
.usePlaintext()
.build();
SecretManagerServiceClient client = SecretManagerServiceClient.create(
SecretManagerServiceSettings.newBuilder()
.setTransportChannelProvider(
FixedTransportChannelProvider.create(GrpcTransportChannel.create(channel)))
.setCredentialsProvider(NoCredentialsProvider.create())
.build());
String projectId = "floci-local";
String secretId = "my-secret";
// Create secret
Secret secret = Secret.newBuilder()
.setReplication(Replication.newBuilder()
.setAutomatic(Replication.Automatic.getDefaultInstance()))
.build();
Secret createdSecret = client.createSecret(
ProjectName.of(projectId),
secretId,
secret);
// Add version
SecretPayload payload = SecretPayload.newBuilder()
.setData(ByteString.copyFromUtf8("my-secret-value"))
.build();
client.addSecretVersion(createdSecret.getName(), payload);
// Access latest version
AccessSecretVersionResponse response = client.accessSecretVersion(
SecretVersionName.of(projectId, secretId, "latest"));
System.out.println(response.getPayload().getData().toStringUtf8());
import os
os.environ["SECRET_MANAGER_EMULATOR_HOST"] = "localhost:4588"
from google.cloud import secretmanager
client = secretmanager.SecretManagerServiceClient()
project = "projects/floci-local"
# Create secret
secret = client.create_secret(request={
"parent": project,
"secret_id": "my-secret",
"secret": {
"replication": {"automatic": {}},
},
})
# Add version
version = client.add_secret_version(request={
"parent": secret.name,
"payload": {"data": b"my-secret-value"},
})
# Access latest version
response = client.access_secret_version(request={
"name": f"{project}/secrets/my-secret/versions/latest",
})
print(response.payload.data.decode("UTF-8"))
process.env.SECRET_MANAGER_EMULATOR_HOST = "localhost:4588";
import { SecretManagerServiceClient } from "@google-cloud/secret-manager";
const client = new SecretManagerServiceClient();
const projectId = "floci-local";
// Create secret
const [secret] = await client.createSecret({
parent: `projects/${projectId}`,
secretId: "my-secret",
secret: { replication: { automatic: {} } },
});
// Add version
await client.addSecretVersion({
parent: secret.name,
payload: { data: Buffer.from("my-secret-value") },
});
// Access latest version
const [version] = await client.accessSecretVersion({
name: `projects/${projectId}/secrets/my-secret/versions/latest`,
});
console.log(version.payload.data.toString());
Secret Versions
Each AddSecretVersion call creates a new version. Version numbers are sequential starting from 1. The alias latest always resolves to the most recently added version.
# List versions
gcloud secrets versions list my-secret
# Access specific version
gcloud secrets versions access 2 --secret=my-secret
# Disable a version
gcloud secrets versions disable 1 --secret=my-secret
# Destroy a version
gcloud secrets versions destroy 1 --secret=my-secret
IAM Bindings
# Grant access to a secret
gcloud secrets add-iam-policy-binding my-secret \
--member="serviceAccount:my-sa@floci-local.iam.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
Secret-level IAM policies persist across GetIamPolicy / SetIamPolicy calls,
including etag-based read-modify-write flows used by Terraform and OpenTofu.
Deleting a secret clears its versions and policy. Cleanup intent is persisted
before deletion, so interrupted cleanup is resumed before the emulator serves
requests after restart or recreates the same secret name. Policies are stored
but not enforced: a caller that would be denied by IAM in GCP can still access
emulator resources.
Supported Operations
CreateSecretGetSecretUpdateSecretDeleteSecretListSecretsAddSecretVersionGetSecretVersionAccessSecretVersionDisableSecretVersionEnableSecretVersionDestroySecretVersionListSecretVersionsGetIamPolicySetIamPolicyTestIamPermissions
Limitations
- Regional secrets (
projects/{project}/locations/{location}/secrets/...) are not supported. gRPC calls that use a regional parent or name returnUNIMPLEMENTED, and the REST API only serves the global/v1/projects/{project}/secrets/...paths. - gRPC parents and names must match
projects/{project},projects/{project}/secrets/{secret}orprojects/{project}/secrets/{secret}/versions/{version}; anything else returnsINVALID_ARGUMENT.