MCPcopy Index your code
hub / github.com/cruise-automation/daytona

github.com/cruise-automation/daytona @v1.3.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release v1.3.0 ↗ · + Follow
122 symbols 498 edges 26 files 55 documented · 45% 1 cross-repo links
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Testing

DAYTONA

This is intended to be a lighter, alternative, implementation of the Vault client CLI primarily for services and containers. Its core features are the ability to automate authentication, fetching of secrets, and automated token renewal.

Previously authentication to, and secret retrieval from, Vault via a server or container was a delicate balance of shell scripts or potentially lengthy http implementations, similar to:

vault login -token-only -method=$METHOD role=$VAULT_ROLE"
THING="$(vault read -field=key secret/path/to/thing)"
ANOTHER_THING="$(vault read -field=key secret/path/to/another/thing)"
echo $THING | app
...

Instead, a single binary can be used to accomplish most of these goals.

Installation

Run go install github.com/cruise-automation/daytona/cmd/daytona@latest to install in your go source path

Authentication

The following authentication methods are supported:

  • Kubernetes - To be used with the Vault Kubernetes Auth Backend. Uses the JWT of the bound kubernetes service account as described in the official Vault documentation. Intended for use as an initContainer, sidecar container, or entrypoint; for managing secrets within a pod.

  • AWS IAM - To be used with the Vault AWS Auth Backend. Uses the IAM Role for Vault authentication. Intended for use on AWS resources that utilize IAM roles.

  • GCP IAM - To be used with the Vault GCP Auth Backend. Uses GCP service accounts for IAM Vault authentication. Intended for use with GCP resources that utilize bound service accounts.

  • Azure IAM - To be used with the Vault Azure Auth Backend. Uses Azure Active Directory credentials for IAM Vault authentication. Intended for use with Azure resources that utilize manged identities.


Secret Fetching

daytona gives you the ability to pre-fetch secrets upon launch and store them either in environment variables, as JSON to a specified file, or as singular secrets to a specified file. You define what secrets should be fetched by supplying one or more Secret Definitions. Secret Definitions are supplied via environment variables.

Secret Definition Decoder Guide

<STORAGE PATH PREFIX>_<secretID-SUFFIX>=<SECRET-APEX>

  • VAULT_SECRET_ (<STORAGE PATH PREFIX>): Singular Secret Storage Path Prefix
  • VAULT_SECRETS_ (<STORAGE PATH PREFIX>): Plural (more than 1 secret beneath the specified path) Secret Storage Path Prefix
  • secretID-SUFFIX: The unique secret identifier that can be used to tie a Secret Storage Path Prefix to a corresponding Destination Prefix. The uniqueness of this value provides the ability to supply multiple secret paths.
  • SECRET-APEX: When used with Singular definitions, the Vault path where the secret exists in Vault that can be read. When used with Plural definitions, the Vault path where the secrets exist in Vault that can be listed and then read. This will fetch all secrets within the given Vault directory.

Secret Definition Options

  • DAYTONA_SECRET_DESTINATION_: Secret Destination Prefix. This is a full file path location where the corresponding secret from the supplied storage path is written to. Usage: DAYTONA_SECRET_DESTINATION_<secretID-SUFFIX>=/path/to/file
  • VAULT_VALUE_KEY_: Can be used to indicate the retrieval of a single key from a singular secret definition. Usage: VAULT_VAULT_KEY_<secretID-SUFFIX>=api_key

Singular Secrets - Singular Secret Declaration: VAULT_SECRET_<secretID-SUFFIX>=<SECRET-APEX> - Singular Secret Destination: DAYTONA_SECRET_DESTINATION_<secretID-SUFFIX>=<FILE-PATH>

Plural Secrets - Plural Secret Declaration: VAULT_SECRETS_<secretID-SUFFIX>=<SECRET-APEX> - Singular Secret Destination: DAYTONA_SECRET_DESTINATION_<secretID-SUFFIX>=<FILE-PATH>


Examples

Singular Secret

Vault Data

$ vault read secret/whatever/thing

Key                 Value
---                 -----
refresh_interval    768h
value               hello

Secret Definition

VAULT_SECRET_THING=secret/whatever/thing
DAYTONA_SECRET_DESTINATION_THING=/tmp/top-secret

Result

hello would be written to the file /tmp/top-secret


Singular Secret w/Specific Key

Vault Data

$ vault read secret/whatever/thing

Key                 Value
---                 -----
refresh_interval    768h
value               hello
api_key             potato1234

Secret Definition

VAULT_SECRET_THING=secret/whatever/thing
DAYTONA_SECRET_DESTINATION_THING=/tmp/top-secret
VAULT_VALUE_KEY_THING=api_key

Result

potato1234 would be written to the file /tmp/top-secret


Plural Secrets

Vault Data

$ vault list secret/many

Keys
----
thing1
thing2
thing3

$ vault read secret/many/thing1

