Feat: Add corporate certificates to Build APIs#2107
Conversation
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
fe07158 to
b4af1ae
Compare
|
/retest |
There was a problem hiding this comment.
Pull request overview
This PR adds support for corporate/custom Root CA certificates to the Build and BuildRun APIs. The feature allows users to specify CA bundles via Secret or ConfigMap references at both the Build and BuildRun levels, with the BuildRun specification taking precedence. The certificates are mounted into workload containers and relevant environment variables are configured.
Changes:
- Added CA bundle API types (
CABundle,SourceObjectKeySelector) for Build/BuildRun specifications - Implemented CA bundle validation logic in the validate package
- Implemented CA bundle volume and environment variable injection for TaskRun/PipelineRun execution
- Added CA bundle parameter definitions to task specifications
- Enhanced error handling in
taskrun_generator.goto properly check errors from called functions
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/apis/build/v1beta1/cabundle.go | New API types for CA bundle references |
| pkg/apis/build/v1beta1/build_types.go | Added CABundle field to BuildSpec and new BuildReason constant |
| pkg/apis/build/v1beta1/buildrun_types.go | Added CABundle field to BuildRunSpec |
| pkg/cabundle/cabundle.go | Core CA bundle validation and volume/environment variable generation |
| pkg/cabundle/cabundle_test.go | Comprehensive tests for CA bundle handling |
| pkg/cabundle/suite_test.go | Test suite setup |
| pkg/validate/cabundle.go | Validation logic for Build CA bundle references |
| pkg/validate/validate.go | Added Certificates constant and validation case |
| pkg/reconciler/buildrun/resources/cabundle.go | TaskRun/PipelineRun certificate injection |
| pkg/reconciler/buildrun/resources/taskrun.go | Added paramCABundle constant |
| pkg/reconciler/buildrun/resources/resource_builders.go | CA bundle parameter specifications and values |
| pkg/reconciler/buildrun/resources/taskrun_generator.go | Enhanced error handling and certificate injection |
| pkg/reconciler/buildrun/buildrun.go | Added CA bundle validation to reconciliation |
| deploy/crds/shipwright.io_builds.yaml | Updated Build CRD |
| deploy/crds/shipwright.io_buildruns.yaml | Updated BuildRun CRD |
| test/utils/v1beta1/pipelinerun.go | Added copyright header |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
b4af1ae to
5da79ab
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 16 out of 16 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
5da79ab to
8df886b
Compare
8df886b to
584410e
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated 12 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
584410e to
eea9bd4
Compare
57586d0 to
e912647
Compare
avinal
left a comment
There was a problem hiding this comment.
In my testing, builds using TaskRuns ran successfully, but builds using PipelineRuns failed due to nil pointer error.
Apart from that, it seems deepcopygen was not rerun.
| switch o := object.(type) { | ||
| case *corev1.Secret: | ||
| data = o.Data[ca.Secret.Key] | ||
| case *corev1.ConfigMap: | ||
| data = []byte(o.Data[ca.ConfigMap.Key]) | ||
| } | ||
|
|
||
| // Parse and validate each certificate in the PEM data | ||
| block, rest := pem.Decode(data) | ||
| if block == nil { | ||
| return fmt.Errorf("no certificate present in CA bundle") | ||
| } |
There was a problem hiding this comment.
I believe in case the key doesn't exist, we should return a proper error and not rely on Decode() to finally return that "Certificate is not present". This way we will know what exactly is wrong.
There was a problem hiding this comment.
In the API the key field is not optional.
There was a problem hiding this comment.
Are we validating that the provided key is present in the actual Secret/ConfigMap? Probably not up until this point.
This would be completely valid API:
caBundle:
configMap:
name: my-cas
key: this-is-not-a-real-file.txtAnd yet the ConfigMap (from say, cert-manager) would have the following:
kind: ConfigMap
metadata:
name: my-cas
data:
ca.crt: |
...There was a problem hiding this comment.
We are validating a valid CA to be present in the Secret or ConfigMap, so the key is implicit here.
But I'll test this out.
| return generateTaskSpecVolumes( | ||
| if err = generateTaskSpecVolumes( | ||
| g.taskRun.Spec.TaskSpec, | ||
| execCtx.volumeMounts, | ||
| execCtx.strategyVolumes, | ||
| execCtx.buildVolumes, | ||
| execCtx.buildRunVolumes, | ||
| ) | ||
| ); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| return nil |
There was a problem hiding this comment.
The original code can be left as is.
if err is nil, nil is returned and if err is not nil, err is returned, so the value of err is returned in all cases. Therefore, return generateTaskSpecVolumes( is already doing the right thing.
| return generateTaskSpecVolumes( | |
| if err = generateTaskSpecVolumes( | |
| g.taskRun.Spec.TaskSpec, | |
| execCtx.volumeMounts, | |
| execCtx.strategyVolumes, | |
| execCtx.buildVolumes, | |
| execCtx.buildRunVolumes, | |
| ) | |
| ); err != nil { | |
| return err | |
| } | |
| return nil | |
| return generateTaskSpecVolumes( |
There was a problem hiding this comment.
Yes.. This part of the code was already correct. Made this change to improve the code readability and match the other function.
There was a problem hiding this comment.
I think this is a matter of style preference. As a community I don't think we've established any hard guideline with respect to error returns (return nil vs. return value of an error-return function).
Don't we need to add the CA bundles to the set of task spec volumes?
There was a problem hiding this comment.
Yes.
Its added separately in pkg/reconciler/buildrun/resource/cabundle.go
| if err := applyParameters(g.taskRun, g.build, g.buildRun, g.strategy); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| return nil |
There was a problem hiding this comment.
Same as above, the original return statement should stay as is.
There was a problem hiding this comment.
Do we need to apply the CA bundle parameter here?
There was a problem hiding this comment.
Parameter for CA bundle is not needed for it functioning. But it was given with the intention of allowing user to dynamically use it in TaskRun steps. Not sure what use case that would be.
adambkaplan
left a comment
There was a problem hiding this comment.
I think this is a good start to implementing SHIP-0042.
The one thing that stands out which isn't implemented here Java keytool support (jks and pkcs12 formats). These are separate files that are provided in addition to (not separately from) the standard PEM-encoded cert bundles.
| // +kubebuilder:validation:ExactlyOneOf=configMap;secret | ||
| type CABundle struct { |
There was a problem hiding this comment.
I think we need a Type field in this object as well, to act as a union discriminator. This solidifies the user's intent, and follows the same API pattern we have for source code.
Kubernetes has go tags which can be used to generate the correct OpenAPI schema (unionDiscriminator and unionMember). I am pretty sure that controller-gen does the right thing with these. https://kubernetes.io/docs/reference/using-api/declarative-validation/#tag-unionDiscriminator
There was a problem hiding this comment.
@adambkaplan What do we want use the type field for?
Holding Secret or ConfigMap type?
| return generateTaskSpecVolumes( | ||
| if err = generateTaskSpecVolumes( | ||
| g.taskRun.Spec.TaskSpec, | ||
| execCtx.volumeMounts, | ||
| execCtx.strategyVolumes, | ||
| execCtx.buildVolumes, | ||
| execCtx.buildRunVolumes, | ||
| ) | ||
| ); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| return nil |
There was a problem hiding this comment.
I think this is a matter of style preference. As a community I don't think we've established any hard guideline with respect to error returns (return nil vs. return value of an error-return function).
Don't we need to add the CA bundles to the set of task spec volumes?
| if err := applyParameters(g.taskRun, g.build, g.buildRun, g.strategy); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| return nil |
There was a problem hiding this comment.
Do we need to apply the CA bundle parameter here?
90a9dfa to
1504859
Compare
1504859 to
a618966
Compare
Reference: shipwright-io/community#281 # Changes: - Added APIs to allow user to define custom Root CAs in Build and BuildRun APIs - Add unit tests Signed-off-by: Sayan Biswas <sabiswas@redhat.com>
a618966 to
c247c2b
Compare
@adambkaplan I have few queries related to few comments for the TLS development. Do we intend to use the Type field to differentiate between Secret and ConfigMap? For jks and pkcs12 , do we want the user to provide the references object key in the API like ? or have them generated dynamically from the PEM data of CA during runtime when enabled in the API like? |
Changes:
Submitter Checklist
See the contributor guide
for details on coding conventions, github and prow interactions, and the code review process.
Release Notes