manager/drivers/secrets.go
package drivers
import (
"fmt"
"github.com/moby/swarmkit/v2/api"
"github.com/moby/swarmkit/v2/api/naming"
"github.com/moby/swarmkit/v2/node/plugin"
)
const (
// SecretsProviderAPI is the endpoint for fetching secrets from plugins
SecretsProviderAPI = "/SecretProvider.GetSecret"
// SecretsProviderCapability is the secrets provider plugin capability identification
SecretsProviderCapability = "secretprovider"
)
// SecretDriver provides secrets from different stores
type SecretDriver struct {
plugin plugin.Plugin
}
// NewSecretDriver creates a new driver that provides third party secrets
func NewSecretDriver(plugin plugin.Plugin) *SecretDriver {
return &SecretDriver{plugin: plugin}
}
// Get gets a secret from the secret provider. The function returns: the secret value;
// a bool indicating whether the value should be reused across different tasks (defaults to false);
// and an error if either the spec or task are nil, if calling the driver returns an error, or if
// the driver returns an error in the payload.
func (d *SecretDriver) Get(spec *api.SecretSpec, task *api.Task) ([]byte, bool, error) {
if spec == nil {
return nil, false, fmt.Errorf("secret spec is nil")
}
if task == nil {
return nil, false, fmt.Errorf("task is nil")
}
var secretResp SecretsProviderResponse
secretReq := &SecretsProviderRequest{
SecretName: spec.Annotations.Name,
SecretLabels: spec.Annotations.Labels,
ServiceID: task.ServiceID,
ServiceName: task.ServiceAnnotations.Name,
ServiceLabels: task.ServiceAnnotations.Labels,
TaskID: task.ID,
TaskName: naming.Task(task),
TaskImage: task.Spec.GetContainer().Image,
NodeID: task.NodeID,
}
container := task.Spec.GetContainer()
if container != nil {
secretReq.ServiceHostname = container.Hostname
}
if task.Endpoint != nil && task.Endpoint.Spec != nil {
secretReq.ServiceEndpointSpec = &EndpointSpec{
Mode: int32(task.Endpoint.Spec.Mode),
}
for _, p := range task.Endpoint.Spec.Ports {
if p == nil {
continue
}
secretReq.ServiceEndpointSpec.Ports =
append(secretReq.ServiceEndpointSpec.Ports,
PortConfig{
Name: p.Name,
Protocol: int32(p.Protocol),
PublishedPort: p.PublishedPort,
TargetPort: p.TargetPort,
PublishMode: int32(p.PublishMode),
})
}
}
err := d.plugin.Client().Call(SecretsProviderAPI, secretReq, &secretResp)
if err != nil {
return nil, false, err
}
if secretResp.Err != "" {
return nil, secretResp.DoNotReuse, fmt.Errorf(secretResp.Err)
}
// Assign the secret value
return secretResp.Value, secretResp.DoNotReuse, nil
}
// SecretsProviderRequest is the secrets provider request.
type SecretsProviderRequest struct {
SecretName string `json:",omitempty"` // SecretName is the name of the secret to request from the plugin
SecretLabels map[string]string `json:",omitempty"` // SecretLabels capture environment names and other metadata pertaining to the secret
ServiceHostname string `json:",omitempty"` // ServiceHostname is the hostname of the service, can be used for x509 certificate
ServiceID string `json:",omitempty"` // ServiceID is the name of the service that requested the secret
ServiceName string `json:",omitempty"` // ServiceName is the name of the service that requested the secret
ServiceLabels map[string]string `json:",omitempty"` // ServiceLabels capture environment names and other metadata pertaining to the service
TaskID string `json:",omitempty"` // TaskID is the ID of the task that the secret will be assigned to
TaskName string `json:",omitempty"` // TaskName is the name of the task that the secret will be assigned to
TaskImage string `json:",omitempty"` // TaskName is the image of the task that the secret will be assigned to
NodeID string `json:",omitempty"` // NodeID is the ID of the node that the task will be executed on
ServiceEndpointSpec *EndpointSpec `json:",omitempty"` // ServiceEndpointSpec holds the specification for endpoints
}
// SecretsProviderResponse is the secrets provider response.
type SecretsProviderResponse struct {
Value []byte `json:",omitempty"` // Value is the value of the secret
Err string `json:",omitempty"` // Err is the error response of the plugin
// DoNotReuse indicates that the secret returned from this request should
// only be used for one task, and any further tasks should call the secret
// driver again.
DoNotReuse bool `json:",omitempty"`
}
// EndpointSpec represents the spec of an endpoint.
type EndpointSpec struct {
Mode int32 `json:",omitempty"`
Ports []PortConfig `json:",omitempty"`
}
// PortConfig represents the config of a port.
type PortConfig struct {
Name string `json:",omitempty"`
Protocol int32 `json:",omitempty"`
// TargetPort is the port inside the container
TargetPort uint32 `json:",omitempty"`
// PublishedPort is the port on the swarm hosts
PublishedPort uint32 `json:",omitempty"`
// PublishMode is the mode in which port is published
PublishMode int32 `json:",omitempty"`
}