posixage

package
v0.2.1 Latest Latest
Warning

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

Go to latest
Published: Jul 15, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

README

Store posixage

The posixage store is a POSIX compliant encrypted file store. It uses age to encrypt/decrypt its files and has support for password, ssh and age keys.

Quickstart

import "github.com/docker/secrets-engine/store/posixage"

func main() {
    root, err := os.OpenRoot("my/secrets/path")
    if err != nil {
        panic(err)
    }

    s, err := posixage.New(root,
			func() *mocks.MockCredential {
				return &mocks.MockCredential{}
			},
			WithEncryptionCallbackFunc[EncryptionPassword](func(_ context.Context) ([]byte, error) {
				return []byte(masterKey), nil
			}),
			WithDecryptionCallbackFunc[DecryptionPassword](func(_ context.Context) ([]byte, error) {
				return []byte(masterKey), nil
			}),
		)
}

The store allows you to register multiple encryption and decryption callback functions. Each callback gives your application control over how to retrieve the required data — for example, from environment variables, a configuration file, or via an interactive user prompt.

Features
  • Support for multiple encryption functions
  • Support for multiple decryption functions
Locking

The store uses a process-level lock file to coordinate access across processes. Lock acquisition retries until the caller context is canceled or the lock is acquired. Use context.WithTimeout or context.WithDeadline on store operations when lock acquisition should be bounded.

The store can recover a stale .posixage.lock file when it is older than 30s.

Callbacks are invoked in the order they are registered. For decryption, the store tries each callback in sequence, and the first one that successfully provides a valid key will return the decrypted secret.

Here's an example of accepting multiple passwords for encryption:

import "github.com/docker/secrets-engine/store/posixage"

func main() {
    root, err := os.OpenRoot("my/secrets/path")
    if err != nil {
        panic(err)
    }

    s, err := posixage.New(root,
			func() *mocks.MockCredential {
				return &mocks.MockCredential{}
			},
            WithEncryptionCallbackFunc[EncryptionPassword](func(_ context.Context) ([]byte, error) {
				return []byte(masterKey), nil
			}),
			WithEncryptionCallbackFunc[EncryptionPassword](func(_ context.Context) ([]byte, error) {
				return []byte(bobPassword), nil
			}),
            WithEncryptionCallbackFunc[EncryptionAgeX25519](func(_ context.Context) ([]byte, error) {
				return []byte(identity.Recipient().String()), nil
			}),
			WithDecryptionCallbackFunc[DecryptionPassword](func(_ context.Context) ([]byte, error) {
				return []byte(masterKey), nil
			}),
		)
}
Secrets

Any secret format is supported as long as it conforms to the store.Secret interface.

Documentation

Overview

Package posixage provides a file-based secret store secured with age(https://github.com/FiloSottile/age) encryption.

Secrets are stored in directories named after a base64-encoded secret ID. Each secret can be encrypted with one or more encryption keys. When retrieving a secret, one or more corresponding decryption keys may be provided to unlock it.

This allows flexible key management, supporting scenarios such as multiple recipients, key rotation, or shared access.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func New

func New[T store.Secret](rootDir *os.Root, f store.Factory[T], opts ...Options) (store.Store, error)

New returns a store.Store that manages encrypted files on disk.

Each secret is stored in its own directory, named with a base64-encoded secret ID. The directory contains:

  • one encrypted secret file for each configured encryption key type
  • a metadata file, which is public and always formatted as valid JSON

Types

type DecryptionAgeX25519

type DecryptionAgeX25519 secretfile.PromptFunc

DecryptionAgeX25519 is the age private key

type DecryptionPassword

type DecryptionPassword secretfile.PromptFunc

type DecryptionSSH

type DecryptionSSH secretfile.PromptFunc

DecryptionSSH is the ssh private key

type EncryptionAgeX25519

type EncryptionAgeX25519 secretfile.PromptFunc

type EncryptionPassword

type EncryptionPassword secretfile.PromptFunc

type EncryptionSSH

type EncryptionSSH secretfile.PromptFunc

EncryptionSSH supports ssh-rsa and ssh-ed25519

type Options

type Options func(c *config) error

func WithDecryptionCallbackFunc

func WithDecryptionCallbackFunc[K decryptionFuncs](callback K) Options

WithDecryptionCallbackFunc registers a callback used to prompt the user for input when decrypting credentials.

Multiple callbacks may be registered. They are invoked in the same order they were added.

func WithEncryptionCallbackFunc

func WithEncryptionCallbackFunc[K encryptionFuncs](callback K) Options

WithEncryptionCallbackFunc registers a callback used to prompt the user for input when encrypting credentials.

Multiple callbacks may be registered. They are invoked in the same order they were added.

func WithLogger

func WithLogger(l logging.Logger) Options

WithLogger adds a custom logger to the store. If a no logger has been specified, a noop logger is used instead.

func WithScryptWorkFactor added in v0.2.1

func WithScryptWorkFactor(logN int) Options

WithScryptWorkFactor sets the scrypt work factor (2^logN) used when encrypting password-protected secrets. A higher value increases the cost of brute-forcing the passphrase at the expense of encryption and decryption time.

age fixes the other scrypt parameters at r=8, p=1, so only N=2^logN varies. Each increment of logN doubles both the derivation time and the memory required: derivation time is roughly linear in N (age calibrates logN=18 at about one second on a modern machine), and memory usage is exactly 2^logN KiB. The following table is anchored to age's own calibration; absolute times are approximate and vary by CPU, but the ratios are exact powers of two:

logN  memory    ~time/unlock  attacker cost vs. 18
----  --------  ------------  --------------------
 8    256 KiB   ~1 ms         1024x cheaper
10    1 MiB     ~4 ms         256x cheaper
12    4 MiB     ~16 ms        64x cheaper
14    16 MiB    ~60 ms        16x cheaper
15    32 MiB    ~125 ms       8x cheaper
16    64 MiB    ~250 ms       4x cheaper
17    128 MiB   ~500 ms       2x cheaper  (OWASP minimum)
18    256 MiB   ~1 s          baseline    (age default)
20    1 GiB     ~4 s          4x dearer
22    4 GiB     ~16 s         16x dearer   (MaxScryptWorkFactor)

Guidance on choosing a value:

  • The work factor is only a linear multiplier on the attacker's cost per guess; the passphrase's own entropy dominates security. A low-entropy, guessable passphrase is not rescued by a high work factor, and genuinely high-entropy random key material is safe even at a low one.
  • OWASP's Password Storage guidance recommends a minimum of N=2^17 (logN 17) with r=8, p=1 for human-chosen passphrases. Values at or below logN 12 fit in CPU cache, lose scrypt's memory-hardness, and degrade toward a plain fast hash that GPUs and ASICs can attack in parallel; reserve them for high-entropy key material or tests.
  • Pick the highest logN whose derivation time is tolerable on the weakest device that must unlock a secret (each unlock pays the cost once). Note that memory is consumed per concurrent derivation, so logN=20 costs 1 GiB per in-flight unlock and can exhaust constrained hosts.
  • Lowering the work factor is a deliberate security downgrade; make sure the inputs are high-entropy before doing so.

Valid values are 1..secretfile.MaxScryptWorkFactor. Values above the maximum are rejected because the default age decryption work-factor ceiling is 22; files written with a higher factor could not be decrypted by standard age tooling. If unset, the age default (logN=18) is used.

This option only affects password keys; it is a no-op for age and ssh keys. Because the work factor is recorded in each file's header, changing it does not affect the ability to decrypt secrets written with a previous value; the new factor applies only to secrets written after the change.

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

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