github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/controlapi/secret.go (about) 1 package controlapi 2 3 import ( 4 "context" 5 "crypto/subtle" 6 "strings" 7 8 "github.com/docker/swarmkit/api" 9 "github.com/docker/swarmkit/api/validation" 10 "github.com/docker/swarmkit/identity" 11 "github.com/docker/swarmkit/log" 12 "github.com/docker/swarmkit/manager/state/store" 13 "github.com/sirupsen/logrus" 14 "google.golang.org/grpc/codes" 15 "google.golang.org/grpc/status" 16 ) 17 18 // assumes spec is not nil 19 func secretFromSecretSpec(spec *api.SecretSpec) *api.Secret { 20 return &api.Secret{ 21 ID: identity.NewID(), 22 Spec: *spec, 23 } 24 } 25 26 // GetSecret returns a `GetSecretResponse` with a `Secret` with the same 27 // id as `GetSecretRequest.SecretID` 28 // - Returns `NotFound` if the Secret with the given id is not found. 29 // - Returns `InvalidArgument` if the `GetSecretRequest.SecretID` is empty. 30 // - Returns an error if getting fails. 31 func (s *Server) GetSecret(ctx context.Context, request *api.GetSecretRequest) (*api.GetSecretResponse, error) { 32 if request.SecretID == "" { 33 return nil, status.Errorf(codes.InvalidArgument, "secret ID must be provided") 34 } 35 36 var secret *api.Secret 37 s.store.View(func(tx store.ReadTx) { 38 secret = store.GetSecret(tx, request.SecretID) 39 }) 40 41 if secret == nil { 42 return nil, status.Errorf(codes.NotFound, "secret %s not found", request.SecretID) 43 } 44 45 secret.Spec.Data = nil // clean the actual secret data so it's never returned 46 return &api.GetSecretResponse{Secret: secret}, nil 47 } 48 49 // UpdateSecret updates a Secret referenced by SecretID with the given SecretSpec. 50 // - Returns `NotFound` if the Secret is not found. 51 // - Returns `InvalidArgument` if the SecretSpec is malformed or anything other than Labels is changed 52 // - Returns an error if the update fails. 53 func (s *Server) UpdateSecret(ctx context.Context, request *api.UpdateSecretRequest) (*api.UpdateSecretResponse, error) { 54 if request.SecretID == "" || request.SecretVersion == nil { 55 return nil, status.Errorf(codes.InvalidArgument, errInvalidArgument.Error()) 56 } 57 var secret *api.Secret 58 err := s.store.Update(func(tx store.Tx) error { 59 secret = store.GetSecret(tx, request.SecretID) 60 if secret == nil { 61 return status.Errorf(codes.NotFound, "secret %s not found", request.SecretID) 62 } 63 64 // Check if the Name is different than the current name, or the secret is non-nil and different 65 // than the current secret 66 if secret.Spec.Annotations.Name != request.Spec.Annotations.Name || 67 (request.Spec.Data != nil && subtle.ConstantTimeCompare(request.Spec.Data, secret.Spec.Data) == 0) { 68 return status.Errorf(codes.InvalidArgument, "only updates to Labels are allowed") 69 } 70 71 // We only allow updating Labels 72 secret.Meta.Version = *request.SecretVersion 73 secret.Spec.Annotations.Labels = request.Spec.Annotations.Labels 74 75 return store.UpdateSecret(tx, secret) 76 }) 77 if err != nil { 78 return nil, err 79 } 80 81 log.G(ctx).WithFields(logrus.Fields{ 82 "secret.ID": request.SecretID, 83 "secret.Name": request.Spec.Annotations.Name, 84 "method": "UpdateSecret", 85 }).Debugf("secret updated") 86 87 // WARN: we should never return the actual secret data here. We need to redact the private fields first. 88 secret.Spec.Data = nil 89 return &api.UpdateSecretResponse{ 90 Secret: secret, 91 }, nil 92 } 93 94 // ListSecrets returns a `ListSecretResponse` with a list all non-internal `Secret`s being 95 // managed, or all secrets matching any name in `ListSecretsRequest.Names`, any 96 // name prefix in `ListSecretsRequest.NamePrefixes`, any id in 97 // `ListSecretsRequest.SecretIDs`, or any id prefix in `ListSecretsRequest.IDPrefixes`. 98 // - Returns an error if listing fails. 99 func (s *Server) ListSecrets(ctx context.Context, request *api.ListSecretsRequest) (*api.ListSecretsResponse, error) { 100 var ( 101 secrets []*api.Secret 102 respSecrets []*api.Secret 103 err error 104 byFilters []store.By 105 by store.By 106 labels map[string]string 107 ) 108 109 // return all secrets that match either any of the names or any of the name prefixes (why would you give both?) 110 if request.Filters != nil { 111 for _, name := range request.Filters.Names { 112 byFilters = append(byFilters, store.ByName(name)) 113 } 114 for _, prefix := range request.Filters.NamePrefixes { 115 byFilters = append(byFilters, store.ByNamePrefix(prefix)) 116 } 117 for _, prefix := range request.Filters.IDPrefixes { 118 byFilters = append(byFilters, store.ByIDPrefix(prefix)) 119 } 120 labels = request.Filters.Labels 121 } 122 123 switch len(byFilters) { 124 case 0: 125 by = store.All 126 case 1: 127 by = byFilters[0] 128 default: 129 by = store.Or(byFilters...) 130 } 131 132 s.store.View(func(tx store.ReadTx) { 133 secrets, err = store.FindSecrets(tx, by) 134 }) 135 if err != nil { 136 return nil, err 137 } 138 139 // strip secret data from the secret, filter by label, and filter out all internal secrets 140 for _, secret := range secrets { 141 if secret.Internal || !filterMatchLabels(secret.Spec.Annotations.Labels, labels) { 142 continue 143 } 144 secret.Spec.Data = nil // clean the actual secret data so it's never returned 145 respSecrets = append(respSecrets, secret) 146 } 147 148 return &api.ListSecretsResponse{Secrets: respSecrets}, nil 149 } 150 151 // CreateSecret creates and returns a `CreateSecretResponse` with a `Secret` based 152 // on the provided `CreateSecretRequest.SecretSpec`. 153 // - Returns `InvalidArgument` if the `CreateSecretRequest.SecretSpec` is malformed, 154 // or if the secret data is too long or contains invalid characters. 155 // - Returns an error if the creation fails. 156 func (s *Server) CreateSecret(ctx context.Context, request *api.CreateSecretRequest) (*api.CreateSecretResponse, error) { 157 if err := validateSecretSpec(request.Spec); err != nil { 158 return nil, err 159 } 160 161 if request.Spec.Driver != nil { // Check that the requested driver is valid 162 if _, err := s.dr.NewSecretDriver(request.Spec.Driver); err != nil { 163 return nil, err 164 } 165 } 166 167 secret := secretFromSecretSpec(request.Spec) // the store will handle name conflicts 168 err := s.store.Update(func(tx store.Tx) error { 169 return store.CreateSecret(tx, secret) 170 }) 171 172 switch err { 173 case store.ErrNameConflict: 174 return nil, status.Errorf(codes.AlreadyExists, "secret %s already exists", request.Spec.Annotations.Name) 175 case nil: 176 secret.Spec.Data = nil // clean the actual secret data so it's never returned 177 log.G(ctx).WithFields(logrus.Fields{ 178 "secret.Name": request.Spec.Annotations.Name, 179 "method": "CreateSecret", 180 }).Debugf("secret created") 181 182 return &api.CreateSecretResponse{Secret: secret}, nil 183 default: 184 return nil, err 185 } 186 } 187 188 // RemoveSecret removes the secret referenced by `RemoveSecretRequest.ID`. 189 // - Returns `InvalidArgument` if `RemoveSecretRequest.ID` is empty. 190 // - Returns `NotFound` if the a secret named `RemoveSecretRequest.ID` is not found. 191 // - Returns `SecretInUse` if the secret is currently in use 192 // - Returns an error if the deletion fails. 193 func (s *Server) RemoveSecret(ctx context.Context, request *api.RemoveSecretRequest) (*api.RemoveSecretResponse, error) { 194 if request.SecretID == "" { 195 return nil, status.Errorf(codes.InvalidArgument, "secret ID must be provided") 196 } 197 198 err := s.store.Update(func(tx store.Tx) error { 199 // Check if the secret exists 200 secret := store.GetSecret(tx, request.SecretID) 201 if secret == nil { 202 return status.Errorf(codes.NotFound, "could not find secret %s", request.SecretID) 203 } 204 205 // Check if any services currently reference this secret, return error if so 206 services, err := store.FindServices(tx, store.ByReferencedSecretID(request.SecretID)) 207 if err != nil { 208 return status.Errorf(codes.Internal, "could not find services using secret %s: %v", request.SecretID, err) 209 } 210 211 if len(services) != 0 { 212 serviceNames := make([]string, 0, len(services)) 213 for _, service := range services { 214 serviceNames = append(serviceNames, service.Spec.Annotations.Name) 215 } 216 217 secretName := secret.Spec.Annotations.Name 218 serviceNameStr := strings.Join(serviceNames, ", ") 219 serviceStr := "services" 220 if len(serviceNames) == 1 { 221 serviceStr = "service" 222 } 223 224 return status.Errorf(codes.InvalidArgument, "secret '%s' is in use by the following %s: %v", secretName, serviceStr, serviceNameStr) 225 } 226 227 return store.DeleteSecret(tx, request.SecretID) 228 }) 229 switch err { 230 case store.ErrNotExist: 231 return nil, status.Errorf(codes.NotFound, "secret %s not found", request.SecretID) 232 case nil: 233 log.G(ctx).WithFields(logrus.Fields{ 234 "secret.ID": request.SecretID, 235 "method": "RemoveSecret", 236 }).Debugf("secret removed") 237 238 return &api.RemoveSecretResponse{}, nil 239 default: 240 return nil, err 241 } 242 } 243 244 func validateSecretSpec(spec *api.SecretSpec) error { 245 if spec == nil { 246 return status.Errorf(codes.InvalidArgument, errInvalidArgument.Error()) 247 } 248 if err := validateConfigOrSecretAnnotations(spec.Annotations); err != nil { 249 return err 250 } 251 // Check if secret driver is defined 252 if spec.Driver != nil { 253 // Ensure secret driver has a name 254 if spec.Driver.Name == "" { 255 return status.Errorf(codes.InvalidArgument, "secret driver must have a name") 256 } 257 return nil 258 } 259 if err := validation.ValidateSecretPayload(spec.Data); err != nil { 260 return status.Errorf(codes.InvalidArgument, "%s", err.Error()) 261 } 262 return nil 263 }