locket

package module
v0.12.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 28, 2026 License: MIT Imports: 28 Imported by: 0

README

🔐 locket ❤️

Go - Test

secrets management service

Purpose

Locket is a secrets cache for production services. It stores secrets in memory, loaded from external secrets cache, environment, or .env file as source. Why? Because the 1password GUI is great for business use, but the sacntioned 1password cache was flaky, and I wanted a go based soluton that abstracts away secret origin while providing tight access control and integration with existing deployment.

Overview

sequenceDiagram
    autonumber

    box rgb(0, 40, 0) External 
    participant CI
    participant source
    end

    box rgb(40, 0, 0) Locket Server
    participant secrets
    participant registry
    participant handler
    end

    box rgb(0, 0, 40) Locket Client
    participant client
    end

    activate CI
    CI->>registry: public signing keys
    CI->>client: private signing key
    CI->>secrets: source access key (or source files)
    note over CI: depoy complete
    deactivate CI

    secrets->>source: request all secrets
    activate secrets
    source->>secrets: all secrets
    note over secrets: server init complete
    deactivate secrets

    client->>handler: GET - public key
    activate client
    handler->>client: server public encryption key
    note over client: client init complete
    deactivate client

    client->>handler: GET - public key
    activate client
    handler->>client: server public encryption key
    client->>handler: POST - secret (encrypted & signed)
    handler->>registry: authenticate signing key
    activate handler
    registry->>handler: verify identity
    note over handler: auth & access control
    handler--x client: forbidden
    deactivate handler
    handler->>secrets: request secret
    secrets->>handler: secret value
    handler-->>client: encrypted secret
    note over client: secret fetched
    deactivate client
1-3 Deploy

Create registry and distribute signing keys.

4-5 Init Server

Load secrets using any struct that satisfies the source interface.

struct source
env local environment
dotenv .env file
onepass 1password server
6-7 Init Client

Fetch server public encryption key.

8-9 Refetch public encryption key

Server may generate a new public key upon restart. No caching is currently implemented. 🤷

10-12 Enforce Access Control
  • clients must encrypt and sign every request
  • clients can only requeest their own secrets
13-15 Fetch & Return Secret
  • responses are encrypted

Examples

See tests for examples, and checkout docstings for extensive descriptions.

Future Work

See issues.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var Defaults = defaults{
	AllowCIDR:    "10.0.0.0/24",
	BitsizeRSA:   2048,
	MaxClockSkew: 30 * time.Second,
}
View Source
var OnePasswordVar = "LOCKET_OP_SERVICE_ACCOUNT_TOKEN"

1password service account token environment variable name

View Source
var PathRegistry = "/locket/registry"

PathRegistry is the API endpoint for registry operations.

  • GET: list all entries
  • POST: upsert an entry (RegEntry JSON body)
  • DELETE: remove an entry (RegEntry JSON body with name)

Functions

func NewPairEd25519

func NewPairEd25519() (string, string, error)

NewPairEd25519 generates a new Ed25519 key pair used to authenticate clients requests to the server. Returns: publicKeyPEM, privateKeyPEM, error.

Types

type AllowRequestFunc added in v0.12.0

type AllowRequestFunc func(r *http.Request) error

AllowRequestFunc decides whether an HTTP request is permitted. Return nil to allow, or an error to deny with 403.

func AllowCIDR added in v0.12.0

func AllowCIDR(cidr string) AllowRequestFunc

AllowCIDR returns an AllowRequestFunc that permits requests from the given CIDR range only. This is the default policy when none is provided to NewServer.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client makes requests to a locket server, and must know the server address. serverPubkey is the server's encryption public key, and will be fetched on creation of NewClient(). Rsa and Ed25519 key pairs are also generated on creation of NewClient().

func NewClient

func NewClient(serverURL, keyPub, keyPriv string) (*Client, error)

NewClient creates a new client, fetches the server's encryption public key, and generates RSA key pairs for encrypting k/v secret requests.

Pre-computed ed25519 signing keys (via NewPairEd25519() or any other means) must be passed to a new client, with the expectation that the public key be made available to the server to facilitate authentication. see: FileRegistry.Register() for details

func (*Client) FetchSecret added in v0.6.6

func (c *Client) FetchSecret(name string) (string, error)

fetchSecret produces an ecrypted and signed request to the server, containing the name of the secret to fetch and the client's own public key (to be used for encrypting the response).

type Dotenv added in v0.0.2

type Dotenv struct {
	Path           string              // path to .env file to read
	ServiceSecrets map[string][]string // service names and a list of their secrets
}

Dotenv satisfies the source interface, loading secrets from a specified path to .env file.

func (Dotenv) Load added in v0.2.1

func (d Dotenv) Load() (map[string]Secrets, error)

Load k=v pairs from a .env file, ignoring any #comments. Service name will be set by the keys in ServiceSecrets map.

type Env added in v0.2.1

