# Secrets management

Source: https://docs.quake.ai/docs/security/secrets-management
Markdown: https://docs.quake.ai/docs/security/secrets-management.md

---

# Secrets management

Managing credentials securely is a core part of operating workloads on any cloud platform. Quake AI uses distinct credential types, each with different scopes, lifetimes, and rotation requirements. This page explains what each credential type is, where it applies, how to rotate it, and what patterns to avoid.

## Platform credential types

### App credentials (openrc.sh)

App credentials authenticate the OpenStack CLI and API. You download them as an `openrc.sh` file from the Quake AI portal and source them into your shell session. The file sets environment variables (`OS_AUTH_URL`, `OS_AUTH_TYPE`, `OS_APPLICATION_CREDENTIAL_ID`, `OS_APPLICATION_CREDENTIAL_SECRET`, `OS_REGION_NAME`) that the OpenStack client reads automatically.

- **Scope:** full project access: compute, network, storage, and identity operations
- **Generation:** [Generate app credentials](/docs/tools/generate-app-credentials)
- **Storage:** local filesystem, excluded from version control via `.gitignore`
- **Rotation:** regenerate and re-download from the portal; the old credentials remain valid until explicitly revoked



Never commit `openrc.sh` to version control. If you expose these credentials, revoke them immediately in the portal and generate a new set.



### S3 credentials (EC2 key pairs)

S3 credentials are access key / secret key pairs that authenticate S3-compatible tools (`s3cmd`, AWS CLI, `boto3`, `rclone`) against Quake AI Object Storage. They are separate from app credentials and scoped to the object storage API.

- **Scope:** S3-compatible object storage operations only
- **Generation:** [Create S3 credentials](/docs/object/how-to/create-s3-credentials)
- **Storage:** local configuration files (e.g., `~/.s3cfg`, `~/.aws/credentials`), excluded from version control
- **Rotation:** generate a new EC2 credential pair, update your tools, then delete the old pair with `openstack ec2 credentials delete ACCESS_KEY`

### API tokens

API tokens provide short-lived authentication for direct API calls. You obtain a token with `openstack token issue` or by posting to the Keystone identity endpoint. Tokens expire automatically (typically within hours).

- **Scope:** full project access for the token's lifetime (typically hours)
- **Generation:** [API tokens](/docs/tools/api-tokens)
- **Rotation:** tokens expire automatically; re-issue as needed. Do not store tokens persistently or cache them across sessions; treat them as ephemeral.

## In-VM secrets

Secrets inside your running instances require a different approach than platform credentials. The platform does not provide a managed secrets store; you implement secrets management at the application level.

### Environment variables

The most common pattern for passing secrets to applications running on an instance. Set environment variables in your shell profile, systemd unit files, or container runtime configuration.

```bash
export DATABASE_URL="postgresql://USER:PASSWORD@DB_HOST:5432/DB_NAME"
export API_KEY="YOUR_APPLICATION_API_KEY"
```

Advantages: supported by common runtimes and frameworks. Disadvantages: visible in `/proc/PID/environ` to anyone with root access on the instance, and logged by some process managers.

### Cloud-init user-data

Cloud-init runs at first boot and can inject secrets into an instance through the user-data field. Use `write_files` to place configuration files or `runcmd` to set environment variables.

```yaml
#cloud-config
write_files:
  - path: /etc/app/config.env
    permissions: "0600"
    content: |
      DATABASE_URL=postgresql://USER:PASSWORD@DB_HOST:5432/DB_NAME
```



User-data is stored in the instance metadata service at `http://169.254.169.254/latest/user-data` and readable by any process on the instance. Use it for bootstrap configuration only; rotate any injected credentials after first boot. See [Anti-patterns to avoid](#anti-patterns-to-avoid) for the full list.



### Mounted secrets (Docker and Kubernetes)

If you run containerized workloads, mount secrets as files instead of passing them as environment variables. Docker supports `--mount type=bind` for secret files, and Kubernetes has native `Secret` resources.

```bash
docker run -d \
  --mount type=bind,source=/etc/app/db-password,target=/run/secrets/db-password,readonly \
  my-application
```

File-mounted secrets avoid the `/proc` visibility issue and integrate with container orchestration tooling.

## Rotation patterns

| Credential type | Rotation trigger | Procedure |
|---|---|---|
| App credentials | Quarterly, or on suspected compromise | Regenerate in portal, re-download `openrc.sh`, update CI/CD pipelines, revoke old credentials |
| S3 credentials | Quarterly, or on suspected compromise | Generate new EC2 pair, update tool configs, delete old pair with `openstack ec2 credentials delete` |
| API tokens | No manual rotation needed | Tokens expire automatically; re-issue as needed |
| SSH key pairs | Annually, or on team member departure | Generate new key pair, upload new public key, remove old public key from instances and portal |
| Application secrets | Per your security policy | Update the secret in your deployment tooling, restart affected services, verify connectivity |

## Anti-patterns to avoid

**Hardcoded credentials in user-data scripts.** User-data is readable from the metadata service. If you place long-lived database passwords or API keys in user-data, any process on the instance can read them. Use user-data for bootstrap only and rotate the injected credentials after first boot.

**Credentials in version control.** Committed secrets are difficult to fully remove from git history. Use `.gitignore` for credential files and environment-specific configs. If you discover a committed secret, revoke it immediately; do not rely on removing the commit.

**Unencrypted configuration files.** Storing credentials in plaintext config files with default permissions (world-readable) exposes them to any user or process on the instance. Set file permissions to `0600` (owner-only) for any file containing secrets.

**Overly broad security groups as a substitute for authentication.** Restricting network access with security groups is important, but it is not a replacement for application-level authentication. Always require credentials for API and database access even on private networks.

**Shared credentials across environments.** Using the same S3 credentials or app credentials in development, staging, and production means a compromise in one environment affects all environments. Generate separate credential sets for each environment.

## See also

- [How to store application secrets and inject them at runtime](/docs/security/how-to/inject-app-secrets): VM, Kubernetes, and Vault patterns for application credentials
- [Shared responsibility model](/docs/security/shared-responsibility)
- [Security hardening checklist](/docs/security/hardening-checklist)
- [Generate app credentials](/docs/tools/generate-app-credentials)
- [Create S3 credentials](/docs/object/how-to/create-s3-credentials)
- [API tokens](/docs/tools/api-tokens)
- [Security overview](/docs/security)
