diff --git a/cmd/docker-buildx/config.go b/cmd/docker-buildx/config.go index a99cccf..4038fe8 100644 --- a/cmd/docker-buildx/config.go +++ b/cmd/docker-buildx/config.go @@ -289,5 +289,47 @@ func settingsFlags(settings *plugin.Settings) []cli.Flag { Usage: "sets build output type and destination configuration", Destination: &settings.Build.Output, }, + &cli.StringFlag{ + Name: "ecr.aws_access_key_id", + EnvVars: []string{"PLUGIN_AWS_ACCESS_KEY_ID"}, + Usage: "Access Key ID for AWS", + Destination: &settings.AwsAccessKeyId, + }, + &cli.StringFlag{ + Name: "ecr.aws_secret_access_key_id", + EnvVars: []string{"PLUGIN_AWS_SECRET_ACCESS_KEY"}, + Usage: "Secret Access Key for AWS", + Destination: &settings.AwsSecretAccessKey, + }, + &cli.StringFlag{ + Name: "ecr.aws_region", + EnvVars: []string{"PLUGIN_AWS_REGION"}, + Usage: "AWS region to use", + Destination: &settings.AwsRegion, + }, + &cli.BoolFlag{ + Name: "ecr.create_repository", + EnvVars: []string{"PLUGIN_CREATE_REPOSITORY"}, + Usage: "creates the ECR repository if it does not exist", + Destination: &settings.CreateRepository, + }, + &cli.StringFlag{ + Name: "ecr.lifecycle_policy", + EnvVars: []string{"PLUGIN_LIFECYCLE_POLICY"}, + Usage: "AWS ECR lifecycle policy", + Destination: &settings.LifecyclePolicy, + }, + &cli.StringFlag{ + Name: "ecr.repository_policy", + EnvVars: []string{"PLUGIN_REPOSITORY_POLICY"}, + Usage: "AWS ECR repository policy", + Destination: &settings.RepositoryPolicy, + }, + &cli.BoolFlag{ + Name: "ecr.scan_on_push", + EnvVars: []string{"PLUGIN_SCAN_ON_PUSH"}, + Usage: "AWS: whether to enable image scanning on push", + Destination: &settings.ScanOnPush, + }, } } diff --git a/go.mod b/go.mod index 7216460..7f25329 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.20 require ( codeberg.org/6543/go-yaml2json v1.0.0 github.com/6543/go-version v1.3.1 + github.com/aws/aws-sdk-go v1.47.4 github.com/drone-plugins/drone-plugin-lib v0.4.2 github.com/joho/godotenv v1.5.1 github.com/sirupsen/logrus v1.9.3 @@ -18,12 +19,13 @@ require ( github.com/BurntSushi/toml v1.3.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/sys v0.11.0 // indirect + golang.org/x/sys v0.12.0 // indirect golang.org/x/tools v0.12.1-0.20230825192346-2191a27a6dc5 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index ff84ee8..47cfb98 100644 --- a/go.sum +++ b/go.sum @@ -1,59 +1,37 @@ -codeberg.org/6543/go-yaml2json v0.3.0 h1:BlvjmY0Gous8P+rr8aBdgPYnIfUAqFepF8q7Tp0R5t8= -codeberg.org/6543/go-yaml2json v0.3.0/go.mod h1:mz61q14LWF4ZABrgMEDMmk3t9dPi6zgR1uBh2VKV2RQ= codeberg.org/6543/go-yaml2json v1.0.0 h1:heGqo9VEi7gY2yNqjj7X4ADs5nzlFIbGsJtgYDLrnig= codeberg.org/6543/go-yaml2json v1.0.0/go.mod h1:mz61q14LWF4ZABrgMEDMmk3t9dPi6zgR1uBh2VKV2RQ= github.com/6543/go-version v1.3.1 h1:HvOp+Telns7HWJ2Xo/05YXQSB2bE0WmVgbHqwMPZT4U= github.com/6543/go-version v1.3.1/go.mod h1:oqFAHCwtLVUTLdhQmVZWYvaHXTdsbB4SY85at64SQEo= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/aws/aws-sdk-go v1.47.4 h1:IyhNbmPt+5ldi5HNzv7ZnXiqSglDMaJiZlzj4Yq3qnk= +github.com/aws/aws-sdk-go v1.47.4/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/drone-plugins/drone-plugin-lib v0.4.0 h1:qywEYGhquUuid6zNLmKia8CWY1TUa8jPQQ/G9ozfAmc= -github.com/drone-plugins/drone-plugin-lib v0.4.0/go.mod h1:EgqogX38GoJFtckeSQyhBJYX8P+KWBPhdprAVvyRxF8= -github.com/drone-plugins/drone-plugin-lib v0.4.1 h1:47rZlmcMpr1hSp+6Gl+1Z4t+efi/gMQU3lxukC1Yg64= -github.com/drone-plugins/drone-plugin-lib v0.4.1/go.mod h1:KwCu92jFjHV3xv2hu5Qg/8zBNvGwbhoJDQw/EwnTvoM= github.com/drone-plugins/drone-plugin-lib v0.4.2 h1:EiJ3Kco6ypP5noBQqVt1bBbuO1eUAumtPvLTX/NVAYg= github.com/drone-plugins/drone-plugin-lib v0.4.2/go.mod h1:KwCu92jFjHV3xv2hu5Qg/8zBNvGwbhoJDQw/EwnTvoM= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= -github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= -github.com/urfave/cli/v2 v2.16.3 h1:gHoFIwpPjoyIMbJp/VFd+/vuD0dAgFK4B6DpEMFJfQk= -github.com/urfave/cli/v2 v2.16.3/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI= -github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= -github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/urfave/cli/v2 v2.26.0 h1:3f3AMg3HpThFNT4I++TKOejZO8yU55t3JnnSr4S4QEI= github.com/urfave/cli/v2 v2.26.0/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/urfave/cli/v2 v2.27.0 h1:uNs1K8JwTFL84X68j5Fjny6hfANh9nTlJ6dRtZAFAHY= @@ -62,44 +40,33 @@ github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRT github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e h1:qyrTQ++p1afMkO4DPEeLGq/3oTsdlvdH4vqZUBWzUKM= -golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a h1:Jw5wfR+h9mnIYH+OtGT2im5wV1YGGDora5vTv/aa5bE= golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f h1:OKYpQQVE3DKSc3r3zHVzq46vq5YH7x8xpR3/k9ixmUg= -golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= golang.org/x/tools v0.12.1-0.20230825192346-2191a27a6dc5 h1:Vk4mysSz+GqQK2eqgWbo4zEO89wkeAjJiFIr9bpqa8k= golang.org/x/tools v0.12.1-0.20230825192346-2191a27a6dc5/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -honnef.co/go/tools v0.3.3 h1:oDx7VAwstgpYpb3wv0oxiZlxY+foCpRAwY7Vk6XpAgA= -honnef.co/go/tools v0.3.3/go.mod h1:jzwdWgg7Jdq75wlfblQxO4neNaFFSvgc1tD5Wv8U0Yw= honnef.co/go/tools v0.4.6 h1:oFEHCKeID7to/3autwsWfnuv69j3NsfcXbvJKuIcep8= honnef.co/go/tools v0.4.6/go.mod h1:+rnGS1THNh8zMwnd2oVOTL9QF6vmfyG6ZXBULae2uc0= diff --git a/plugin/docker.go b/plugin/docker.go index 395a4c6..750810a 100644 --- a/plugin/docker.go +++ b/plugin/docker.go @@ -44,7 +44,7 @@ func commandBuilder(daemon Daemon) *exec.Cmd { for _, driveropt := range daemon.BuildkitDriverOpt.Value() { args = append(args, "--driver-opt", driveropt) } - + return exec.Command(dockerExe, args...) } diff --git a/plugin/ecr.go b/plugin/ecr.go new file mode 100644 index 0000000..ed91494 --- /dev/null +++ b/plugin/ecr.go @@ -0,0 +1,227 @@ +// Source: https://github.com/drone-plugins/drone-docker/tree/939591f01828eceae54f5768dc7ce08ad0ad0bba/cmd/drone-ecr +package plugin + +import ( + "encoding/base64" + "fmt" + "log" + "os" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws/credentials/stscreds" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/ecr" +) + +const DefaultRegion = "us-east-1" + +var repo string +var assumeRole string +var externalID string +var ecr_login Login +var aws_region string + +func (p *Plugin) EcrInit() { + + // create a standalone Login object to account for single repo and multi-repo case + if len(p.settings.Logins) >= 1 { + for _, login := range p.settings.Logins { + if strings.Contains(login.Registry, "amazonaws.com") { + ecr_login = login + aws_region = login.Aws_region + + // filter repo containing ecr registry + substrings := make([]string, 0) + for _, repo := range p.settings.Build.Repo.Value() { + substrings = append(substrings, strings.Split(repo, ",")...) + } + filtered := make([]string, 0) + for _, s := range substrings { + if strings.Contains(s, "amazonaws.com") { + filtered = append(filtered, s) + } + } + + // Join the filtered substrings into a comma-separated string + repo = strings.Join(filtered, ",") + + // set the region + if aws_region == "" { + aws_region = DefaultRegion + } + + os.Setenv("AWS_REGION", aws_region) + os.Setenv("AWS_ACCESS_KEY_ID", ecr_login.Aws_access_key_id) + os.Setenv("AWS_SECRET_ACCESS_KEY", ecr_login.Aws_secret_access_key) + + } + } + } else { + ecr_login.Aws_access_key_id = p.settings.AwsAccessKeyId + ecr_login.Aws_secret_access_key = p.settings.AwsSecretAccessKey + aws_region = p.settings.AwsRegion + repo = p.settings.Build.Repo.Value()[0] + + // set the region + if aws_region == "" { + aws_region = DefaultRegion + } + + os.Setenv("AWS_REGION", p.settings.AwsRegion) + os.Setenv("AWS_ACCESS_KEY_ID", p.settings.AwsAccessKeyId) + os.Setenv("AWS_SECRET_ACCESS_KEY", p.settings.AwsSecretAccessKey) + } + // here the env vars are used for authentication + sess, err := session.NewSession(&aws.Config{Region: &aws_region}) + if err != nil { + log.Fatalf("error creating aws session: %v", err) + } + + svc := getECRClient(sess, assumeRole, externalID) + username, password, registry, err := getAuthInfo(svc) + + if err != nil { + log.Fatalf("error getting ECR auth: %v", err) + } + + if !strings.HasPrefix(repo, registry) { + repo = fmt.Sprintf("%s/%s", registry, repo) + } + + if p.settings.CreateRepository { + err = ensureRepoExists(svc, trimHostname(repo, registry), p.settings.ScanOnPush) + if err != nil { + log.Fatalf("error creating ECR repo: %v", err) + } + err = updateImageScannningConfig(svc, trimHostname(repo, registry), p.settings.ScanOnPush) + if err != nil { + log.Fatalf("error updating scan on push for ECR repo: %v", err) + } + } + + if p.settings.LifecyclePolicy != "" { + p, err := os.ReadFile(p.settings.LifecyclePolicy) + if err != nil { + log.Fatal(err) + } + if err := uploadLifeCyclePolicy(svc, string(p), trimHostname(repo, registry)); err != nil { + log.Fatalf("error uploading ECR lifecycle policy: %v", err) + } + } + + if p.settings.RepositoryPolicy != "" { + p, err := os.ReadFile(p.settings.RepositoryPolicy) + if err != nil { + log.Fatal(err) + } + if err := uploadRepositoryPolicy(svc, string(p), trimHostname(repo, registry)); err != nil { + log.Fatalf("error uploading ECR repository policy. %v", err) + } + } + + // set Username and Password for all Login which contain an AWS key + if len(p.settings.Logins) >= 1 { + for i, login := range p.settings.Logins { + if login.Aws_secret_access_key != "" && login.Aws_access_key_id != "" { + p.settings.Logins[i].Username = username + p.settings.Logins[i].Password = password + p.settings.Logins[i].Registry = registry + } + } + } else { + p.settings.DefaultLogin.Username = username + p.settings.DefaultLogin.Password = password + p.settings.DefaultLogin.Registry = registry + } + +} + +func trimHostname(repo, registry string) string { + repo = strings.TrimPrefix(repo, registry) + repo = strings.TrimLeft(repo, "/") + return repo +} + +func ensureRepoExists(svc *ecr.ECR, name string, scanOnPush bool) (err error) { + input := &ecr.CreateRepositoryInput{} + input.SetRepositoryName(name) + input.SetImageScanningConfiguration(&ecr.ImageScanningConfiguration{ScanOnPush: &scanOnPush}) + _, err = svc.CreateRepository(input) + if err != nil { + if aerr, ok := err.(awserr.Error); ok && aerr.Code() == ecr.ErrCodeRepositoryAlreadyExistsException { + // eat it, we skip checking for existing to save two requests + err = nil + } + } + + return +} + +func updateImageScannningConfig(svc *ecr.ECR, name string, scanOnPush bool) (err error) { + input := &ecr.PutImageScanningConfigurationInput{} + input.SetRepositoryName(name) + input.SetImageScanningConfiguration(&ecr.ImageScanningConfiguration{ScanOnPush: &scanOnPush}) + _, err = svc.PutImageScanningConfiguration(input) + + return err +} + +func uploadLifeCyclePolicy(svc *ecr.ECR, lifecyclePolicy string, name string) (err error) { + input := &ecr.PutLifecyclePolicyInput{} + input.SetLifecyclePolicyText(lifecyclePolicy) + input.SetRepositoryName(name) + _, err = svc.PutLifecyclePolicy(input) + + return err +} + +func uploadRepositoryPolicy(svc *ecr.ECR, repositoryPolicy string, name string) (err error) { + input := &ecr.SetRepositoryPolicyInput{} + input.SetPolicyText(repositoryPolicy) + input.SetRepositoryName(name) + _, err = svc.SetRepositoryPolicy(input) + + return err +} + +func getAuthInfo(svc *ecr.ECR) (username, password, registry string, err error) { + var result *ecr.GetAuthorizationTokenOutput + var decoded []byte + + result, err = svc.GetAuthorizationToken(&ecr.GetAuthorizationTokenInput{}) + if err != nil { + return + } + + auth := result.AuthorizationData[0] + token := *auth.AuthorizationToken + decoded, err = base64.StdEncoding.DecodeString(token) + if err != nil { + return + } + + registry = strings.TrimPrefix(*auth.ProxyEndpoint, "https://") + creds := strings.Split(string(decoded), ":") + username = creds[0] + password = creds[1] + return +} + +func getECRClient(sess *session.Session, role string, externalId string) *ecr.ECR { + if role == "" { + return ecr.New(sess) + } + if externalId != "" { + return ecr.New(sess, &aws.Config{ + Credentials: stscreds.NewCredentials(sess, role, func(p *stscreds.AssumeRoleProvider) { + p.ExternalID = &externalId + }), + }) + } else { + return ecr.New(sess, &aws.Config{ + Credentials: stscreds.NewCredentials(sess, role), + }) + } +} diff --git a/plugin/impl.go b/plugin/impl.go index 720fbeb..38a48f6 100644 --- a/plugin/impl.go +++ b/plugin/impl.go @@ -37,11 +37,17 @@ type Daemon struct { // Login defines Docker login parameters. type Login struct { + // Generic Registry string // Docker registry address Username string // Docker registry username Password string // Docker registry password Email string // Docker registry email Config string // Docker Auth Config + + // ECR + Aws_access_key_id string `json:"aws_access_key_id"` // AWS access key id + Aws_secret_access_key string `json:"aws_secret_access_key"` // AWS secret access key + Aws_region string `json:"aws_region"` // AWS region } // Build defines Docker build parameters. @@ -75,6 +81,16 @@ type Build struct { // Settings for the Plugin. type Settings struct { + // ECR + AwsRegion string `json:"aws_region"` // AWS region + ScanOnPush bool // ECR scan on push + RepositoryPolicy string // ECR repository policy + LifecyclePolicy string // ECR lifecycle policy + CreateRepository bool // ECR create repository + AwsAccessKeyId string `json:"aws_access_key_id"` // AWS access key id + AwsSecretAccessKey string `json:"aws_secret_access_key"` // AWS secret access key + + // Generic Daemon Daemon Logins []Login LoginsRaw string @@ -99,6 +115,17 @@ func (p *Plugin) InitSettings() error { p.settings.Build.Branch = p.pipeline.Repo.Branch p.settings.Build.Ref = p.pipeline.Commit.Ref + // check if any Login struct contains AWS credentials + for _, login := range p.settings.Logins { + if strings.Contains(login.Registry, "amazonaws.com") { + p.EcrInit() + } + } + + if p.settings.AwsAccessKeyId != "" && p.settings.AwsSecretAccessKey != "" { + p.EcrInit() + } + if len(p.settings.Logins) == 0 { p.settings.Logins = []Login{p.settings.DefaultLogin} } else if !p.settings.DefaultLogin.anonymous() {