Skip to content

Commit 3716ec2

Browse files
committed
secrets: secret management for swarm
Signed-off-by: Evan Hazlett <[email protected]> wip: use tmpfs for swarm secrets Signed-off-by: Evan Hazlett <[email protected]> wip: inject secrets from swarm secret store Signed-off-by: Evan Hazlett <[email protected]> secrets: use secret names in cli for service create Signed-off-by: Evan Hazlett <[email protected]> switch to use mounts instead of volumes Signed-off-by: Evan Hazlett <[email protected]> vendor: use ehazlett swarmkit Signed-off-by: Evan Hazlett <[email protected]> secrets: finish secret update Signed-off-by: Evan Hazlett <[email protected]>
1 parent 1310dad commit 3716ec2

46 files changed

Lines changed: 1292 additions & 13 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

api/server/router/swarm/backend.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,9 @@ type Backend interface {
2323
RemoveNode(string, bool) error
2424
GetTasks(basictypes.TaskListOptions) ([]types.Task, error)
2525
GetTask(string) (types.Task, error)
26+
GetSecrets(opts basictypes.SecretListOptions) ([]types.Secret, error)
27+
CreateSecret(s types.SecretSpec) (string, error)
28+
RemoveSecret(id string) error
29+
GetSecret(id string) (types.Secret, error)
30+
UpdateSecret(id string, version uint64, spec types.SecretSpec) error
2631
}

api/server/router/swarm/cluster.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,10 @@ func (sr *swarmRouter) initRoutes() {
4040
router.NewPostRoute("/nodes/{id:.*}/update", sr.updateNode),
4141
router.NewGetRoute("/tasks", sr.getTasks),
4242
router.NewGetRoute("/tasks/{id:.*}", sr.getTask),
43+
router.NewGetRoute("/secrets", sr.getSecrets),
44+
router.NewPostRoute("/secrets/create", sr.createSecret),
45+
router.NewDeleteRoute("/secrets/{id:.*}", sr.removeSecret),
46+
router.NewGetRoute("/secrets/{id:.*}", sr.getSecret),
47+
router.NewPostRoute("/secrets/{id:.*}/update", sr.updateSecret),
4348
}
4449
}

api/server/router/swarm/cluster_routes.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,3 +261,77 @@ func (sr *swarmRouter) getTask(ctx context.Context, w http.ResponseWriter, r *ht
261261

262262
return httputils.WriteJSON(w, http.StatusOK, task)
263263
}
264+
265+
func (sr *swarmRouter) getSecrets(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
266+
if err := httputils.ParseForm(r); err != nil {
267+
return err
268+
}
269+
filter, err := filters.FromParam(r.Form.Get("filters"))
270+
if err != nil {
271+
return err
272+
}
273+
274+
secrets, err := sr.backend.GetSecrets(basictypes.SecretListOptions{Filter: filter})
275+
if err != nil {
276+
logrus.Errorf("Error getting secrets: %v", err)
277+
return err
278+
}
279+
280+
return httputils.WriteJSON(w, http.StatusOK, secrets)
281+
}
282+
283+
func (sr *swarmRouter) createSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
284+
var secret types.SecretSpec
285+
if err := json.NewDecoder(r.Body).Decode(&secret); err != nil {
286+
return err
287+
}
288+
289+
id, err := sr.backend.CreateSecret(secret)
290+
if err != nil {
291+
logrus.Errorf("Error creating secret %s: %v", id, err)
292+
return err
293+
}
294+
295+
return httputils.WriteJSON(w, http.StatusCreated, &basictypes.SecretCreateResponse{
296+
ID: id,
297+
})
298+
}
299+
300+
func (sr *swarmRouter) removeSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
301+
if err := sr.backend.RemoveSecret(vars["id"]); err != nil {
302+
logrus.Errorf("Error removing secret %s: %v", vars["id"], err)
303+
return err
304+
}
305+
306+
return nil
307+
}
308+
309+
func (sr *swarmRouter) getSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
310+
secret, err := sr.backend.GetSecret(vars["id"])
311+
if err != nil {
312+
logrus.Errorf("Error getting secret %s: %v", vars["id"], err)
313+
return err
314+
}
315+
316+
return httputils.WriteJSON(w, http.StatusOK, secret)
317+
}
318+
319+
func (sr *swarmRouter) updateSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
320+
var secret types.SecretSpec
321+
if err := json.NewDecoder(r.Body).Decode(&secret); err != nil {
322+
return err
323+
}
324+
325+
rawVersion := r.URL.Query().Get("version")
326+
version, err := strconv.ParseUint(rawVersion, 10, 64)
327+
if err != nil {
328+
return fmt.Errorf("Invalid secret version '%s': %s", rawVersion, err.Error())
329+
}
330+
331+
id := vars["id"]
332+
if err := sr.backend.UpdateSecret(id, version, secret); err != nil {
333+
return fmt.Errorf("Error updating secret: %s", err)
334+
}
335+
336+
return nil
337+
}

api/types/container/secret.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package container
2+
3+
import "os"
4+
5+
type ContainerSecret struct {
6+
Name string
7+
Target string
8+
Data []byte
9+
Uid int
10+
Gid int
11+
Mode os.FileMode
12+
}

