zalando/go-keyring

repository·master·Indexed 23 days ago

https://github.com/zalando/go-keyring

An OS-agnostic Go library for managing secrets in the system keyring. It provides a unified interface for macOS Keychain, Linux/BSD Secret Service/D-Bus, and Windows Credential Manager, supporting operations to set, get, delete, and list secrets. The library includes a mock implementation via MockInit() and MockInitWithError() for testing in environments without a system keyring.

Tokens
2.2K
Snippets
6
Records
15
Agent score
72%

What's inside go-keyring

  1. Configure Linux/BSD keyring collection

    master

    The Linux/BSD implementation expects a default collection named login to exist. If it does not exist, you can create it using the seahorse frontend program:

    1. Open seahorse.
    2. Go to File > New > Password Keyring.
    3. Click Continue.
    4. When asked for a name, use: login.
  2. Use the go-keyring Go API

    master

    The go-keyring library provides a simple, OS-agnostic API to manage secrets. You can use keyring.Set to store a secret, keyring.Get to retrieve it, and keyring.Delete to remove it. Secrets are identified by a combination of a service name and a user name.

    package main
    
    import (
        "log"
    
        "github.com/zalando/go-keyring"
    )
    
    func main() {
        service := "my-app"
        user := "anon"
        password := "secret"
    
        // set password
        err := keyring.Set(service, user, password)
        if err != nil {
            log.Fatal(err)
        }
    
        // get password
        secret, err := keyring.Get(service, user)
        if err != nil {
            log.Fatal(err)
        }
    
        log.Println(secret)
    }
  3. Mock the keyring for testing

    master

    If you are running tests on a system without a keyring implementation (like a CI environment), you can use keyring.MockInit(). This replaces the OS-specific provider with an in-memory implementation, allowing you to test your logic without interacting with the actual system keychain.

    package implementation
    
    import (
        "testing"
    
        "github.com/zalando/go-keyring"
    )
    
    func TestMockedSetGet(t *testing.T) {
        keyring.MockInit()
        err := keyring.Set("service", "user", "password")
        if err != nil {
            t.Fatal(err)
        }
    
        p, err := keyring.Get("service", "user")
        if err != nil {
            t.Fatal(err)
        }
    
        if p != "password" {
            t.Error("password was not the expected string")
        }
    }
  4. Interact with Windows Credential Manager via CLI

    master

    On Windows, the library uses the Credential Manager. The library combines the service and username into a single target string: service:username.

    Using cmdkey

    • Set a password: cmdkey /generic:"service:user" /user:"user" /pass:"password"
    • Delete a password: cmdkey /delete:"service:user"
    • Note: cmdkey cannot retrieve passwords directly; use PowerShell for retrieval.

    Using PowerShell

    Retrieving a password:

    $cred = Get-StoredCredential -Target "service:user"
    $cred.GetNetworkCredential().Password

    Using the CredentialManager module:

    1. Install: Install-Module -Name CredentialManager -Force
    2. Set: New-StoredCredential -Target "service:user" -UserName "user" -Password "password" -Type Generic -Persist LocalMachine
    3. Get: (Get-StoredCredential -Target "service:user").GetNetworkCredential().Password
    4. Delete: Remove-StoredCredential -Target "service:user"
  5. Interact with Linux/BSD Secret Service via CLI

    master

    On Linux and *BSD, the library uses the Secret Service API via D-Bus. You can use secret-tool (part of libsecret) to interact with it.

    Installation:

    • Debian/Ubuntu: sudo apt-get install libsecret-tools
    • Fedora/RHEL: sudo dnf install libsecret
    • Arch Linux: sudo pacman -S libsecret

    Usage:

    • Set a password: secret-tool store --label="<label>" service "<service>" username "<user>" (you will be prompted for the password, or you can pipe it in).
    • Get a password: secret-tool lookup service "<service>" username "<user>"
    • Delete a password: secret-tool clear service "<service>" username "<user>".
  6. Interact with the macOS Keychain via CLI

    master

    On macOS, you can use the security command to manage secrets. This is useful for debugging or scripting outside of Go.

    • Set/Update a password: security add-generic-password -U -s "service" -a "user" -w "password" (the -U flag updates the password if it already exists).
    • Get a password: security find-generic-password -s "service" -wa "user" (the -w flag ensures only the password value is output).
    • Delete a password: security delete-generic-password -s "service" -a "user".
  7. Use MockInit to simulate keyring operations in tests

    master

    The keyring package provides a mock implementation to simulate keyring behavior in memory without interacting with the actual OS keychain. This is useful for unit testing or simulating environments where a real keyring is unavailable.

    Use MockInit() to switch the global provider to an in-memory store. To revert to the platform's default keyring provider after your tests are complete, call MockRestore() (typically using defer).

    func MockInit()
    func MockRestore()
    func MockInitWithError(err error)
  8. Set a password in the keyring

    master

    Use Set(service, user, password string) to store a secret in the system's keyring. The service acts as a namespace for your application, and user identifies the specific account.

    Note on data limits:

    • macOS: The combined length of service, username, and password should not exceed ~3000 bytes.
    • Windows: The service is limited to 32KiB and the password to 2560 bytes.
    • Linux/Unix: No theoretical limit, but performance may degrade for values >100KiB.
  9. Simulate keyring errors with MockInitWithError

    master

    If you need to test how your application handles keyring failures (e.g., permission denied or system errors), use MockInitWithError(err). This configures the mock provider to return the specified error for every operation (Set, Get, Delete, DeleteAll, and ListUsers).

    func MockInitWithError(err error)