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  }