api/types/swarm/container.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,5 @@ type ContainerSpec struct {
3737
StopGracePeriod *time.Duration `json:",omitempty"`
3838
Healthcheck *container.HealthConfig `json:",omitempty"`
3939
DNSConfig *DNSConfig `json:",omitempty"`
40+
Secrets []*SecretReference `json:",omitempty"`
4041
}

api/types/swarm/secret.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package swarm
2+
3+
// Secret represents a secret.
4+
type Secret struct {
5+
ID string
6+
Meta
7+
Spec *SecretSpec `json:",omitempty"`
8+
Digest string `json:",omitempty"`
9+
SecretSize int64 `json:",omitempty"`
10+
}
11+
12+
type SecretSpec struct {
13+
Annotations
14+
Data []byte `json",omitempty"`
15+
}
16+
17+
type SecretReferenceMode int
18+
19+
const (
20+
SecretReferenceSystem SecretReferenceMode = 0
21+
SecretReferenceFile SecretReferenceMode = 1
22+
SecretReferenceEnv SecretReferenceMode = 2
23+
)
24+
25+
type SecretReference struct {
26+
SecretID string `json:",omitempty"`
27+
Mode SecretReferenceMode `json:",omitempty"`
28+
Target string `json:",omitempty"`
29+
SecretName string `json:",omitempty"`
30+
}

api/types/types.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"time"
77

88
"github.com/docker/docker/api/types/container"
9+
"github.com/docker/docker/api/types/filters"
910
"github.com/docker/docker/api/types/mount"
1011
"github.com/docker/docker/api/types/network"
1112
"github.com/docker/docker/api/types/registry"
@@ -509,3 +510,15 @@ type ImagesPruneReport struct {
509510
type NetworksPruneReport struct {
510511
NetworksDeleted []string
511512
}
513+
514+
// SecretCreateResponse contains the information returned to a client
515+
// on the creation of a new secret.
516+
type SecretCreateResponse struct {
517+
// ID is the id of the created secret.
518+
ID string
519+
}
520+
521+
// SecretListOptions holds parameters to list secrets
522+
type SecretListOptions struct {
523+
Filter filters.Args
524+
}

cli/command/commands/commands.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/docker/docker/cli/command/node"
1212
"github.com/docker/docker/cli/command/plugin"
1313
"github.com/docker/docker/cli/command/registry"
14+
"github.com/docker/docker/cli/command/secret"
1415
"github.com/docker/docker/cli/command/service"
1516
"github.com/docker/docker/cli/command/stack"
1617
"github.com/docker/docker/cli/command/swarm"
@@ -25,6 +26,7 @@ func AddCommands(cmd *cobra.Command, dockerCli *command.DockerCli) {
2526
node.NewNodeCommand(dockerCli),
2627
service.NewServiceCommand(dockerCli),
2728
swarm.NewSwarmCommand(dockerCli),
29+
secret.NewSecretCommand(dockerCli),
2830
container.NewContainerCommand(dockerCli),
2931
image.NewImageCommand(dockerCli),
3032
system.NewSystemCommand(dockerCli),

cli/command/secret/cmd.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package secret
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/spf13/cobra"
7+
8+
"github.com/docker/docker/cli"
9+
"github.com/docker/docker/cli/command"
10+
)
11+
12+
// NewSecretCommand returns a cobra command for `secret` subcommands
13+
func NewSecretCommand(dockerCli *command.DockerCli) *cobra.Command {
14+
cmd := &cobra.Command{
15+
Use: "secret",
16+
Short: "Manage Docker secrets",
17+
Args: cli.NoArgs,
18+
Run: func(cmd *cobra.Command, args []string) {
19+
fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString())
20+
},
21+
}
22+
cmd.AddCommand(
23+
newSecretListCommand(dockerCli),
24+
newSecretCreateCommand(dockerCli),
25+
newSecretInspectCommand(dockerCli),
26+
newSecretRemoveCommand(dockerCli),
27+
)
28+
return cmd
29+
}

cli/command/secret/create.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package secret
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"io/ioutil"
7+
"os"
8+
9+
"github.com/docker/docker/api/types/swarm"
10+
"github.com/docker/docker/cli"
11+
"github.com/docker/docker/cli/command"
12+
"github.com/spf13/cobra"
13+
)
14+
15+
type createOptions struct {
16+
name string
17+
}
18+
19+
func newSecretCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
20+
return &cobra.Command{
21+
Use: "create [name]",
22+
Short: "Create a secret using stdin as content",
23+
Args: cli.ExactArgs(1),
24+
RunE: func(cmd *cobra.Command, args []string) error {
25+
opts := createOptions{
26+
name: args[0],
27+
}
28+
29+
return runSecretCreate(dockerCli, opts)
30+
},
31+
}
32+
}
33+
34+
func runSecretCreate(dockerCli *command.DockerCli, opts createOptions) error {
35+
client := dockerCli.Client()
36+
ctx := context.Background()
37+
38+
secretData, err := ioutil.ReadAll(os.Stdin)
39+
if err != nil {
40+
return fmt.Errorf("Error reading content from STDIN: %v", err)
41+
}
42+
43+
spec := swarm.SecretSpec{
44+
Annotations: swarm.Annotations{
45+
Name: opts.name,
46+
},
47+
Data: secretData,
48+
}
49+
50+
r, err := client.SecretCreate(ctx, spec)
51+
if err != nil {
52+
return err
53+
}
54+
55+
fmt.Fprintln(dockerCli.Out(), r.ID)
56+
return nil
57+
}

0 commit comments

Comments
 (0)