type Env struct {
	ServiceSecrets map[string][]string // service name mapped to list of service secret names
}

Env satisfies the source interface, loading secrets from the local environment.

func (Env) Load added in v0.2.1

func (e Env) Load() (map[string]Secrets, error)

Load k=v pairs from local environment.

Expect environment variables to be prefixed with the service name. e.g. SERVICE1_FOO=bar will be interpreted as:

  • service name: SERVICE1
  • secret name: SERVICE1_FOO
  • secret value: bar

type FileRegistry added in v0.12.0

type FileRegistry struct {
	Path string
}

FileRegistry is a Registry backed by a local YAML file.

func (FileRegistry) Delete added in v0.12.0

func (f FileRegistry) Delete(name string) error

Delete removes a client entry by name from the YAML file.

func (FileRegistry) Entries added in v0.12.0

func (f FileRegistry) Entries() ([]RegEntry, error)

Entries reads all authorized clients from the YAML file.

func (FileRegistry) Register added in v0.12.0

func (f FileRegistry) Register(name string) (string, string, error)

Register generates a new ed25519 signing keypair, upserts the public key into the YAML file, and returns the keypair.

func (FileRegistry) Upsert added in v0.12.0

func (f FileRegistry) Upsert(entry RegEntry) error

Upsert inserts or updates a client entry in the YAML file. If the file does not exist, it is created. The name is stored verbatim (exact-match, case-sensitive) to match the RemoteRegistry / cloud contract; derive a clean service name before calling if needed.

type KeysPrivateSigning added in v0.2.0

type KeysPrivateSigning map[string]string

map[serviceName]keyPrivateSigning

type Onepass added in v0.0.2

type Onepass struct {
	Vault string // name of the vault containig service secrets
}

Onepass satisfies the source interface, loading secrets from a 1password vault over the net with 1password API. Service account token must be set environment as locket.OnePasswordVar.

func (Onepass) Load added in v0.2.1

func (o Onepass) Load() (map[string]Secrets, error)

Load all service secrets from a named 1password vault, returning a map of service names to their set of k/v secrets.

type RegEntry

type RegEntry struct {
	Name   string `yaml:"name"   json:"name"`
	KeyPub string `yaml:"keypub" json:"keypub"`
}

RegEntry is a single registry item, representing a single client which the server should recognize and authorize.

type Registry added in v0.12.0

type Registry interface {
	// Entries returns all authorized clients.
	Entries() ([]RegEntry, error)
	// Upsert inserts or updates a client entry by name.
	Upsert(RegEntry) error
	// Delete removes a client entry by name.
	Delete(name string) error
}

Registry reads and writes authorized client entries. Implementations include FileRegistry (local YAML) and RemoteRegistry (HTTP API).

type RemoteRegistry added in v0.12.0

type RemoteRegistry struct {
	URL    string
	Token  string
	Client *http.Client
}

RemoteRegistry is a Registry backed by an HTTP API. URL is the base URL (e.g. "http://api:8888") to which PathRegistry is appended for all operations. Token, if set, is sent as an X-Auth-Token header.

func (RemoteRegistry) Delete added in v0.12.0

func (r RemoteRegistry) Delete(name string) error

Delete removes an authorized client by name via the remote API.

func (RemoteRegistry) Entries added in v0.12.0

func (r RemoteRegistry) Entries() ([]RegEntry, error)

Entries fetches all authorized clients from the remote API.

func (RemoteRegistry) Register added in v0.12.0

func (r RemoteRegistry) Register(name string) (string, string, error)

Register generates a new ed25519 signing keypair, upserts the public key via the remote API, and returns the keypair.

func (RemoteRegistry) Upsert added in v0.12.0

func (r RemoteRegistry) Upsert(entry RegEntry) error

Upsert creates or updates an authorized client via the remote API.

type Secrets added in v0.0.2

type Secrets map[string]string // all key/value secrets for a single service

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server serves secrets to authenticated clients over HTTP. It validates client requests against a Registry of authorized signing keys, refreshing the registry on a configurable interval.

func NewServer

func NewServer(
	ctx context.Context,
	opts source,
	reg Registry,
	pollInterval time.Duration,
	allow AllowRequestFunc,
) (*Server, error)

NewServer creates a Server, loading secrets from the given source and authorized clients from the given Registry. If pollInterval is positive, the server refreshes its registry in the background. If allow is nil, AllowCIDR(Defaults.AllowCIDR) is used.

func (*Server) Close added in v0.12.0

func (s *Server) Close()

Close releases the server's background resources: the registry poll goroutine (if any) and the nonce-cache sweeper. The Server must not be used after Close.

func (*Server) Handler

func (s *Server) Handler(w http.ResponseWriter, r *http.Request)

Handler is the HTTP handler for the locket secret server. GET returns the server's RSA public encryption key. POST accepts an encrypted, signed secret request and returns the encrypted secret value.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL