Skip to content

Commit 1158ce5

Browse files
authored
Merge pull request #463 from docker/update/readme
chore: update readme
2 parents e4e28d7 + df9cf92 commit 1158ce5

File tree

2 files changed

+201
-38
lines changed

2 files changed

+201
-38
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: 182 additions & 38 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 (such as `.env` files)
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 to
60+
organize secrets by purpose, environment, application, or target system, and then retrieve
61+
them using glob-style patterns.
62+
63+
A realm is 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(context.Background(), client.MustParsePattern("my-secret"))
119+
if errors.Is(err, client.ErrSecretNotFound) {
120+
log.Fatalf("no secret found")
121+
}
122+
// fallback to generic error
42123
if err != nil {
43124
log.Fatalf("failed fetching secrets: %v", err)
44125
}
45-
fmt.Println(resp.Value)
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,95 @@ package main
99180
100181
import (
101182
"context"
183+
"fmt"
184+
"log/slog"
102185
103186
"github.com/docker/secrets-engine/plugin"
104187
)
105188
189+
type myLogger struct{}
190+
191+
func (m *myLogger) Errorf(format string, v ...any) {
192+
slog.Error(fmt.Sprintf(format, v...))
193+
}
194+
195+
func (m *myLogger) Printf(format string, v ...any) {
196+
slog.Info(fmt.Sprintf(format, v...))
197+
}
198+
199+
func (m *myLogger) Warnf(format string, v ...any) {
200+
slog.Warn(fmt.Sprintf(format, v...))
201+
}
202+
106203
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)
204+
config := plugin.Config{
205+
Version: plugin.MustNewVersion("v0.0.1"),
206+
Pattern: plugin.MustParsePattern("myrealm/**"),
207+
// custom logger
208+
Logger: &myLogger{},
209+
}
210+
secrets := map[plugin.ID]string{
211+
plugin.MustParseID("myrealm/foo"): "bar",
114212
}
213+
p, err := plugin.New(&myPlugin{secrets: secrets}, config)
214+
if err != nil {
215+
panic(err)
216+
}
217+
// Run your plugin
218+
if err := p.Run(context.Background()); err != nil {
219+
panic(err)
220+
}
115221
}
116222
```
117223

118-
### 3. Test and verify the plugin:
224+
### 3. Query secrets from your plugin:
225+
226+
To verify your plugin works, run the binary and it should connect to the
227+
Secrets Engine.
119228

120-
The secrets engine is integrated with Docker Desktop.
121-
To verify your plugin works, run the binary.
122-
Using the SDK it will automatically connect to the secrets engine in Docker Desktop.
123-
Then, you can query secrets, e.g. using curl:
229+
As a quick test we can retrieve secrets using `curl`, when running standalone
230+
the default socket is `daemon.sock` and with Docker Desktop it is `engine.sock`.
231+
Below we will query the Secrets Engine in standalone mode.
124232

125-
```console
126-
$ curl --unix-socket ~/Library/Caches/docker-secrets-engine/engine.sock \
233+
```bash
234+
curl --unix-socket ~/Library/Caches/docker-secrets-engine/daemon.sock \
127235
-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"}
236+
-H "Content-Type: application/json" \
237+
-d '{"pattern": "myrealm/**"}'
130238
```
239+
240+
The value of a secret is always encoded into base64.
241+
When using Go's `json.Unmarshal` it will automatically convert it back into
242+
a slice of bytes `[]byte`.
243+
244+
To manually decode it, you can pipe the value into `base64`, using the
245+
flags appropriate for your platform:
246+
247+
```bash
248+
# macOS / BSD
249+
echo "<base64 string>" | base64 -D
250+
251+
# GNU/Linux (coreutils)
252+
echo "<base64 string>" | base64 --decode
253+
# or
254+
echo "<base64 string>" | base64 -d
255+
```
256+
257+
## Legal
258+
259+
_Brought to you courtesy of our legal counsel. For more context,
260+
see the [NOTICE](https://github.com/docker/secrets-engine/blob/main/NOTICE) document in this repo._
261+
262+
Use and transfer of Docker may be subject to certain restrictions by the
263+
United States and other governments.
264+
265+
It is your responsibility to ensure that your use and/or transfer does not
266+
violate applicable laws.
267+
268+
For more information, see https://www.bis.doc.gov
269+
270+
## Licensing
271+
272+
docker/secrets-engine is licensed under the Apache License, Version 2.0. See
273+
[LICENSE](https://github.com/docker/secrets-engine/blob/main/LICENSE) for the full
274+
license text.

0 commit comments

Comments
 (0)