Key                 Value
---                 -----
refresh_interval    768h
value               1

etc...

Secret Definition

VAULT_SECRETS_THING=secret/many
DAYTONA_SECRET_DESTINATION_THING=/tmp/top-secret-many

Result

/tmp/top-secret-many would be populated with:

{
  "thing1": "1",
  "thing2": "2",
  "thing3": "3"
}

Outputs

Fetched secrets can be output via the following methods:

  • DAYTONA_SECRET_DESTINATION_ The secret destination prefix as specified above
  • Environment variables via -secret-env. Because docker containers cannot set each other's environment variables, -secret-env will have no effect unless used with the -entrypoint flag, so that any populated environment variables are passed to a provided executable.
  • -secret-path (Deprecated) Specifies a file which to output a JSON representation of a fetched secret(s)

Data and Secret Key Layout

daytona prefers secret data containing the key value, but is able to detect other key names (this decreases readability, as you'll see later below). For example:

the secret secret/path/to/database should have its data stored as:

{
  "value": "databasepassword"
}

If -secret-env is supplied at runtime, the above example would be written to an environment variable as DATABASE=databasepassword, while DAYTONA_SECRET_DESTINATION_PATH=/tmp/secrets would be written to a file as:

{
  "database": "password"
}

If data within a secret is stored as multiple key-values, which is the non-preferred format, then the secret data will be stored as a combination of SECRETNAME_DATAKEYNAME=value. For example, if the Vault secret secret/path/to/database has multiple key-values:

{
  "db_username": "foo",
  "db_password": "databasepassword"
}

then a secret's data will be fetched by daytona, and stored as variables DATABASE_DB_USERNAME=foo and DATABASE_DB_password=databasepassword, or respectively, written to a file as:

{
  "database_db_username": "foo",
  "database_db_password": "databasepassword"
}

Supported Paths

Top Level Path Iteration

Consider the following path, secret/path/to/directory which when listed, contains the following secrets:

database
api_key
moredatahere/

daytona would iterate through all of these values attempting to read their secret data. Because moredatahere/ is a subdirectory in a longer path, it would be skipped.

Direct Path

If provided a direct path secret/path/to/database, daytona will process secret data as outlined in the Data and Secret Key Layout section above.


Implementation Examples

You have configured a vault k8s auth role named awesome-app-vault-role-name that contains the following configuration:

{
  "bound_service_account_names": [
    "awesome-app"
  ],
  "bound_service_account_namespaces": [
    "elite-squad"
  ],
  "policies": [
    "too-permissive"
  ],
  "ttl": 3600
}

K8s Pod Definition Example:

Be sure to populate the serviceAccountName and VAULT_AUTH_ROLE with the corresponding values from your vault k8s auth role as described above.

---
apiVersion: v1
kind: Pod
metadata:
  name: awesome-app
spec:
  serviceAccountName: awesome-app
  volumes:
    - name: vault-secrets
      emptyDir:
        medium: Memory
  initContainers:
    - name: daytona
      image: gcr.io/supa-fast-c432/daytona@sha256:abcd123
      securityContext:
        runAsUser: 9999
        allowPrivilegeEscalation: false
      volumeMounts:
      - name: vault-secrets
        mountPath: /home/vault
      env:
      - name: K8S_AUTH
        value: "true"
      - name : K8S_AUTH_MOUNT
        value: "kubernetes-gcp-dev-cluster"
      - name: SECRET_ENV
        value: "true"
      - name: TOKEN_PATH
        value: /home/vault/.vault-token
      - name: VAULT_AUTH_ROLE
        value: awesome-app-vault-role-name
      - name: DAYTONA_SECRET_DESTINATION_PATH
        value: /home/vault/secrets
      - name: VAULT_SECRETS_PATH
        value: secret/path/to/app
      - name: VAULT_SECRETS_GLOBAL
        value: secret/path/to/global/metrics
````

Note the `securityContext` provided above. Without it, the daytona container runs as UID 0, which is root. Because daytona writes files with `0600` permissions, the files are only readable by a user with the same UID. It is necessary to run your other containers in the pod with the same `securityContext` in order to read the files that daytona places.

The example above, assuming a successful authentication, would yield a vault token at `/home/vault/.vault-token` and any specified secrets written to `/home/vault/secrets` as

```json
{
  "api_key": "supersecret",
  "database": "databasepassword",
  "metrics": "helloworld"
}

the secrets written above would be the representation of the following vault data:

secret/path/to/app/api_key

{
  "value": "supersecret"
}

secret/path/to/app/database

{
  "value": "databasepassword"
}

secret/path/to/global/metrics

{
  "value": "helloworld"
}

AWS IAM Example - Writing to a File:

Assume you have the following Vault AWS Auth Role, vault-role-name:

{
  "auth_type": "iam",
  "bound_iam_principal_arn": [
    "arn:aws:iam::12345:role/my-role"
  ],
  "policies": [
    "my-ro-policy"
  ]
}
VAULT_SECRETS_TEST=secret/path/to/app/secrets DAYTONA_SECRET_DESTINATION_TEST=/home/vault/secrets daytona -iam-auth -token-path /home/vault/.vault-token -vault-auth-role vault-role-name

The execution example above (assuming a successful authentication) would yield a vault token at /home/vault/.vault-token and any specified secrets written to /home/vault/secrets as

{
  "secrets_secretA": "hellooo",
  "secrets_api_key": "supersecret"
}

as a representation of the following vault data:

secret/path/to/app/secrets

{
  "secretA": "hellooo",
  "api_key": "supersecret"
}

AWS IAM Example - As a container entrypoint:

In a Dockerfile:

ENTRYPOINT [ "./daytona", "-secret-env", "-iam-auth", "-vault-auth-role", "vault-role-name", "-entrypoint", "--" ]

combined with supplying the following during a docker run:

-e "VAULT_SECRETS_APP=secret/path/to/app"

would yield the following environment variables in a container:

API_KEY=supersecret
DATABASE=databasepassword

as a representation of the following vault data:

secret/path/to/app/api_key

{
  "value": "supersecret"
}

secret/path/to/app/database

{
  "value": "databasepassword"
}

AWS IAM Example - As a container entrypoint, for requesting a PKI certificate:

In a Dockerfile:

ENTRYPOINT [ "./daytona", "-iam-auth", "-vault-auth-role", "vault-role-name", "-pki-issuer", "pki-backend", "-pki-role", "my-role", "-pki-domains", "www.example.com", "-pki-cert", "/etc/cert.pem", "-pki-privkey", "/etc/key.pem", "-pki-use-ca-chain", -entrypoint", "--" ]

Given a PKI backend issuer role located at pki-backend/issue/my-role, and update permissions granted to vault-role-name on this path, Daytona will request a certificate for www.example.com from Vault, placing the certificate (with CA chain) and private key in /etc.

N.b.: * The role should have www.example.com configured in its allowed_domains * Before setting -pki-use-ca-chain, verify whether the PKI backend in question has the full chain at <pki-backend>/ca_chain - Some Vault PKI backends may have the full chain (including the root), while others may only have the intermediates. - Services using this cert/chain may refuse to accept a cert with the root in the chain - use with caution.

GCP GCE Example - Writing to a File:

Assume you have the following Vault GCP Auth Role:

{
    "bound_projects": [
        "my-project"
    ],
    "bound_service_accounts": [
        "cruise-automation-sa@my-project.iam.gserviceaccount.com"
    ],
    "policies": [
        "my-ro-policy"
    ],
    "type": "iam"
}
VAULT_SECRETS_TEST=secret/path/to/app/secrets DAYTONA_SECRET_DESTINATION_TEST=/home/vault/secrets daytona -gcp-auth -gcp-svc-acct cruise-automation-sa@my-project.iam.gserviceaccount.com -token-path /home/vault/.vault-token -vault-auth-role vault-gcp-role-name

The execution example above (assuming a successful authentication) would yield a vault token at /home/vault/.vault-token and any specified secrets written to /home/vault/secrets as

```jso

Extension points exported contracts — how you extend this code

Authenticator (Interface)
Authenticator is an interface to represent an external source that should be authenticated against. [5 implementers]
pkg/auth/auth.go
Option (Interface)
Option defines how an option should be applied [3 implementers]
pkg/daytona/options.go
LogicalClient (Interface)
LogicalClient is the minimum interface needed to read secrets from the API [1 implementers]
pkg/secrets/reader.go

Core symbols most depended-on inside this repo

Unmarshal
called by 30
pkg/daytona/unmarshal.go
GetTestClient
called by 29
pkg/helpers/testhelpers/testing.go
Close
called by 27
pkg/secrets/reader.go
SecretFetcher
called by 14
pkg/secrets/secrets.go
CertFetcher
called by 10
pkg/pki/pki.go
ValidateAuthType
called by 7
pkg/config/config.go
valueConverter
called by 6
pkg/secrets/secrets.go
fetchVaultToken
called by 6
pkg/auth/auth.go

Shape

Function 74
Method 26
Struct 19
Interface 3

Languages

Go100%

Modules by API surface

pkg/secrets/secrets_test.go14 symbols
pkg/daytona/options.go11 symbols
pkg/secrets/secrets.go10 symbols
pkg/auth/auth.go9 symbols
pkg/secrets/reader.go8 symbols
pkg/auth/azure.go8 symbols
pkg/auth/auth_test.go8 symbols
pkg/daytona/unmarshal.go5 symbols
pkg/config/config.go5 symbols
pkg/auth/k8s.go5 symbols
pkg/secrets/reader_test.go4 symbols
pkg/pki/pki_test.go4 symbols

Used by 1 indexed graphs manifest dependencies, hub-wide

For agents

$ claude mcp add daytona \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact