Skip to content

Commit

Permalink
Builds - Mount trusted CA for cluster proxies
Browse files Browse the repository at this point in the history
  • Loading branch information
gabemontero committed Jul 31, 2019
1 parent b391b7d commit fb5b43e
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 3 deletions.
16 changes: 13 additions & 3 deletions pkg/build/buildutil/util.go
Expand Up @@ -36,9 +36,13 @@ const (
BuildBlobsContentCache = "/var/cache/blobs"

// buildPodSuffix is the suffix used to append to a build pod name given a build name
buildPodSuffix = "build"
caConfigMapSuffix = "ca"
sysConfigConfigMapSuffix = "sys-config"
buildPodSuffix = "build"
caConfigMapSuffix = "ca"
globalCAConfigMapSuffix = "global-ca"
// GlobalCAConfigMapAnnotation is the annotation key to set on a config map so that the platform's
// global CA support that will inject any required CA's needed for external communication.
GlobalCAConfigMapAnnotation = "config.openshift.io/inject-trusted-cabundle"
sysConfigConfigMapSuffix = "sys-config"
)

func HasTriggerType(triggerType buildv1.BuildTriggerType, bc *buildv1.BuildConfig) bool {
Expand Down Expand Up @@ -238,6 +242,12 @@ func GetBuildCAConfigMapName(build *buildv1.Build) string {
return naming.GetConfigMapName(build.Name, caConfigMapSuffix)
}

// GetBuildGlobalCAConfigMapName returns the name of the ConfigMap containing the build's
// certificate authority bundles obtained from the proxy cfg
func GetBuildGlobalCAConfigMapName(build *buildv1.Build) string {
return naming.GetConfigMapName(build.Name, globalCAConfigMapSuffix)
}

// GetBuildSystemConfigMapName returns the name of the ConfigMap containing the build's
// registry configuration.
func GetBuildSystemConfigMapName(build *buildv1.Build) string {
Expand Down
50 changes: 50 additions & 0 deletions pkg/build/controller/build/build_controller.go
Expand Up @@ -1210,6 +1210,19 @@ func (bc *BuildController) createBuildPod(build *buildv1.Build) (*buildUpdate, e
return update, err
}
}
//TODO commented out until CA injection controller lands
/*
hasGlobalCAMap, err := bc.findOwnedConfigMap(existingPod, build.Namespace, buildutil.GetBuildGlobalCAConfigMapName(build))
if err != nil {
return update, fmt.Errorf("could not find global certificate authority for build: %v", err)
}
if !hasGlobalCAMap {
update, err = bc.createBuildGlobalCAConfigMap(build, existingPod, update)
if err != nil {
return update, err
}
}
*/
hasRegistryConf, err := bc.findOwnedConfigMap(existingPod, build.Namespace, buildutil.GetBuildSystemConfigMapName(build))
if err != nil {
return update, fmt.Errorf("could not find registry config for build: %v", err)
Expand All @@ -1234,6 +1247,13 @@ func (bc *BuildController) createBuildPod(build *buildv1.Build) (*buildUpdate, e
if err != nil {
return nil, err
}
//TODO commented out until CA injection controller lands
/*
update, err = bc.createBuildGlobalCAConfigMap(build, pod, update)
if err != nil {
return update, err
}
*/
}

update = transitionToPhase(buildv1.BuildPhasePending, "", "")
Expand Down Expand Up @@ -1700,6 +1720,21 @@ func (bc *BuildController) handleBuildConfigError(err error, key interface{}) {
bc.buildConfigQueue.Forget(key)
}

// createBuildGlobalCAConfigMap creates a ConfigMap container certificate authorities used by the build pod
// that are injected via the platform's proxy support based on setting a particular annotation on the config map
func (bc *BuildController) createBuildGlobalCAConfigMap(build *buildv1.Build, buildPod *corev1.Pod, update *buildUpdate) (*buildUpdate, error) {
configMapSpec := bc.createBuildGlobalCAConfigMapSpec(build, buildPod)
cm, err := bc.configMapClient.ConfigMaps(buildPod.Namespace).Create(configMapSpec)
if err != nil {
bc.recorder.Eventf(build, corev1.EventTypeWarning, "FailedCreate", "Error creating build proxy certificate authority configMap: %v", err)
update.setReason("CannotCreateGlobalCAConfigMap")
update.setMessage("Failed creating build proxy certificate authority configMap.")
return update, fmt.Errorf("failed to create build proxy certificate authority configMap: %v", err)
}
klog.V(4).Infof("Created proxy certificate authority configMap %s/%s for build %s", build.Namespace, cm.Name, buildDesc(build))
return update, nil
}

// createBuildCAConfigMap creates a ConfigMap containing certificate authorities used by the build pod.
func (bc *BuildController) createBuildCAConfigMap(build *buildv1.Build, buildPod *corev1.Pod, update *buildUpdate, additionalCAs map[string]string) (*buildUpdate, error) {
configMapSpec := bc.createBuildCAConfigMapSpec(build, buildPod, additionalCAs)
Expand Down Expand Up @@ -1754,6 +1789,21 @@ func (bc *BuildController) createBuildCAConfigMapSpec(build *buildv1.Build, buil
return cm
}

// createBuildGlobalCAConfigMapSpec creates a ConfigMap template to hold certificate authorities provided by the platform's proxy support
// to be used by thebuild pod. The returned ConfigMap has an owner reference to the provided pod, ensuring proper garbage collection.
func (bc *BuildController) createBuildGlobalCAConfigMapSpec(build *buildv1.Build, buildPod *corev1.Pod) *corev1.ConfigMap {
cm := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: buildutil.GetBuildGlobalCAConfigMapName(build),
OwnerReferences: []metav1.OwnerReference{
makeBuildPodOwnerRef(buildPod),
},
Annotations: map[string]string{buildutil.GlobalCAConfigMapAnnotation: "true"},
},
}
return cm
}

// findOwnedConfigMap finds the ConfigMap with the given name and namespace, and owned by the provided pod.
func (bc *BuildController) findOwnedConfigMap(owner *corev1.Pod, namespace string, name string) (bool, error) {
cm, err := bc.configMapClient.ConfigMaps(namespace).Get(name, metav1.GetOptions{})
Expand Down
20 changes: 20 additions & 0 deletions pkg/build/controller/build/build_controller_test.go
Expand Up @@ -1241,6 +1241,26 @@ func TestCreateBuildCAConfigMap(t *testing.T) {
}
}

func TestCreateBuildProxyCAConfigMap(t *testing.T) {
bc := newFakeBuildController(nil, nil, nil, nil, nil)
defer bc.stop()
build := dockerStrategy(mockBuild(buildv1.BuildPhaseNew, buildv1.BuildOutput{}))
pod := mockBuildPod(build)
caMap := bc.createBuildGlobalCAConfigMapSpec(build, pod)
if caMap == nil {
t.Error("proxy certificate authority configMap was not created")
}
if !hasBuildPodOwnerRef(pod, caMap) {
t.Error("build proxy CA configMap is missing owner ref to the build pod")
}
if caMap.Annotations == nil || len(caMap.Annotations) == 0 {
t.Errorf("build proxy CA config map has empty annotations")
}
if _, ok := caMap.Annotations[buildutil.GlobalCAConfigMapAnnotation]; !ok {
t.Errorf("build proxy configMap is missing the proxy CA annotation")
}
}

func TestHandleControllerConfig(t *testing.T) {
tests := []struct {
name string
Expand Down
56 changes: 56 additions & 0 deletions pkg/build/controller/strategy/util.go
Expand Up @@ -32,6 +32,9 @@ const (
ConfigMapCertsMountPath = "/var/run/configs/openshift.io/certs"
SecretBuildSourceBaseMountPath = "/var/run/secrets/openshift.io/build"
SourceImagePullSecretMountPath = "/var/run/secrets/openshift.io/source-image"
// ConfigMapBuildProxyCAMountPath is the directory where the tls-ca-bundle.pem file will be mounted
// by the cluster CA operator
ConfigMapBuildProxyCAMountPath = "/etc/pki/ca-trust/extracted/pem"

// ExtractImageContentContainer is the name of the container that will
// pull down input images and extract their content for input to the build.
Expand Down Expand Up @@ -474,9 +477,16 @@ func setupContainersNodeStorage(pod *corev1.Pod, container *corev1.Container) {
// setupBuildCAs mounts certificate authorities for the build from a predetermined ConfigMap.
func setupBuildCAs(build *buildv1.Build, pod *corev1.Pod, additionalCAs map[string]string, internalRegistryHost string) {
casExist := false
globalCAsExist := false
for _, v := range pod.Spec.Volumes {
if v.Name == "build-ca-bundles" {
casExist = true
}
if v.Name == "build-proxy-ca-bundles" {
globalCAsExist = true
}

if casExist && globalCAsExist {
break
}
}
Expand Down Expand Up @@ -531,6 +541,52 @@ func setupBuildCAs(build *buildv1.Build, pod *corev1.Pod, additionalCAs map[stri
}
pod.Spec.Containers = containers
}

//TODO add back in when global CA injector controller is ready
/*if !globalCAsExist {
cmSource := &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: buildutil.GetBuildGlobalCAConfigMapName(build),
},
//the TBD global CA injector will update the ConfigMapVolumeSource keyToPath items
Items: []corev1.KeyToPath{
{
Key: "ca-bundle.crt",
Path: "tls-ca-bundle.pem",
},
},
}
pod.Spec.Volumes = append(pod.Spec.Volumes,
corev1.Volume{
Name: "build-proxy-ca-bundles",
VolumeSource: corev1.VolumeSource{
ConfigMap: cmSource,
},
},
)
containers := make([]corev1.Container, len(pod.Spec.Containers))
for i, c := range pod.Spec.Containers {
c.VolumeMounts = append(c.VolumeMounts,
corev1.VolumeMount{
Name: "build-proxy-ca-bundles",
MountPath: ConfigMapBuildProxyCAMountPath,
},
)
containers[i] = c
}
pod.Spec.Containers = containers
initContainers := make([]corev1.Container, len(pod.Spec.InitContainers))
for i, c := range pod.Spec.InitContainers {
c.VolumeMounts = append(c.VolumeMounts,
corev1.VolumeMount{
Name: "build-proxy-ca-bundles",
MountPath: ConfigMapBuildProxyCAMountPath,
},
)
initContainers[i] = c
}
}*/
}

// setupBlobCache configures a shared volume for caching image blobs across the build pod containers.
Expand Down

0 comments on commit fb5b43e

Please sign in to comment.