-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[DOM-47678] Add support for passing buildkit secrets via k8s secrets (#…
…106) * Update docs for service Secrets * Add secrets support to ImageBuild CR - Adds the ability to pass k8s secret names by namespace / name to be consumed by the ImageBuild request using buildkit secrets. The `secrets` field is added to the CR as optional, and is fully backwards compatible with previous requests. Previously, the Helm chart supported exposing service level secrets into *all* builds, but this adds supports on a per request basis. Buildkit secrets must be mounted in via Dockerfile syntax: https://docs.docker.com/engine/reference/builder/#run---mounttypesecret Consumption via Dockerfile is therefore similar to: RUN --mount=type=secret,id=domino-compute/mysecret/foo cat /run/secrets/foo - For the secret to be accessible by Hephaestus, it must have the label `hephaestus-accessible: "true"`. This prevents the build service from having access to arbitrary secrets in the cluster and requires clients to specifically opt-in. - Additionally the `hephaestus-owned: "true"` label can be added to secrets to help manage their lifecycle. When set, the secret will be updated to specify the attached ImageBuild as the owner -- when ImageBuild resources are routinely purged by the service, those secrets will be cleaned up at the same time. This removes the burden of secret cleanup from clients, but changes the cleanup timing to be non-determinstic. The ClusterRole for Hephaestus is updated to allow for secret resource updates to support this feature.
- Loading branch information
1 parent
9c626eb
commit a927281
Showing
15 changed files
with
458 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package secrets | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/go-logr/logr" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/fields" | ||
"k8s.io/apimachinery/pkg/labels" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/client-go/kubernetes" | ||
"k8s.io/client-go/rest" | ||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" | ||
|
||
hephv1 "github.com/dominodatalab/hephaestus/pkg/api/hephaestus/v1" | ||
) | ||
|
||
// exists only so it can overridden by tests with a fake client | ||
var clientsetFunc = func(config *rest.Config) (kubernetes.Interface, error) { | ||
return kubernetes.NewForConfig(config) | ||
} | ||
|
||
func ReadSecrets( | ||
ctx context.Context, | ||
obj *hephv1.ImageBuild, | ||
log logr.Logger, | ||
cfg *rest.Config, | ||
scheme *runtime.Scheme, | ||
) (map[string][]byte, error) { | ||
clientset, err := clientsetFunc(cfg) | ||
if err != nil { | ||
return map[string][]byte{}, fmt.Errorf("failure to get kubernetes client: %w", err) | ||
} | ||
v1 := clientset.CoreV1() | ||
|
||
// Extracts secrets into data to pass to buildkit | ||
secretsData := make(map[string][]byte) | ||
for _, secretRef := range obj.Spec.Secrets { | ||
secretClient := v1.Secrets(secretRef.Namespace) | ||
|
||
path := strings.Join([]string{secretRef.Namespace, secretRef.Name}, "/") | ||
log.Info("Finding secret", "path", path) | ||
fields := fields.SelectorFromSet(map[string]string{"Namespace": secretRef.Namespace, "Name": secretRef.Name}) | ||
// prevent exfiltration of arbitrary secret values by using the presence of this label | ||
labels := labels.SelectorFromSet(map[string]string{hephv1.AccessLabel: "true"}) | ||
secrets, err := secretClient.List(ctx, | ||
metav1.ListOptions{FieldSelector: fields.String(), LabelSelector: labels.String()}) | ||
|
||
if err != nil { | ||
return map[string][]byte{}, fmt.Errorf("failure querying for secret %q: %w", path, err) | ||
} | ||
|
||
if len(secrets.Items) == 0 { | ||
return map[string][]byte{}, fmt.Errorf("secret %q unreadable or missing required label %q", path, hephv1.AccessLabel) | ||
} | ||
secret := &secrets.Items[0] | ||
|
||
// adopt the secret resource if hephaestus-owned is true to delete when ImageBuild is deleted | ||
if _, ok := secret.Labels[hephv1.OwnedLabel]; ok { | ||
log.Info("Taking ownership of secret", "owner", obj.Name, "secret", path) | ||
|
||
// non-fatal error thats logged but ignored | ||
if err = controllerutil.SetOwnerReference(obj, secret, scheme); err != nil { | ||
log.Info("Ignoring error taking ownership of secret", "secret", path, "error", err) | ||
} else if _, err = secretClient.Update(ctx, secret, metav1.UpdateOptions{}); err != nil { | ||
log.Info("Ignoring error taking ownership of secret", "secret", path, "error", err) | ||
} | ||
} | ||
|
||
// builds a path for the secret like {namespace}/{name}/{key} to avoid hash key collisions | ||
for filename, data := range secret.Data { | ||
name := strings.Join([]string{path, filename}, "/") | ||
secretsData[name] = data | ||
log.Info("Read secret bytes", "path", name, "bytes", len(data)) | ||
} | ||
} | ||
|
||
return secretsData, nil | ||
} |
Oops, something went wrong.