Compare commits

...

2 Commits

Author SHA1 Message Date
sahithiharness a93b4bd647 fix: [CI-14845]: support for build args which has comma seperated values 2024-11-26 21:32:39 +05:30
Abhay 113a61b0e1 feat: [Ci-14242]: add oidc support for ecr (#131)
* feat: [Ci-14242]: add oidc support for ecr

* fix: [CI-14242]: error handling for oidc kakniko-ecr
2024-10-17 01:51:09 +05:30
3 changed files with 143 additions and 28 deletions
+33
View File
@@ -0,0 +1,33 @@
package main
import (
"strings"
)
// CustomStringSliceFlag is like a regular StringSlice flag but with
// semicolon as a delimiter
type CustomStringSliceFlag struct {
Value []string
}
func (f *CustomStringSliceFlag) GetValue() []string {
if f.Value == nil {
return make([]string, 0)
}
return f.Value
}
func (f *CustomStringSliceFlag) String() string {
if f.Value == nil {
return ""
}
return strings.Join(f.Value, ";")
}
func (f *CustomStringSliceFlag) Set(v string) error {
for _, s := range strings.Split(v, ";") {
s = strings.TrimSpace(s)
f.Value = append(f.Value, s)
}
return nil
}
+76 -2
View File
@@ -7,6 +7,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"strings" "strings"
"time"
"github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/config"
@@ -17,6 +18,7 @@ import (
"github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/aws/session"
ecrv1 "github.com/aws/aws-sdk-go/service/ecr" ecrv1 "github.com/aws/aws-sdk-go/service/ecr"
ecrpublicv1 "github.com/aws/aws-sdk-go/service/ecrpublic" ecrpublicv1 "github.com/aws/aws-sdk-go/service/ecrpublic"
"github.com/aws/aws-sdk-go/service/sts"
"github.com/aws/smithy-go" "github.com/aws/smithy-go"
"github.com/hashicorp/go-version" "github.com/hashicorp/go-version"
"github.com/joho/godotenv" "github.com/joho/godotenv"
@@ -35,6 +37,7 @@ const (
secretKeyEnv string = "AWS_SECRET_ACCESS_KEY" secretKeyEnv string = "AWS_SECRET_ACCESS_KEY"
ecrPublicDomain string = "public.ecr.aws" ecrPublicDomain string = "public.ecr.aws"
kanikoVersionEnv string = "KANIKO_VERSION" kanikoVersionEnv string = "KANIKO_VERSION"
sessionKeyEnv string = "AWS_SESSION_TOKEN"
oneDotEightVersion string = "1.8.0" oneDotEightVersion string = "1.8.0"
defaultDigestFile string = "/kaniko/digest-file" defaultDigestFile string = "/kaniko/digest-file"
@@ -122,6 +125,17 @@ func main() {
Usage: "build args", Usage: "build args",
EnvVar: "PLUGIN_BUILD_ARGS", EnvVar: "PLUGIN_BUILD_ARGS",
}, },
cli.GenericFlag{
Name: "args-new",
Usage: "build args new",
EnvVar: "PLUGIN_BUILD_ARGS_NEW",
Value: new(CustomStringSliceFlag),
},
cli.BoolFlag{
Name: "plugin-multiple-build-agrs",
Usage: "plugin multiple build agrs",
EnvVar: "PLUGIN_MULTIPLE_BUILD_ARGS",
},
cli.StringFlag{ cli.StringFlag{
Name: "target", Name: "target",
Usage: "build target", Usage: "build target",
@@ -384,6 +398,11 @@ func main() {
Usage: "Number of retries for downloading base images.", Usage: "Number of retries for downloading base images.",
EnvVar: "PLUGIN_IMAGE_DOWNLOAD_RETRY", EnvVar: "PLUGIN_IMAGE_DOWNLOAD_RETRY",
}, },
cli.StringFlag{
Name: "oidc-token-id",
Usage: "OIDC token for assuming role via web identity",
EnvVar: "PLUGIN_OIDC_TOKEN_ID",
},
} }
if err := app.Run(os.Args); err != nil { if err := app.Run(os.Args); err != nil {
@@ -398,6 +417,7 @@ func run(c *cli.Context) error {
noPush := c.Bool("no-push") noPush := c.Bool("no-push")
assumeRole := c.String("assume-role") assumeRole := c.String("assume-role")
externalId := c.String("external-id") externalId := c.String("external-id")
oidcToken := c.String("oidc-token-id")
// setup docker config for azure registry and base image docker registry // setup docker config for azure registry and base image docker registry
err := setDockerAuth( err := setDockerAuth(
@@ -411,6 +431,7 @@ func run(c *cli.Context) error {
externalId, externalId,
region, region,
noPush, noPush,
oidcToken,
) )
if err != nil { if err != nil {
return errors.Wrap(err, "failed to create docker config") return errors.Wrap(err, "failed to create docker config")
@@ -454,6 +475,8 @@ func run(c *cli.Context) error {
AutoTagSuffix: c.String("auto-tag-suffix"), AutoTagSuffix: c.String("auto-tag-suffix"),
ExpandTag: c.Bool("expand-tag"), ExpandTag: c.Bool("expand-tag"),
Args: c.StringSlice("args"), Args: c.StringSlice("args"),
ArgsNew: c.Generic("args-new").(*CustomStringSliceFlag).GetValue(),
IsMultipleBuildArgs: c.Bool("plugin-multiple-build-agrs"),
Target: c.String("target"), Target: c.String("target"),
Repo: fmt.Sprintf("%s/%s", c.String("registry"), c.String("repo")), Repo: fmt.Sprintf("%s/%s", c.String("registry"), c.String("repo")),
Mirrors: c.StringSlice("registry-mirrors"), Mirrors: c.StringSlice("registry-mirrors"),
@@ -518,7 +541,7 @@ func run(c *cli.Context) error {
} }
func setDockerAuth(dockerRegistry, dockerUsername, dockerPassword, accessKey, secretKey, func setDockerAuth(dockerRegistry, dockerUsername, dockerPassword, accessKey, secretKey,
registry, assumeRole, externalId, region string, noPush bool) error { registry, assumeRole, externalId, region string, noPush bool, oidcToken string) error {
dockerConfig := docker.NewConfig() dockerConfig := docker.NewConfig()
credentials := []docker.RegistryCredentials{} credentials := []docker.RegistryCredentials{}
// set docker credentials for base image registry // set docker credentials for base image registry
@@ -531,7 +554,24 @@ func setDockerAuth(dockerRegistry, dockerUsername, dockerPassword, accessKey, se
credentials = append(credentials, pullFromRegistryCreds) credentials = append(credentials, pullFromRegistryCreds)
} }
if assumeRole != "" { if assumeRole != "" && oidcToken != "" {
oidcAccessKey, oidcSecretKey, oidcSessionKey, err := getOidcCreds(oidcToken, assumeRole)
if err != nil {
return err
}
_ = os.Setenv(accessKeyEnv, oidcAccessKey)
_ = os.Setenv(secretKeyEnv, oidcSecretKey)
_ = os.Setenv(sessionKeyEnv, oidcSessionKey)
// kaniko-executor >=1.8.0 does not require additional cred helper logic for ECR,
// as it discovers ECR repositories automatically and acts accordingly.
if isKanikoVersionBelowOneDotEight(os.Getenv(kanikoVersionEnv)) {
dockerConfig.SetCredHelper(ecrPublicDomain, "ecr-login")
dockerConfig.SetCredHelper(registry, "ecr-login")
}
} else if assumeRole != "" {
var err error var err error
username, password, registry, err := getAssumeRoleCreds(region, assumeRole, externalId, "") username, password, registry, err := getAssumeRoleCreds(region, assumeRole, externalId, "")
if err != nil { if err != nil {
@@ -771,3 +811,37 @@ func isKanikoVersionBelowOneDotEight(v string) bool {
return currVer.LessThan(oneEightVer) return currVer.LessThan(oneEightVer)
} }
func getOidcCreds(oidcToken, assumeRole string) (string, string, string, error) {
// Create a new session
sess, err := session.NewSession()
if err != nil {
return "", "", "", fmt.Errorf("failed to create AWS session: %w", err)
}
// Create a new STS client
svc := sts.New(sess)
// Prepare the input parameters for the STS call
duration := int64(time.Hour / time.Second)
input := &sts.AssumeRoleWithWebIdentityInput{
RoleArn: aws.String(assumeRole),
RoleSessionName: aws.String("kaniko-ecr-oidc"),
WebIdentityToken: aws.String(oidcToken),
DurationSeconds: aws.Int64(duration),
}
// Call the AssumeRoleWithWebIdentity function
result, err := svc.AssumeRoleWithWebIdentity(input)
if err != nil {
return "", "", "", fmt.Errorf("failed to assume role with web identity: %w", err)
}
// Check if credentials exist in the result
if result.Credentials == nil {
return "", "", "", errors.New("no credentials returned by AssumeRoleWithWebIdentity")
}
// Return the credentials
return *result.Credentials.AccessKeyId, *result.Credentials.SecretAccessKey, *result.Credentials.SessionToken, nil
}
+34 -26
View File
@@ -16,30 +16,32 @@ import (
type ( type (
// Build defines Docker build parameters. // Build defines Docker build parameters.
Build struct { Build struct {
DroneCommitRef string // Drone git commit reference DroneCommitRef string // Drone git commit reference
DroneRepoBranch string // Drone repo branch DroneRepoBranch string // Drone repo branch
Dockerfile string // Docker build Dockerfile Dockerfile string // Docker build Dockerfile
Context string // Docker build context Context string // Docker build context
Tags []string // Docker build tags Tags []string // Docker build tags
AutoTag bool // Set this to auto detect tags from git commits and semver-tagged labels AutoTag bool // Set this to auto detect tags from git commits and semver-tagged labels
AutoTagSuffix string // Suffix to append to the auto detect tags AutoTagSuffix string // Suffix to append to the auto detect tags
ExpandTag bool // Set this to expand the `Tags` into semver-tagged labels ExpandTag bool // Set this to expand the `Tags` into semver-tagged labels
Args []string // Docker build args Args []string // Docker build args
Target string // Docker build target ArgsNew []string // docker build args with comma seperated values
Repo string // Docker build repository IsMultipleBuildArgs bool // env variable for fallback for docker build args
Mirrors []string // Docker repository mirrors Target string // Docker build target
Labels []string // Label map Repo string // Docker build repository
SkipTlsVerify bool // Docker skip tls certificate verify for registry Mirrors []string // Docker repository mirrors
SnapshotMode string // Kaniko snapshot mode Labels []string // Label map
EnableCache bool // Whether to enable kaniko cache SkipTlsVerify bool // Docker skip tls certificate verify for registry
CacheRepo string // Remote repository that will be used to store cached layers SnapshotMode string // Kaniko snapshot mode
CacheTTL int // Cache timeout in hours EnableCache bool // Whether to enable kaniko cache
DigestFile string // Digest file location CacheRepo string // Remote repository that will be used to store cached layers
NoPush bool // Set this flag if you only want to build the image, without pushing to a registry CacheTTL int // Cache timeout in hours
Verbosity string // Log level DigestFile string // Digest file location
Platform string // Allows to build with another default platform than the host, similarly to docker build --platform NoPush bool // Set this flag if you only want to build the image, without pushing to a registry
SkipUnusedStages bool // Build only used stages Verbosity string // Log level
TarPath string // Set this flag to save the image as a tarball at path Platform string // Allows to build with another default platform than the host, similarly to docker build --platform
SkipUnusedStages bool // Build only used stages
TarPath string // Set this flag to save the image as a tarball at path
Cache bool // Enable or disable caching during the build process. Cache bool // Enable or disable caching during the build process.
CacheDir string // Directory to store cached layers. CacheDir string // Directory to store cached layers.
@@ -202,8 +204,14 @@ func (p Plugin) Exec() error {
} }
// Set the build arguments // Set the build arguments
for _, arg := range p.Build.Args { if p.Build.IsMultipleBuildArgs {
cmdArgs = append(cmdArgs, fmt.Sprintf("--build-arg=%s", arg)) for _, arg := range p.Build.ArgsNew {
cmdArgs = append(cmdArgs, "--build-arg", arg)
}
} else {
for _, arg := range p.Build.Args {
cmdArgs = append(cmdArgs, "--build-arg", arg)
}
} }
// Set the labels // Set the labels
for _, label := range p.Build.Labels { for _, label := range p.Build.Labels {