Skip to content

allow secrets with "." characters to be used in builds #15946

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 32 additions & 24 deletions pkg/build/admission/buildpodutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,16 @@ import (
// GetBuildFromPod returns a build object encoded in a pod's BUILD environment variable along with
// its encoding version
func GetBuildFromPod(pod *v1.Pod) (*buildapi.Build, schema.GroupVersion, error) {
envVar, err := buildEnvVar(pod)
if err != nil {
return nil, schema.GroupVersion{}, fmt.Errorf("unable to get build from pod: %v", err)
if len(pod.Spec.Containers) == 0 {
return nil, schema.GroupVersion{}, errors.New("unable to get build from pod: pod has no containers")
}

buildEnvVar := getEnvVar(&pod.Spec.Containers[0], "BUILD")
if len(buildEnvVar) == 0 {
return nil, schema.GroupVersion{}, errors.New("unable to get build from pod: BUILD environment variable is empty")
}
obj, groupVersionKind, err := kapi.Codecs.UniversalDecoder().Decode([]byte(envVar.Value), nil, nil)

obj, groupVersionKind, err := kapi.Codecs.UniversalDecoder().Decode([]byte(buildEnvVar), nil, nil)
if err != nil {
return nil, schema.GroupVersion{}, fmt.Errorf("unable to get build from pod: %v", err)
}
Expand All @@ -32,15 +37,18 @@ func GetBuildFromPod(pod *v1.Pod) (*buildapi.Build, schema.GroupVersion, error)

// SetBuildInPod encodes a build object and sets it in a pod's BUILD environment variable
func SetBuildInPod(pod *v1.Pod, build *buildapi.Build, groupVersion schema.GroupVersion) error {
envVar, err := buildEnvVar(pod)
if err != nil {
return fmt.Errorf("unable to set build in pod: %v", err)
}
encodedBuild, err := runtime.Encode(kapi.Codecs.LegacyCodec(groupVersion), build)
if err != nil {
return fmt.Errorf("unable to set build in pod: %v", err)
}
envVar.Value = string(encodedBuild)

for i := range pod.Spec.Containers {
setEnvVar(&pod.Spec.Containers[i], "BUILD", string(encodedBuild))
}
for i := range pod.Spec.InitContainers {
setEnvVar(&pod.Spec.InitContainers[i], "BUILD", string(encodedBuild))
}

return nil
}

Expand Down Expand Up @@ -79,25 +87,25 @@ func SetPodLogLevelFromBuild(pod *v1.Pod, build *buildapi.Build) error {
return nil
}

func buildEnvVar(pod *v1.Pod) (*v1.EnvVar, error) {
if len(pod.Spec.Containers) == 0 {
return nil, errors.New("pod has no containers")
}
env := pod.Spec.Containers[0].Env
for i := range env {
if env[i].Name == "BUILD" {
if len(env[i].Value) == 0 {
return nil, errors.New("BUILD environment variable is empty")
}
return &env[i], nil
func getEnvVar(c *v1.Container, name string) string {
for _, envVar := range c.Env {
if envVar.Name == name {
return envVar.Value
}
}
return nil, errors.New("pod does not have a BUILD environment variable")

return ""
}

func hasBuildEnvVar(pod *v1.Pod) bool {
_, err := buildEnvVar(pod)
return err == nil
func setEnvVar(c *v1.Container, name, value string) {
for i, envVar := range c.Env {
if envVar.Name == name {
c.Env[i] = v1.EnvVar{Name: name, Value: value}
return
}
}

c.Env = append(c.Env, v1.EnvVar{Name: name, Value: value})
}

func hasBuildAnnotation(pod *v1.Pod) bool {
Expand Down
2 changes: 2 additions & 0 deletions pkg/build/controller/strategy/custom_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ func TestCustomCreateBuildPod(t *testing.T) {
t.Errorf("Expected %s variable to be set", name)
}
}

checkAliasing(t, actual)
}

func TestCustomCreateBuildPodExpectedForcePull(t *testing.T) {
Expand Down
8 changes: 4 additions & 4 deletions pkg/build/controller/strategy/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func (bs *DockerBuildStrategy) CreateBuildPod(build *buildapi.Build) (*v1.Pod, e
Name: "docker-build",
Image: bs.Image,
Command: []string{"openshift-docker-build"},
Env: containerEnv,
Env: copyEnvVarSlice(containerEnv),
// TODO: run unprivileged https://github.com/openshift/origin/issues/662
SecurityContext: &v1.SecurityContext{
Privileged: &privileged,
Expand Down Expand Up @@ -92,7 +92,7 @@ func (bs *DockerBuildStrategy) CreateBuildPod(build *buildapi.Build) (*v1.Pod, e
Name: GitCloneContainer,
Image: bs.Image,
Command: []string{"openshift-git-clone"},
Env: containerEnv,
Env: copyEnvVarSlice(containerEnv),
TerminationMessagePolicy: v1.TerminationMessageFallbackToLogsOnError,
VolumeMounts: []v1.VolumeMount{
{
Expand All @@ -115,7 +115,7 @@ func (bs *DockerBuildStrategy) CreateBuildPod(build *buildapi.Build) (*v1.Pod, e
Name: ExtractImageContentContainer,
Image: bs.Image,
Command: []string{"openshift-extract-image-content"},
Env: containerEnv,
Env: copyEnvVarSlice(containerEnv),
// TODO: run unprivileged https://github.com/openshift/origin/issues/662
SecurityContext: &v1.SecurityContext{
Privileged: &privileged,
Expand All @@ -137,7 +137,7 @@ func (bs *DockerBuildStrategy) CreateBuildPod(build *buildapi.Build) (*v1.Pod, e
Name: "manage-dockerfile",
Image: bs.Image,
Command: []string{"openshift-manage-dockerfile"},
Env: containerEnv,
Env: copyEnvVarSlice(containerEnv),
TerminationMessagePolicy: v1.TerminationMessageFallbackToLogsOnError,
VolumeMounts: []v1.VolumeMount{
{
Expand Down
2 changes: 2 additions & 0 deletions pkg/build/controller/strategy/docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ func TestDockerCreateBuildPod(t *testing.T) {
t.Errorf("Expected %s:%s, got %s:%s!\n", exp[0], exp[1], e.Name, e.Value)
}
}

checkAliasing(t, actual)
}

func TestDockerBuildLongName(t *testing.T) {
Expand Down
8 changes: 4 additions & 4 deletions pkg/build/controller/strategy/sti.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func (bs *SourceBuildStrategy) CreateBuildPod(build *buildapi.Build) (*v1.Pod, e
Name: "sti-build",
Image: bs.Image,
Command: []string{"openshift-sti-build"},
Env: containerEnv,
Env: copyEnvVarSlice(containerEnv),
// TODO: run unprivileged https://github.com/openshift/origin/issues/662
SecurityContext: &v1.SecurityContext{
Privileged: &privileged,
Expand Down Expand Up @@ -114,7 +114,7 @@ func (bs *SourceBuildStrategy) CreateBuildPod(build *buildapi.Build) (*v1.Pod, e
Name: GitCloneContainer,
Image: bs.Image,
Command: []string{"openshift-git-clone"},
Env: containerEnv,
Env: copyEnvVarSlice(containerEnv),
TerminationMessagePolicy: v1.TerminationMessageFallbackToLogsOnError,
VolumeMounts: []v1.VolumeMount{
{
Expand All @@ -137,7 +137,7 @@ func (bs *SourceBuildStrategy) CreateBuildPod(build *buildapi.Build) (*v1.Pod, e
Name: ExtractImageContentContainer,
Image: bs.Image,
Command: []string{"openshift-extract-image-content"},
Env: containerEnv,
Env: copyEnvVarSlice(containerEnv),
// TODO: run unprivileged https://github.com/openshift/origin/issues/662
SecurityContext: &v1.SecurityContext{
Privileged: &privileged,
Expand All @@ -159,7 +159,7 @@ func (bs *SourceBuildStrategy) CreateBuildPod(build *buildapi.Build) (*v1.Pod, e
Name: "manage-dockerfile",
Image: bs.Image,
Command: []string{"openshift-manage-dockerfile"},
Env: containerEnv,
Env: copyEnvVarSlice(containerEnv),
TerminationMessagePolicy: v1.TerminationMessageFallbackToLogsOnError,
VolumeMounts: []v1.VolumeMount{
{
Expand Down
2 changes: 2 additions & 0 deletions pkg/build/controller/strategy/sti_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ func testSTICreateBuildPod(t *testing.T, rootAllowed bool) {
t.Errorf("Expected %s:%s, got %s:%s!\n", exp[0], exp[1], e.Name, e.Value)
}
}

checkAliasing(t, actual)
}

func TestS2IBuildLongName(t *testing.T) {
Expand Down
14 changes: 13 additions & 1 deletion pkg/build/controller/strategy/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"path/filepath"
"strconv"
"strings"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kvalidation "k8s.io/apimachinery/pkg/util/validation"
Expand Down Expand Up @@ -93,7 +94,11 @@ func setupDockerSocket(pod *v1.Pod) {
// mountSecretVolume is a helper method responsible for actual mounting secret
// volumes into a pod.
func mountSecretVolume(pod *v1.Pod, container *v1.Container, secretName, mountPath, volumeSuffix string) {
volumeName := namer.GetName(secretName, volumeSuffix, kvalidation.DNS1123SubdomainMaxLength)
volumeName := namer.GetName(secretName, volumeSuffix, kvalidation.DNS1123LabelMaxLength)

// coerce from RFC1123 subdomain to RFC1123 label.
volumeName = strings.Replace(volumeName, ".", "-", -1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it matter that you can no longer distinguish between a-b and a.b?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not particularly. if you really have two secrets like that, we have no way to support it anyway and the pod creation will fail when we try to define two volumes w/ the same name (I would assume). You're stuck regardless.

if we were really worried about it we could try multiple de-duplication replacements, but someone having "my.secret" and "my-secret" and wanting them both in a single build isn't terribly high on my list of worries, especially since this is the first we've even heard of someone trying to use one with a dot in the name.


volumeExists := false
for _, v := range pod.Spec.Volumes {
if v.Name == volumeName {
Expand Down Expand Up @@ -265,3 +270,10 @@ func setOwnerReference(pod *v1.Pod, build *buildapi.Build) {
},
}
}

// copyEnvVarSlice returns a copy of an []v1.EnvVar
func copyEnvVarSlice(in []v1.EnvVar) []v1.EnvVar {
out := make([]v1.EnvVar, len(in))
copy(out, in)
return out
}
51 changes: 50 additions & 1 deletion pkg/build/controller/strategy/util_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package strategy

import (
"reflect"
"testing"
"unsafe"

kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/v1"
Expand Down Expand Up @@ -79,7 +81,7 @@ func TestSetupDockerSecrets(t *testing.T) {
}

pushSecret := &kapi.LocalObjectReference{
Name: "pushSecret",
Name: "my.pushSecret.with.full.stops.and.longer.than.sixty.three.characters",
}
pullSecret := &kapi.LocalObjectReference{
Name: "pullSecret",
Expand All @@ -104,6 +106,13 @@ func TestSetupDockerSecrets(t *testing.T) {
seenName[v.Name] = true
}

if !seenName["my-pushSecret-with-full-stops-and-longer-than-six-c6eb4d75-push"] {
t.Errorf("volume my-pushSecret-with-full-stops-and-longer-than-six-c6eb4d75-push was not seen")
}
if !seenName["pullSecret-pull"] {
t.Errorf("volume pullSecret-pull was not seen")
}

seenMount := map[string]bool{}
seenMountPath := map[string]bool{}
for _, m := range pod.Spec.Containers[0].VolumeMounts {
Expand All @@ -117,4 +126,44 @@ func TestSetupDockerSecrets(t *testing.T) {
}
seenMountPath[m.Name] = true
}

if !seenMount["my-pushSecret-with-full-stops-and-longer-than-six-c6eb4d75-push"] {
t.Errorf("volumemount my-pushSecret-with-full-stops-and-longer-than-six-c6eb4d75-push was not seen")
}
if !seenMount["pullSecret-pull"] {
t.Errorf("volumemount pullSecret-pull was not seen")
}
}

func TestCopyEnvVarSlice(t *testing.T) {
s1 := []v1.EnvVar{{Name: "FOO", Value: "bar"}, {Name: "BAZ", Value: "qux"}}
s2 := copyEnvVarSlice(s1)

if !reflect.DeepEqual(s1, s2) {
t.Error(s2)
}

if (*reflect.SliceHeader)(unsafe.Pointer(&s1)).Data == (*reflect.SliceHeader)(unsafe.Pointer(&s2)).Data {
t.Error("copyEnvVarSlice didn't copy backing store")
}
}

func checkAliasing(t *testing.T, pod *v1.Pod) {
m := map[uintptr]bool{}
for _, c := range pod.Spec.Containers {
p := (*reflect.SliceHeader)(unsafe.Pointer(&c.Env)).Data
if m[p] {
t.Error("pod Env slices are aliased")
return
}
m[p] = true
}
for _, c := range pod.Spec.InitContainers {
p := (*reflect.SliceHeader)(unsafe.Pointer(&c.Env)).Data
if m[p] {
t.Error("pod Env slices are aliased")
return
}
m[p] = true
}
}
12 changes: 12 additions & 0 deletions test/extended/testdata/bindata.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions test/extended/testdata/test-auth-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ parameters:
- name: SOURCE_SECRET
required: true
objects:
- apiVersion: v1
kind: ImageStream
metadata:
name: output
- apiVersion: v1
kind: BuildConfig
metadata:
Expand All @@ -28,3 +32,11 @@ objects:
name: ruby:latest
namespace: openshift
type: Source
# this test specifically does a push, to help exercise the code that sets
# environment variables on build pods (i.e., by having a source secret and
# a push secret, multiple environment variables need to be set correctly for
# the build to succeed).
output:
to:
kind: ImageStreamTag
name: output:latest