File: secrets.go

package info (click to toggle)
docker.io 26.1.5%2Bdfsg1-9
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 68,576 kB
  • sloc: sh: 5,748; makefile: 912; ansic: 664; asm: 228; python: 162
file content (101 lines) | stat: -rw-r--r-- 2,943 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package secrets

import (
	"fmt"
	"sync"

	"github.com/moby/swarmkit/v2/agent/exec"
	"github.com/moby/swarmkit/v2/api"
	"github.com/moby/swarmkit/v2/identity"
)

// secrets is a map that keeps all the currently available secrets to the agent
// mapped by secret ID.
type secrets struct {
	mu sync.RWMutex
	m  map[string]*api.Secret
}

// NewManager returns a place to store secrets.
func NewManager() exec.SecretsManager {
	return &secrets{
		m: make(map[string]*api.Secret),
	}
}

// Get returns a secret by ID.  If the secret doesn't exist, returns nil.
func (s *secrets) Get(secretID string) (*api.Secret, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()
	if s, ok := s.m[secretID]; ok {
		return s, nil
	}
	return nil, fmt.Errorf("secret %s not found", secretID)
}

// Add adds one or more secrets to the secret map.
func (s *secrets) Add(secrets ...api.Secret) {
	s.mu.Lock()
	defer s.mu.Unlock()
	for _, secret := range secrets {
		s.m[secret.ID] = secret.Copy()
	}
}

// Remove removes one or more secrets by ID from the secret map.  Succeeds
// whether or not the given IDs are in the map.
func (s *secrets) Remove(secrets []string) {
	s.mu.Lock()
	defer s.mu.Unlock()
	for _, secret := range secrets {
		delete(s.m, secret)
	}
}

// Reset removes all the secrets.
func (s *secrets) Reset() {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.m = make(map[string]*api.Secret)
}

// taskRestrictedSecretsProvider restricts the ids to the task.
type taskRestrictedSecretsProvider struct {
	secrets   exec.SecretGetter
	secretIDs map[string]struct{} // allow list of secret ids
	taskID    string              // ID of the task the provider restricts for
}

func (sp *taskRestrictedSecretsProvider) Get(secretID string) (*api.Secret, error) {
	if _, ok := sp.secretIDs[secretID]; !ok {
		return nil, fmt.Errorf("task not authorized to access secret %s", secretID)
	}

	// First check if the secret is available with the task specific ID, which is the concatenation
	// of the original secret ID and the task ID with a dot in between.
	// That is the case when a secret driver has returned DoNotReuse == true for a secret value.
	taskSpecificID := identity.CombineTwoIDs(secretID, sp.taskID)
	secret, err := sp.secrets.Get(taskSpecificID)
	if err != nil {
		// Otherwise, which is the default case, the secret is retrieved by its original ID.
		return sp.secrets.Get(secretID)
	}
	// For all intents and purposes, the rest of the flow should deal with the original secret ID.
	secret.ID = secretID
	return secret, err
}

// Restrict provides a getter that only allows access to the secrets
// referenced by the task.
func Restrict(secrets exec.SecretGetter, t *api.Task) exec.SecretGetter {
	sids := map[string]struct{}{}

	container := t.Spec.GetContainer()
	if container != nil {
		for _, ref := range container.Secrets {
			sids[ref.SecretID] = struct{}{}
		}
	}

	return &taskRestrictedSecretsProvider{secrets: secrets, secretIDs: sids, taskID: t.ID}
}