secret.go 8.6 KB

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