Skip to content
Open
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
9 changes: 8 additions & 1 deletion cmd/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ EXAMPLES
`,
SuggestFor: []string{"biuld", "buidl", "built"},
PreRunE: bindEnv("image", "path", "builder", "registry", "confirm",
"push", "builder-image", "base-image", "platform", "verbose",
"push", "builder-image", "base-image", "ca-bundle", "platform", "verbose",
"build-timestamp", "registry-insecure", "registry-authfile", "username", "password", "token"),
RunE: func(cmd *cobra.Command, args []string) error {
return runBuild(cmd, args, newClient)
Expand Down Expand Up @@ -114,6 +114,8 @@ EXAMPLES
"Specify a custom builder image for use by the builder other than its default. ($FUNC_BUILDER_IMAGE)")
cmd.Flags().StringP("base-image", "", f.Build.BaseImage,
"Override the base image for your function (host builder only)")
cmd.Flags().StringP("ca-bundle", "", f.Build.CACertBundle,
"Path to CA certificate bundle file for SSL verification during build. Useful when building behind corporate proxies. ($FUNC_CA_BUNDLE)")
cmd.Flags().StringP("image", "i", f.Image,
"Full image name in the form [registry]/[namespace]/[name]:[tag] (optional). This option takes precedence over --registry ($FUNC_IMAGE)")

Expand Down Expand Up @@ -230,6 +232,9 @@ type buildConfig struct {
// TODO: gauron99 -- make option to add a path to dockerfile ?
BaseImage string

// CACertBundle is the path to a CA certificate bundle file for SSL verification
CACertBundle string

// Path of the function implementation on local disk. Defaults to current
// working directory of the process.
Path string
Expand Down Expand Up @@ -271,6 +276,7 @@ func newBuildConfig() buildConfig {
},
BuilderImage: viper.GetString("builder-image"),
BaseImage: viper.GetString("base-image"),
CACertBundle: viper.GetString("ca-bundle"),
Image: viper.GetString("image"),
Path: viper.GetString("path"),
Platform: viper.GetString("platform"),
Expand All @@ -294,6 +300,7 @@ func (c buildConfig) Configure(f fn.Function) fn.Function {
}
f.Image = c.Image
f.Build.BaseImage = c.BaseImage
f.Build.CACertBundle = c.CACertBundle
// Path, Platform and Push are not part of a function's state.
return f
}
Expand Down
1 change: 1 addition & 0 deletions docs/reference/func_build.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func build
--build-timestamp Use the actual time as the created time for the docker image. This is only useful for buildpacks builder.
-b, --builder string Builder to use when creating the function's container. Currently supported builders are "host", "pack" and "s2i". ($FUNC_BUILDER) (default "pack")
--builder-image string Specify a custom builder image for use by the builder other than its default. ($FUNC_BUILDER_IMAGE)
--ca-bundle string Path to CA certificate bundle file for SSL verification during build. Useful when building behind corporate proxies. ($FUNC_CA_BUNDLE)
-c, --confirm Prompt to confirm options interactively ($FUNC_CONFIRM)
-h, --help help for build
-i, --image string Full image name in the form [registry]/[namespace]/[name]:[tag] (optional). This option takes precedence over --registry ($FUNC_IMAGE)
Expand Down
67 changes: 63 additions & 4 deletions pkg/buildpacks/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,11 +224,70 @@ func (b *Builder) Build(ctx context.Context, f fn.Function, platforms []fn.Platf
opts.Env["BP_IMAGE_LABELS"] = strings.Join(imageLabels, " ")
}

var bindings = make([]string, 0, len(f.Build.Mounts))
for _, m := range f.Build.Mounts {
bindings = append(bindings, fmt.Sprintf("%s:%s", m.Source, m.Destination))
// CA Certificate Bundle support for corporate proxies
// Use Paketo CA certificates buildpack binding instead of manual env vars
if f.Build.CACertBundle != "" {
// Resolve to absolute path if relative
caBundlePath := f.Build.CACertBundle
if !filepath.IsAbs(caBundlePath) {
caBundlePath = filepath.Join(f.Root, caBundlePath)
}

// Validate that the CA bundle file exists
if _, err := os.Stat(caBundlePath); err != nil {
return fmt.Errorf("CA bundle file not found: %w", err)
}

// Create a temporary directory for the CA certificates binding
caCertsBindingDir, err := os.MkdirTemp("", "func-ca-certs-*")
if err != nil {
return fmt.Errorf("failed to create CA certificates binding directory: %w", err)
}
// Note: We don't defer cleanup here because the build process needs it
// The directory will be cleaned up when the temp dir is cleaned by the OS

// Write the binding type file
if err := os.WriteFile(filepath.Join(caCertsBindingDir, "type"), []byte("ca-certificates"), 0644); err != nil {
return fmt.Errorf("failed to write CA certificates binding type: %w", err)
}

// Copy the CA bundle to the binding directory
caData, err := os.ReadFile(caBundlePath)
if err != nil {
return fmt.Errorf("failed to read CA bundle: %w", err)
}
if err := os.WriteFile(filepath.Join(caCertsBindingDir, "ca-certificates.crt"), caData, 0644); err != nil {
return fmt.Errorf("failed to write CA certificates to binding: %w", err)
}

// Set SERVICE_BINDING_ROOT if not already set
if _, ok := opts.Env["SERVICE_BINDING_ROOT"]; !ok {
opts.Env["SERVICE_BINDING_ROOT"] = "/platform/bindings"
}

// Create a local copy of mounts and add the CA certificates binding
// We don't modify f.Build.Mounts directly to avoid race conditions
mounts := make([]fn.MountSpec, len(f.Build.Mounts), len(f.Build.Mounts)+1)
copy(mounts, f.Build.Mounts)
mounts = append(mounts, fn.MountSpec{
Source: caCertsBindingDir,
Destination: filepath.Join(opts.Env["SERVICE_BINDING_ROOT"], "ca-certificates"),
})

// Convert mounts to bindings format
var bindings = make([]string, 0, len(mounts))
for _, m := range mounts {
bindings = append(bindings, fmt.Sprintf("%s:%s", m.Source, m.Destination))
}
opts.ContainerConfig.Volumes = bindings
} else {
// No CA bundle, just use the existing mounts
var bindings = make([]string, 0, len(f.Build.Mounts))
for _, m := range f.Build.Mounts {
bindings = append(bindings, fmt.Sprintf("%s:%s", m.Source, m.Destination))
}
opts.ContainerConfig.Volumes = bindings
}
opts.ContainerConfig.Volumes = bindings

// only trust our known builders
opts.TrustBuilder = TrustBuilder
Expand Down
5 changes: 5 additions & 0 deletions pkg/functions/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,11 @@ type BuildSpec struct {
// Build Env variables to be set
BuildEnvs Envs `yaml:"buildEnvs,omitempty"`

// CACertBundle specifies the path to a CA certificate bundle file to use
// for SSL verification during build. This is useful when building behind
// corporate proxies with SSL inspection.
CACertBundle string `yaml:"caCertBundle,omitempty" jsonschema:"description=Path to CA certificate bundle for SSL verification during build"`

// PVCSize specifies the size of persistent volume claim used to store function
// when using deployment and remote build process (only relevant when Remote is true).
PVCSize string `yaml:"pvcSize,omitempty"`
Expand Down
32 changes: 32 additions & 0 deletions pkg/s2i/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,38 @@ func (b *Builder) Build(ctx context.Context, f fn.Function, platforms []fn.Platf
cfg.BuildVolumes = append(cfg.BuildVolumes, fmt.Sprintf("%s:%s:ro,Z", m.Source, m.Destination))
}

// CA Certificate Bundle support for corporate proxies
if f.Build.CACertBundle != "" {
// Resolve to absolute path if relative
caBundlePath := f.Build.CACertBundle
if !filepath.IsAbs(caBundlePath) {
caBundlePath = filepath.Join(f.Root, caBundlePath)
}

// Validate that the CA bundle file exists
if _, err := os.Stat(caBundlePath); err != nil {
return fmt.Errorf("CA bundle file not found: %w", err)
}

// For S2I, we need to mount the CA bundle and set environment variables
// Mount the CA bundle into a standard location
caDestPath := "/tmp/ca-certificates.crt"
cfg.BuildVolumes = append(cfg.BuildVolumes,
fmt.Sprintf("%s:%s:ro,Z", caBundlePath, caDestPath))

// Set environment variables for various runtimes to use the CA bundle
cfg.Environment = append(cfg.Environment,
api.EnvironmentSpec{Name: "SSL_CERT_FILE", Value: caDestPath},
api.EnvironmentSpec{Name: "REQUESTS_CA_BUNDLE", Value: caDestPath}, // Python
api.EnvironmentSpec{Name: "NODE_EXTRA_CA_CERTS", Value: caDestPath}, // Node.js
api.EnvironmentSpec{Name: "CURL_CA_BUNDLE", Value: caDestPath}, // curl
api.EnvironmentSpec{Name: "GIT_SSL_CAINFO", Value: caDestPath}, // git
api.EnvironmentSpec{Name: "PIP_CERT", Value: caDestPath}, // pip (Python)
api.EnvironmentSpec{Name: "NPM_CONFIG_CAFILE", Value: caDestPath}, // npm (Node.js)
api.EnvironmentSpec{Name: "CARGO_HTTP_CAINFO", Value: caDestPath}, // cargo (Rust)
)
}

if runtime.GOOS == "linux" {
cfg.DockerNetworkMode = "host"
}
Expand Down
4 changes: 4 additions & 0 deletions schema/func_yaml-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@
"type": "array",
"description": "Build Env variables to be set"
},
"caCertBundle": {
"type": "string",
"description": "Path to CA certificate bundle for SSL verification during build"
},
"pvcSize": {
"type": "string",
"description": "PVCSize specifies the size of persistent volume claim used to store function\nwhen using deployment and remote build process (only relevant when Remote is true)."
Expand Down
Loading