From 855e6ee3a0d98b12b226e6cf12ac7f7fe44a295b Mon Sep 17 00:00:00 2001 From: Karolis Rusenas Date: Fri, 28 Jul 2017 22:54:02 +0100 Subject: [PATCH] adding secrets getter --- secrets/secrets.go | 142 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 secrets/secrets.go diff --git a/secrets/secrets.go b/secrets/secrets.go new file mode 100644 index 00000000..1252e8db --- /dev/null +++ b/secrets/secrets.go @@ -0,0 +1,142 @@ +package secrets + +import ( + "encoding/json" + "errors" + "net/url" + + "github.com/rusenask/keel/provider/kubernetes" + "github.com/rusenask/keel/types" + + "k8s.io/client-go/pkg/api/v1" + + log "github.com/Sirupsen/logrus" +) + +const dockerConfigJSONKey = ".dockerconfigjson" + +var ( + ErrNamespaceNotSpecified = errors.New("namespace not specified") + ErrSecretsNotSpecified = errors.New("no secrets were specified") +) + +type Getter interface { + Get(image *types.TrackedImage) (*types.Credentials, error) +} + +type DefaultGetter struct { + kubernetesImplementer kubernetes.Implementer +} + +func NewGetter(implementer kubernetes.Implementer) *DefaultGetter { + return &DefaultGetter{ + kubernetesImplementer: implementer, + } +} + +func (g *DefaultGetter) Get(image *types.TrackedImage) (*types.Credentials, error) { + if image.Namespace == "" { + return nil, ErrNamespaceNotSpecified + } + + if len(image.Secrets) == 0 { + return nil, ErrSecretsNotSpecified + } + return g.getCredentialsFromSecret(image) +} + +func (g *DefaultGetter) getCredentialsFromSecret(image *types.TrackedImage) (*types.Credentials, error) { + + credentials := &types.Credentials{} + + for _, secretRef := range image.Secrets { + secret, err := g.kubernetesImplementer.Secret(image.Namespace, secretRef) + if err != nil { + log.WithFields(log.Fields{ + "image": image.Image.Repository(), + "namespace": image.Namespace, + "secret_ref": secretRef, + "error": err, + }).Warn("secrets.defaultGetter: failed to get secret") + continue + } + + if secret.Type != v1.SecretTypeDockercfg { + log.WithFields(log.Fields{ + "image": image.Image.Repository(), + "namespace": image.Namespace, + "secret_ref": secretRef, + "type": secret.Type, + }).Warn("secrets.defaultGetter: supplied secret is not kubernetes.io/dockerconfigjson, ignoring") + continue + } + + secretDataBts := secret.Data[dockerConfigJSONKey] + dockerCfg, err := decodeSecret(secretDataBts) + if err != nil { + log.WithFields(log.Fields{ + "image": image.Image.Repository(), + "namespace": image.Namespace, + "secret_ref": secretRef, + "error": err, + }).Error("secrets.defaultGetter: failed to decode secret") + continue + } + + // looking for our registry + for registry, auth := range dockerCfg { + h, err := hostname(registry) + if err != nil { + log.WithFields(log.Fields{ + "image": image.Image.Repository(), + "namespace": image.Namespace, + "registry": registry, + "secret_ref": secretRef, + "error": err, + }).Error("secrets.defaultGetter: failed to parse hostname") + continue + } + + if h == image.Image.Registry() { + credentials.Username = auth.Username + credentials.Password = auth.Password + return credentials, nil + } + log.WithFields(log.Fields{ + "registry": registry, + "want": image.Image.Registry(), + }).Info("scanning registries") + } + + } + + return credentials, nil +} + +func hostname(registry string) (string, error) { + u, err := url.Parse(registry) + if err != nil { + return "", err + } + return u.Hostname(), nil +} + +func decodeSecret(data []byte) (DockerCfg, error) { + var cfg DockerCfg + err := json.Unmarshal(data, &cfg) + if err != nil { + return nil, err + } + return cfg, nil +} + +// DockerCfg - registry_name=auth +type DockerCfg map[string]*Auth + +// Auth - auth +type Auth struct { + Username string `json:"username"` + Password string `json:"password"` + Email string `json:"email"` + Auth string `json:"auth"` +}