Skip to content

Commit 23bf890

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

File tree

2 files changed

+193
-38
lines changed

2 files changed

+193
-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: 174 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(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,87 @@ 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

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:
225+
To verify your plugin works, run the binary and it should connect to the
226+
Secrets Engine.
124227

125-
```console
126-
$ curl --unix-socket ~/Library/Caches/docker-secrets-engine/engine.sock \
228+
As a quick test we can retrieve secrets using `curl`, when running standalone
229+
the default socket is `daemon.sock` and with Docker Desktop it is `engine.sock`.
230+
Below we will query the Secrets Engine in standalone mode.
231+
232+
```bash
233+
curl --unix-socket ~/Library/Caches/docker-secrets-engine/daemon.sock \
127234
-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"}
235+
-H "Content-Type: application/json" \
236+
-d '{"pattern": "myrealm/**"}'
237+
```
238+
239+
The value of a secret is always encoded into base64.
240+
When using Go's `json.Unmarshal` it will automatically convert it back into
241+
a slice of bytes `[]byte`.
242+
243+
To manually decode it, you can pipe the value into `base64 --decode -i`.
244+
245+
```bash
246+
echo "<base64 string>" | base64 --decode -i
130247
```
248+
249+
## Legal
250+
251+
_Brought to you courtesy of our legal counsel. For more context,
252+
see the [NOTICE](https://github.com/docker/secrets-engine/blob/main/NOTICE) document in this repo._
253+
254+
Use and transfer of Docker may be subject to certain restrictions by the
255+
United States and other governments.
256+
257+
It is your responsibility to ensure that your use and/or transfer does not
258+
violate applicable laws.
259+
260+
For more information, see https://www.bis.doc.gov
261+
262+
## Licensing
263+
264+
docker/secrets-engine is licensed under the Apache License, Version 2.0. See
265+
[LICENSE](https://github.com/docker/secrets-engine/blob/main/LICENSE) for the full
266+
license text.

0 commit comments

Comments
 (0)