Skip to content

Commit 930efff

Browse files
committed
chore: update readme
Signed-off-by: Alano Terblanche <18033717+Benehiko@users.noreply.github.com>
1 parent e4e28d7 commit 930efff

File tree

2 files changed

+191
-34
lines changed

2 files changed

+191
-34
lines changed

NOTICE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Docker
2+
Copyright 2012-2026 Docker, Inc.
3+
4+
This product includes software developed at Docker, Inc. (https://www.docker.com).
5+
6+
This product contains software (https://github.com/keybase/go-keychain) developed
7+
by Keybase, licensed under the MIT License.
8+
9+
The following is courtesy of our legal counsel:
10+
11+
12+
Use and transfer of Docker may be subject to certain restrictions by the
13+
United States and other governments.
14+
It is your responsibility to ensure that your use and/or transfer does not
15+
violate applicable laws.
16+
17+
For more information, see https://www.bis.doc.gov
18+
19+
See also https://www.apache.org/dev/crypto.html and/or seek legal counsel.

README.md

Lines changed: 172 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,97 @@
44
[![lint](https://github.com/docker/secrets-engine/actions/workflows/lint.yml/badge.svg?branch=main)](https://github.com/docker/secrets-engine/actions/workflows/lint.yml)
55
[![License](https://img.shields.io/badge/license-Apache%202.0-purple)](https://github.com/docker/secrets-engine/blob/main/LICENSE)
66

7-
## Quickstart
7+
Secrets Engine and [docker pass](https://docs.docker.com/reference/cli/docker/pass/)
8+
are bundled with [Docker Desktop](https://docs.docker.com/desktop/).
9+
A standalone version can also be installed from the [releases](https://github.com/docker/secrets-engine/releases).
810

9-
Secrets Engine and [docker pass](https://docs.docker.com/reference/cli/docker/pass/) are bundled with [Docker Desktop](https://docs.docker.com/desktop/).
11+
> [!NOTE]
12+
> Secret injection in Docker CE is on our roadmap.
1013
11-
Let's store a secret using `docker pass` in the OS Keychain and then inject it
12-
into a running container using Secrets Engine.
14+
## Runtime secret injection (no plaintext in your CLI or Compose)
1315

14-
```console
15-
# Store `Foo` in the OS Keychain
16-
$ docker pass set Foo=bar
17-
# Tell docker to use the Secrets Engine using the `se://` URI on an environment variable
18-
$ docker run --rm -e Foo=se://Foo busybox /bin/sh -c "echo \${Foo}"
19-
$ bar
16+
Secrets Engine lets you **reference** secrets in `docker run` / `docker compose`
17+
and have Docker **resolve and inject** the real values _at runtime_.
18+
19+
**Key idea:** you pass a _pointer_, not the secret.
20+
21+
- In your config (CLI flags / Compose files), you use a `se://` reference like `se://foo`.
22+
- When the container starts, Docker asks Secrets Engine to resolve that reference
23+
and injects the secret into the container.
24+
- The secret value is sourced from a provider, such as **`docker pass`**, which
25+
stores secrets securely in your **local OS keychain** (or from a custom provider plugin).
26+
27+
This means you don’t need:
28+
29+
- host environment variables containing secret values
30+
- plaintext secret files on disk
31+
- secret literals embedded in `compose.yaml`
32+
33+
### Example: store once, use everywhere
34+
35+
Store the secret in your OS keychain:
36+
37+
```bash
38+
docker pass set foo=bar
39+
```
40+
41+
Run a container using a secret reference (the value se://foo is not the secret itself):
42+
43+
```bash
44+
docker run --rm -e foo=se://foo busybox sh -c 'echo "$foo"'
45+
```
46+
47+
Compose example:
48+
49+
```yaml
50+
services:
51+
app:
52+
image: your/image
53+
environment:
54+
API_TOKEN: se://foo
2055
```
2156
57+
### Realms (namespace + pattern matching)
58+
59+
Secrets Engine supports **realms**: a simple namespacing scheme that helps you
60+
organize secrets by purpose, environment, or target system, and then retrieve
61+
them using glob-style patterns.
62+
63+
A realm is just a prefix in the secret key. For example:
64+
65+
- `docker/auth/hub/mysecret`
66+
- `docker/auth/ghcr/token`
67+
- `docker/db/prod/password`
68+
69+
Because the realm is part of the key, you can query or operate on groups of
70+
secrets using patterns. For example, to target _all_ Docker auth-related secrets:
71+
72+
- `docker/auth/**`
73+
74+
This makes it easy to:
75+
76+
- keep related secrets grouped together
77+
- separate environments (e.g. `prod/`, `staging/`, `dev/`)
78+
- scope listing/lookup operations to a subset of secrets without knowing every
79+
key ahead of time
80+
81+
#### Example layout
82+
83+
```text
84+
docker/
85+
auth/
86+
hub/
87+
mysecret
88+
ghcr/
89+
token
90+
db/
91+
prod/
92+
password
93+
```
94+
95+
> [!TIP]
96+
> Treat realms like paths - predictable structure makes automation and access control much easier.
97+
2298
# Developer Guides
2399

24100
## How to query secrets
@@ -38,11 +114,16 @@ if err != nil {
38114
}
39115
40116
// Fetch a secret from the engine
41-
resp, err := c.GetSecrets(t.Context(), client.MustParsePattern("my-secret"))
117+
// We are using an exact match here, so only one or zero results will return.
118+
secrets, err := c.GetSecrets(t.Context(), client.MustParsePattern("my-secret"))
42119
if err != nil {
43120
log.Fatalf("failed fetching secrets: %v", err)
44121
}
45-
fmt.Println(resp.Value)
122+
// Since we used an exact match, only one secret should be present.
123+
if len(secrets) == 0 {
124+
log.Fatalf("hmm... seems like the plugin isn't returning an ErrNotFound")
125+
}
126+
fmt.Println(secrets[0].Value)
46127
```
47128

48129
## How to create a plugin
@@ -62,17 +143,17 @@ var _ plugin.Plugin = &myPlugin{}
62143
63144
type myPlugin struct {
64145
m sync.Mutex
65-
secrets map[secrets.ID]string
146+
secrets map[plugin.ID]string
66147
}
67148
68-
func (p *myPlugin) GetSecret(_ context.Context, request secrets.Request) ([]secrets.Envelope, error) {
149+
func (p *myPlugin) GetSecrets(_ context.Context, pattern plugin.Pattern) ([]plugin.Envelope, error) {
69150
p.m.Lock()
70151
defer p.m.Unlock()
71152
72-
var result []secrets.Envelope
153+
var result []plugin.Envelope
73154
for id, value := range p.secrets {
74-
if request.Pattern.Match(id) {
75-
result = append(result, secrets.Envelope{
155+
if pattern.Match(id) {
156+
result = append(result, plugin.Envelope{
76157
ID: id,
77158
Value: []byte(value),
78159
CreatedAt: time.Now(),
@@ -82,11 +163,11 @@ func (p *myPlugin) GetSecret(_ context.Context, request secrets.Request) ([]secr
82163
return result, nil
83164
}
84165
85-
func (p *myPlugin) Config() plugin.Config {
86-
return plugin.Config{
87-
Version: "v0.0.1",
88-
Pattern: "*",
89-
}
166+
func (p *myPlugin) Run(ctx context.Context) error {
167+
// add long-running tasks here
168+
// for example, OAuth tokens can be refreshed here.
169+
<-ctx.Done()
170+
return nil
90171
}
91172
```
92173

@@ -99,32 +180,89 @@ package main
99180
100181
import (
101182
"context"
183+
"log/slog"
102184
103185
"github.com/docker/secrets-engine/plugin"
104186
)
105187
188+
type myLogger struct{}
189+
190+
func (m *myLogger) Errorf(format string, v ...any) {
191+
slog.Error(fmt.Sprintf(format, v...))
192+
}
193+
194+
func (m *myLogger) Printf(format string, v ...any) {
195+
slog.Info(fmt.Sprintf(format, v...))
196+
}
197+
198+
func (m *myLogger) Warnf(format string, v ...any) {
199+
slog.Warn(fmt.Sprintf(format, v...))
200+
}
201+
106202
func main() {
107-
p, err := plugin.New(&myPlugin{secrets: map[secrets.ID]string{"foo": "bar"}})
108-
if err != nil {
109-
panic(err)
110-
}
111-
// Run your plugin
112-
if err := p.Run(context.Background()); err != nil {
113-
panic(err)
203+
config := plugin.Config{
204+
Version: plugin.MustNewVersion("v0.0.1"),
205+
Pattern: plugin.MustParsePattern("myrealm/**"),
206+
// custom logger
207+
Logger: &myLogger{},
208+
}
209+
secrets := map[plugin.ID]string{
210+
plugin.MustParseID("myrealm/foo"): "bar",
114211
}
212+
p, err := plugin.New(&myPlugin{secrets: secrets}, config)
213+
if err != nil {
214+
panic(err)
215+
}
216+
// Run your plugin
217+
if err := p.Run(context.Background()); err != nil {
218+
panic(err)
219+
}
115220
}
116221
```
117222

118-
### 3. Test and verify the plugin:
223+
### 3. Query secrets from your plugin:
119224

120225
The secrets engine is integrated with Docker Desktop.
121226
To verify your plugin works, run the binary.
122227
Using the SDK it will automatically connect to the secrets engine in Docker Desktop.
123228
Then, you can query secrets, e.g. using curl:
124229

125-
```console
126-
$ curl --unix-socket ~/Library/Caches/docker-secrets-engine/engine.sock \
230+
```bash
231+
curl --unix-socket ~/Library/Caches/docker-secrets-engine/daemon.sock \
127232
-X POST http://localhost/resolver.v1.ResolverService/GetSecrets \
128-
-H "Content-Type: application/json" -d '{"pattern": "foo"}'
129-
{"id":"foo","value":"bar","provider":"docker-pass","version":"","error":"","createdAt":"0001-01-01T00:00:00Z","resolvedAt":"2025-08-12T08:25:06.166714Z","expiresAt":"0001-01-01T00:00:00Z"}
233+
-H "Content-Type: application/json" \
234+
-d '{"pattern": "myrealm/**"}'
130235
```
236+
237+
> [!NOTE]
238+
> Querying Secrets Engine installed with Docker Desktop requires a different
239+
> socket: `~/Library/Caches/docker-secrets-engine/engine.sock`
240+
241+
The value of a secret is always encoded into base64.
242+
When using Go's `json.Unmarshal` it will automatically convert it back into
243+
a slice of bytes `[]byte`.
244+
245+
To manually decode it, you can pipe the value into `base64 --decode -i`.
246+
247+
```bash
248+
echo "<base64 string>" | base64 --decode -i
249+
```
250+
251+
## Legal
252+
253+
_Brought to you courtesy of our legal counsel. For more context,
254+
see the [NOTICE](https://github.com/docker/secrets-engine/blob/main/NOTICE) document in this repo._
255+
256+
Use and transfer of Docker may be subject to certain restrictions by the
257+
United States and other governments.
258+
259+
It is your responsibility to ensure that your use and/or transfer does not
260+
violate applicable laws.
261+
262+
For more information, see https://www.bis.doc.gov
263+
264+
## Licensing
265+
266+
docker/secrets-engine is licensed under the Apache License, Version 2.0. See
267+
[LICENSE](https://github.com/docker/secrets-engine/blob/main/LICENSE) for the full
268+
license text.

0 commit comments

Comments
 (0)