Secrets Package

Secrets Package

The secrets package provides a secure framework for managing credentials and secrets in Go applications. It offers a unified interface for storing, retrieving, and encrypting sensitive information across various storage backends.

Features

  • Credential Management: Store and retrieve credentials securely
  • Encryption: Built-in AES encryption for sensitive data
  • Pluggable Storage Backends: Interface-based design for multiple storage providers
  • Local Storage: File-based local storage implementation
  • Extensibility: Easy to implement custom storage providers

Core Components

Credential

The Credential type represents a set of sensitive information that needs to be stored securely:

type Credential struct {
    // Fields for storing username, password, and other sensitive information
}

Store Interface

The Store interface defines the contract for any credential storage implementation:

type Store interface {
    Get(key string, ctx context.Context) (*Credential, error)
    Write(key string, credential *Credential, ctx context.Context) error
    Provider() string
}

Store Manager

The StoreManager provides a centralized way to manage multiple credential stores:

type StoreManager struct {
    // Internal implementation details
}

Usage Examples

Basic Credential Storage and Retrieval

package main

import (
    "context"
    "fmt"
    "oss.nandlabs.io/golly/secrets"
)

func main() {
    // Create a new local store
    store, err := secrets.NewLocalStore("/path/to/credentials")
    if err != nil {
        fmt.Printf("Error creating store: %v\n", err)
        return
    }

    // Create a new credential
    cred := secrets.NewCredential()
    cred.SetUsername("user123")
    cred.SetPassword("securePassword!")

    // Store the credential
    ctx := context.Background()
    err = store.Write("myapp.database", cred, ctx)
    if err != nil {
        fmt.Printf("Error writing credential: %v\n", err)
        return
    }

    // Later, retrieve the credential
    retrievedCred, err := store.Get("myapp.database", ctx)
    if err != nil {
        fmt.Printf("Error retrieving credential: %v\n", err)
        return
    }

    // Use the credential
    username := retrievedCred.Username()
    password := retrievedCred.Password()

    fmt.Printf("Retrieved credentials for user: %s\n", username)
}

Using Store Manager with Multiple Backends

package main

import (
    "context"
    "fmt"
    "oss.nandlabs.io/golly/secrets"
)

func main() {
    // Create a store manager
    manager := secrets.NewStoreManager()

    // Add different credential stores
    localStore, _ := secrets.NewLocalStore("/path/to/local/credentials")
    manager.Register(localStore)

    // Register a custom store (implementation not shown)
    // customStore := NewCustomStore()
    // manager.Register(customStore)

    // Get a credential using the manager
    ctx := context.Background()
    cred, err := manager.Get("myapp.apikey", ctx)
    if err != nil {
        fmt.Printf("Error retrieving credential: %v\n", err)
        return
    }

    // Use the credential
    apiKey := cred.GetAttribute("api_key")
    fmt.Printf("Retrieved API key: %s\n", apiKey)
}

Using AES Encryption Directly

package main

import (
    "fmt"
    "oss.nandlabs.io/golly/secrets"
)

func main() {
    // Create a new AES encryptor with a key
    encryptor, err := secrets.NewAES("my-secure-encryption-key-32-bytes!")
    if err != nil {
        fmt.Printf("Error creating encryptor: %v\n", err)
        return
    }

    // Encrypt some sensitive data
    plaintext := []byte("sensitive information")
    ciphertext, err := encryptor.Encrypt(plaintext)
    if err != nil {
        fmt.Printf("Error encrypting data: %v\n", err)
        return
    }

    fmt.Printf("Encrypted data: %x\n", ciphertext)

    // Later, decrypt the data
    decrypted, err := encryptor.Decrypt(ciphertext)
    if err != nil {
        fmt.Printf("Error decrypting data: %v\n", err)
        return
    }

    fmt.Printf("Decrypted data: %s\n", string(decrypted))
}

Creating a Custom Storage Provider

package main

import (
    "context"
    "fmt"
    "oss.nandlabs.io/golly/secrets"
)

// Define a custom credential store
type DatabaseStore struct {
    connectionString string
    // Other fields as needed
}

// Implement the Store interface
func (ds *DatabaseStore) Get(key string, ctx context.Context) (*secrets.Credential, error) {
    // Implementation that retrieves a credential from the database
    // ...
    return credential, nil
}

func (ds *DatabaseStore) Write(key string, credential *secrets.Credential, ctx context.Context) error {
    // Implementation that writes a credential to the database
    // ...
    return nil
}

func (ds *DatabaseStore) Provider() string {
    return "database"
}

// Constructor for the custom store
func NewDatabaseStore(connectionString string) *DatabaseStore {
    return &DatabaseStore{
        connectionString: connectionString,
    }
}

func main() {
    // Create the custom store
    dbStore := NewDatabaseStore("postgresql://localhost/secrets")

    // Use it directly or register with a store manager
    manager := secrets.NewStoreManager()
    manager.Register(dbStore)

    fmt.Println("Database store registered successfully")
}

Installation

go get oss.nandlabs.io/golly/secrets