Skip to content

feat(credentials): protect a local gateway's stored credentials with the OS keychain #3857

Description

@ilaigold

User Story

As a developer running an OpenShell gateway on my own laptop (Homebrew, VM driver),
I want the gateway's stored provider credentials protected by my OS keychain,
so that a copy of my home directory, a backup, or another program running as me can't
decrypt my API keys and subscription tokens.

Problem Statement

On a local gateway, the default encrypted database store keeps its key-encryption key in a
file ($XDG_STATE_HOME/openshell/gateway/credentials/key-encryption-key.bin, mode 0600) on
the same machine as the database it protects. Anyone or anything that can read both files
can decrypt every stored credential. There is no local option that uses the operating
system's own secret store.

#1931 lists "single-player/local environments later using OS-native stores such as macOS
Keychain" as a follow-up. I couldn't find an issue tracking it.

Impact / Why This Matters

  • On macOS with the Homebrew package (0.1.2), the key file and the gateway state directory
    (/opt/homebrew/var/openshell/gateway) are both included in Time Machine by default
    (tmutil isexcluded reports [Included] for both). Every backup holds everything needed
    to decrypt the credentials.
  • Any process running as the user can read the key file.
  • Current workarounds don't help much. key_encryption_key_env moves the key into the
    service's environment, which is still plaintext on disk. The Vault and Kubernetes Secrets
    drivers don't fit a laptop.
  • The credentials OpenShell keeps out of the sandbox (API keys, subscription tokens, GitHub
    tokens) are the user's most sensitive ones, so the local at-rest story matters as much as
    the in-sandbox one.

Proposed Design

  • The gateway owner opts in with one gateway setting. Local setup on macOS could suggest it.
  • From then on, stored credentials are protected by the OS keychain: macOS Keychain first,
    then Windows Credential Manager and Linux Secret Service where available. The gateway
    database and its backups alone are no longer enough to recover them.
  • Nothing else changes: provider create, update, attach, refresh and placeholder injection
    behave as they do today.
  • If the keychain is locked or unavailable when the gateway starts, the gateway refuses to
    start with a clear message. It never falls back to a key file.
  • openshell gateway info shows which credential store is active.
  • Existing gateways get a documented one-time move from the key file to the keychain.

Acceptance Criteria

  • With the option on, no file on disk holds material that, together with the gateway
    database, decrypts stored credentials.
  • Provider create, update, rotate, refresh and sandbox injection work as with the
    default store.
  • The gateway restarts and survives a Homebrew upgrade without the user re-entering any
    credential, and without an access prompt for each credential.
  • A locked or missing keychain stops the gateway with a clear error.
  • openshell gateway info names the active credential store.
  • macOS is documented. Linux and Windows are either supported or rejected with a clear
    message.

Alternatives Considered

  • A keychain credential driver with one keychain item per credential. This is closest to
    the Vault driver. However, the Homebrew openshell-gateway binary is ad-hoc signed
    (codesign -dv reports Signature=adhoc), so macOS keychain access rules tied to it may
    prompt again for every item after each upgrade. Keeping the database store and moving only
    its key-encryption key into the keychain needs one item and gives the same protection. I'd
    leave the choice to maintainers.
  • Excluding the files from backups. This fixes one leak but not reads by other programs,
    and users easily miss it.
  • Using the Vault driver locally. It requires running Vault on a laptop.

Agent Investigation

  • Default store: an AES-256-GCM envelope per credential, with a data key wrapped by the
    key-encryption key. Today the key comes from key_encryption_key_path or
    key_encryption_key_env (docs/how-it-works/gateways/configuration.mdx, "Credential
    Drivers").
  • Checked on macOS with the Homebrew 0.1.2 gateway: key file at the default path, mode 0600,
    32 bytes, and both paths are included in Time Machine.
  • Related: feat: add credential drivers for provider secret storage #1931 (lists macOS Keychain as a follow-up) and feat(credentials): support database KEK rotation #3557 (database key-encryption
    key rotation), which touches the same key.

I'd like to implement this if maintainers agree on the approach.

Checklist

  • I've reviewed existing issues and the architecture docs
  • This is a design proposal, not a "please build this" request

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    state:triage-neededOpened without agent diagnostics and needs triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions