├── .gitattributes ├── .github ├── release-drafter.yml ├── build-and-publish.png ├── dependabot.yml └── workflows │ ├── cd.yaml │ └── jenkins-security-scan.yml ├── .mvn ├── maven.config └── extensions.xml ├── .git-blame-ignore-revs ├── Jenkinsfile ├── LICENSE ├── .gitignore ├── src ├── main │ ├── resources │ │ └── index.jelly │ └── java │ │ └── com │ │ └── cloudbees │ │ └── jenkins │ │ └── plugins │ │ └── amazonecr │ │ ├── AmazonECSRegistryTokenSource.java │ │ ├── AmazonECSRegistryCredentialsProvider.java │ │ └── AmazonECSRegistryCredential.java └── test │ └── java │ └── com │ └── cloudbees │ └── jenkins │ └── plugins │ └── amazonecr │ └── AmazonECSRegistryCredentialPipelineAccessTest.java ├── CHANGELOG.old.md ├── pom.xml └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | _extends: .github 2 | -------------------------------------------------------------------------------- /.mvn/maven.config: -------------------------------------------------------------------------------- 1 | -Pconsume-incrementals 2 | -Pmight-produce-incrementals 3 | -Dchangelist.format=%d.v%s 4 | -------------------------------------------------------------------------------- /.github/build-and-publish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/amazon-ecr-plugin/main/.github/build-and-publish.png -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Spotless reformat: https://github.com/jenkinsci/amazon-ecr-plugin/pull/108 2 | 3808041a014663a1d830dd982428c19025c10e0a 3 | # Reformat to upstream spotless config 4 | 95904a4e7ced02c2856a1d91b49a7cc0a540a780 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "maven" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "weekly" 11 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | /* 2 | See the documentation for more options: 3 | https://github.com/jenkins-infra/pipeline-library/ 4 | */ 5 | buildPlugin( 6 | useContainerAgent: true, // Set to `false` if you need to use Docker for containerized tests 7 | configurations: [ 8 | [platform: 'linux', jdk: 25], 9 | [platform: 'windows', jdk: 21], 10 | ]) 11 | -------------------------------------------------------------------------------- /.mvn/extensions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | io.jenkins.tools.incrementals 4 | git-changelist-maven-extension 5 | 1.12 6 | 7 | 8 | -------------------------------------------------------------------------------- /.github/workflows/cd.yaml: -------------------------------------------------------------------------------- 1 | # Note: additional setup is required, see https://www.jenkins.io/redirect/continuous-delivery-of-plugins 2 | 3 | name: cd 4 | on: 5 | workflow_dispatch: 6 | check_run: 7 | types: 8 | - completed 9 | 10 | jobs: 11 | maven-cd: 12 | uses: jenkins-infra/github-reusable-workflows/.github/workflows/maven-cd.yml@v1 13 | secrets: 14 | MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} 15 | MAVEN_TOKEN: ${{ secrets.MAVEN_TOKEN }} 16 | -------------------------------------------------------------------------------- /.github/workflows/jenkins-security-scan.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Jenkins Security Scan 3 | on: 4 | push: 5 | branches: 6 | - "main" 7 | pull_request: 8 | types: [opened, synchronize, reopened] 9 | workflow_dispatch: 10 | 11 | permissions: 12 | security-events: write 13 | contents: read 14 | actions: read 15 | 16 | jobs: 17 | security-scan: 18 | uses: jenkins-infra/jenkins-security-scan/.github/workflows/jenkins-security-scan.yaml@v2 19 | with: 20 | java-cache: 'maven' # Optionally enable use of a build dependency cache. Specify 'maven' or 'gradle' as appropriate. 21 | # java-version: 21 # Optionally specify what version of Java to set up for the build, or remove to use a recent default. 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015-2017 CloudBees, Inc. 2 | Copyright 2021-2023 TobiX 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Maven template 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | pom.xml.next 7 | release.properties 8 | dependency-reduced-pom.xml 9 | buildNumber.properties 10 | .mvn/timing.properties 11 | ### JetBrains template 12 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 13 | 14 | *.iml 15 | 16 | ## Directory-based project format: 17 | .idea/ 18 | # if you remove the above rule, at least ignore the following: 19 | 20 | # User-specific stuff: 21 | # .idea/workspace.xml 22 | # .idea/tasks.xml 23 | # .idea/dictionaries 24 | 25 | # Sensitive or high-churn files: 26 | # .idea/dataSources.ids 27 | # .idea/dataSources.xml 28 | # .idea/sqlDataSources.xml 29 | # .idea/dynamic.xml 30 | # .idea/uiDesigner.xml 31 | 32 | # Gradle: 33 | # .idea/gradle.xml 34 | # .idea/libraries 35 | 36 | # Mongo Explorer plugin: 37 | # .idea/mongoSettings.xml 38 | 39 | ## File-based project format: 40 | *.ipr 41 | *.iws 42 | 43 | ## Jenkins 44 | work/ 45 | 46 | # IntelliJ 47 | /out/ 48 | 49 | # mpeltonen/sbt-idea plugin 50 | .idea_modules/ 51 | 52 | # JIRA plugin 53 | atlassian-ide-plugin.xml 54 | 55 | # Crashlytics plugin (for Android Studio and IntelliJ) 56 | com_crashlytics_export_strings.xml 57 | crashlytics.properties 58 | crashlytics-build.properties 59 | 60 | # Created by .ignore support plugin (hsz.mobi) 61 | -------------------------------------------------------------------------------- /src/main/resources/index.jelly: -------------------------------------------------------------------------------- 1 | 2 | 26 | 27 | 30 |
31 | This plugin generates Docker authentication token from Amazon Credentials to access Amazon ECR. 32 |
33 | -------------------------------------------------------------------------------- /src/test/java/com/cloudbees/jenkins/plugins/amazonecr/AmazonECSRegistryCredentialPipelineAccessTest.java: -------------------------------------------------------------------------------- 1 | package com.cloudbees.jenkins.plugins.amazonecr; 2 | 3 | import com.cloudbees.jenkins.plugins.awscredentials.AWSCredentialsImpl; 4 | import com.cloudbees.plugins.credentials.CredentialsScope; 5 | import com.cloudbees.plugins.credentials.SystemCredentialsProvider; 6 | import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; 7 | import org.jenkinsci.plugins.workflow.job.WorkflowJob; 8 | import org.junit.jupiter.api.Test; 9 | import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; 10 | import org.jvnet.hudson.test.JenkinsRule; 11 | import org.jvnet.hudson.test.junit.jupiter.WithJenkins; 12 | 13 | @WithJenkins 14 | class AmazonECSRegistryCredentialPipelineAccessTest { 15 | @Test 16 | @EnabledIfEnvironmentVariable(named = "AWS_ACCESS_KEY_ID", matches = ".{10,}") 17 | void pipelineCanLoginWithCredential(JenkinsRule r) throws Exception { 18 | SystemCredentialsProvider.getInstance() 19 | .getCredentials() 20 | .add(new AWSCredentialsImpl( 21 | CredentialsScope.GLOBAL, 22 | "test", 23 | System.getenv("AWS_ACCESS_KEY_ID"), 24 | System.getenv("AWS_SECRET_ACCESS_KEY"), 25 | "test")); 26 | 27 | String script = 28 | "docker.withRegistry('https://" + System.getenv("AWS_REGISTRY_HOST") + "', 'ecr:us-east-1:test') {}"; 29 | 30 | WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "testJob"); 31 | p.setDefinition(new CpsFlowDefinition(script, true)); 32 | 33 | r.assertBuildStatusSuccess(p.scheduleBuild2(0)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /CHANGELOG.old.md: -------------------------------------------------------------------------------- 1 | # Old Changelog 2 | 3 | This is the old changelog imported from the Jenkins wiki, for newer changes see 4 | [GitHub Releases](https://github.com/jenkinsci/amazon-ecr-plugin/releases). 5 | 6 | ## 1.6 (2017-05-16) 7 | 8 | - [JENKINS-34437](https://issues.jenkins-ci.org/browse/JENKINS-34437) Enable 9 | amazon-ecr-plugin behind proxy 10 | - Performance improvements 11 | - Set minor version to 1.642.1 12 | - Upgrade Credentials Plugin 13 | - Upgrade AWS Java SDK Plugin 14 | - Upgrade AWS Credentials Plugin 15 | - Upgrade Docker Commons Plugin 16 | - improve log 17 | 18 | ## 1.5 - Burned 19 | 20 | ## 1.4 (2016-10-29) 21 | 22 | - [JENKINS-38465](https://issues.jenkins-ci.org/browse/JENKINS-38465) ECR 23 | Plugin now it is compatible with credential stored into folders 24 | - [JENKINS-36127](https://issues.jenkins-ci.org/browse/JENKINS-36127) Resolved 25 | a NPE when attempt to configure docker build and publish 26 | - [JENKINS-34958](https://issues.jenkins-ci.org/browse/JENKINS-34958) New 27 | credential format that contains the region. For example, by specifying the 28 | following credentials: ecr:us-west-2:credential-id, the provider will set the 29 | Region of the AWS Client to us-west-2, when requesting for Authorisation 30 | token. 31 | 32 | ## 1.3 (2016-06-06) 33 | 34 | - 1.2 Release failed to upload the artifact - so just release again to 35 | correctly upload the artifact. 36 | 37 | NOTE: This release doesn't contain any update. 38 | 39 | ## 1.2 (2016-06-03) 40 | 41 | - Update parent pom 42 | 43 | ## 1.1 (2016-05-30) 44 | 45 | - [JENKINS-35220](http://localhost:8085/display/JENKINS/Amazon+ECR#) 46 | Correctly display the credentials 47 | 48 | ## 1.0 (2016-01-12) 49 | 50 | - Replace custom ECR API client with aws-java-sdk 51 | 52 | ## 1.0-beta-1 (2015-12-22) 53 | 54 | - Initial release 55 | -------------------------------------------------------------------------------- /src/main/java/com/cloudbees/jenkins/plugins/amazonecr/AmazonECSRegistryTokenSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2015, CloudBees, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | package com.cloudbees.jenkins.plugins.amazonecr; 27 | 28 | import edu.umd.cs.findbugs.annotations.NonNull; 29 | import hudson.Extension; 30 | import hudson.util.Secret; 31 | import java.util.logging.Level; 32 | import java.util.logging.Logger; 33 | import jenkins.authentication.tokens.api.AuthenticationTokenException; 34 | import jenkins.authentication.tokens.api.AuthenticationTokenSource; 35 | import org.jenkinsci.plugins.docker.commons.credentials.DockerRegistryToken; 36 | 37 | @Extension 38 | public class AmazonECSRegistryTokenSource 39 | extends AuthenticationTokenSource { 40 | 41 | private static final Logger LOG = Logger.getLogger(AmazonECSRegistryTokenSource.class.getName()); 42 | 43 | public AmazonECSRegistryTokenSource() { 44 | super(DockerRegistryToken.class, AmazonECSRegistryCredential.class); 45 | } 46 | 47 | @NonNull 48 | @Override 49 | public DockerRegistryToken convert(@NonNull AmazonECSRegistryCredential credential) 50 | throws AuthenticationTokenException { 51 | LOG.log(Level.FINE, "Converting credential to Docker registry token : {0}", credential.getCredentialsId()); 52 | return new DockerRegistryToken(credential.getEmail(), Secret.toString(credential.getPassword())); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/cloudbees/jenkins/plugins/amazonecr/AmazonECSRegistryCredentialsProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2015, CloudBees, Inc. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | package com.cloudbees.jenkins.plugins.amazonecr; 27 | 28 | import com.cloudbees.jenkins.plugins.awscredentials.AmazonWebServicesCredentials; 29 | import com.cloudbees.plugins.credentials.Credentials; 30 | import com.cloudbees.plugins.credentials.CredentialsProvider; 31 | import com.cloudbees.plugins.credentials.domains.DomainRequirement; 32 | import edu.umd.cs.findbugs.annotations.NonNull; 33 | import edu.umd.cs.findbugs.annotations.Nullable; 34 | import hudson.Extension; 35 | import hudson.model.ItemGroup; 36 | import java.util.Collections; 37 | import java.util.LinkedList; 38 | import java.util.List; 39 | import java.util.logging.Level; 40 | import java.util.logging.Logger; 41 | import org.springframework.security.core.Authentication; 42 | import software.amazon.awssdk.regions.Region; 43 | 44 | /** 45 | * This class automatically wraps existing {@link AmazonWebServicesCredentials} instances into a 46 | * username password credential type that is compatible with Docker remote API client plugin. 47 | */ 48 | @Extension 49 | public class AmazonECSRegistryCredentialsProvider extends CredentialsProvider { 50 | 51 | private static final Logger LOG = Logger.getLogger(AmazonECSRegistryCredentialsProvider.class.getName()); 52 | 53 | @NonNull 54 | @Override 55 | public List getCredentialsInItemGroup( 56 | @NonNull Class type, 57 | @Nullable ItemGroup itemGroup, 58 | @Nullable Authentication authentication, 59 | @NonNull List domainRequirements) { 60 | 61 | if (!type.isAssignableFrom(AmazonECSRegistryCredential.class)) { 62 | return Collections.emptyList(); 63 | } 64 | 65 | List derived = new LinkedList<>(); 66 | 67 | final List list = lookupCredentialsInItemGroup( 68 | AmazonWebServicesCredentials.class, itemGroup, authentication, domainRequirements); 69 | 70 | for (AmazonWebServicesCredentials credentials : list) { 71 | LOG.log( 72 | Level.FINE, 73 | "Resolving Amazon Web Services credentials of scope {0} with id {1} , itemgroup {2}", 74 | new Object[] {credentials.getScope(), credentials.getId(), itemGroup}); 75 | derived.add((C) new AmazonECSRegistryCredential( 76 | credentials.getScope(), credentials.getId(), credentials.getDescription(), itemGroup)); 77 | 78 | for (Region region : Region.regions()) { 79 | LOG.log( 80 | Level.FINE, 81 | "Resolving Amazon Web Services credentials of scope {0} with id {1} and region {2}", 82 | new Object[] {credentials.getScope(), credentials.getId(), region}); 83 | derived.add((C) new AmazonECSRegistryCredential( 84 | credentials.getScope(), credentials.getId(), region, credentials.getDescription(), itemGroup)); 85 | } 86 | } 87 | 88 | return derived; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4.0.0 5 | 6 | 7 | org.jenkins-ci.plugins 8 | plugin 9 | 5.22 10 | 11 | 12 | com.cloudbees.jenkins.plugins 13 | amazon-ecr 14 | ${revision}.${changelist} 15 | hpi 16 | 17 | Amazon ECR plugin 18 | Integrate Jenkins with Amazon EC2 Container Registry (ECR) 19 | https://github.com/jenkinsci/amazon-ecr-plugin 20 | 21 | 22 | MIT License 23 | http://opensource.org/licenses/MIT 24 | 25 | 26 | 27 | 28 | 29 | TobiX 30 | Tobias Gruetzmacher 31 | tobias-git@23.gs 32 | 33 | Maintainer 34 | 35 | 36 | 37 | 38 | 39 | 40 | Nicolas De Loof 41 | 42 | Maintainer (retired) 43 | 44 | 45 | 46 | Ivan Fernandez Calvo 47 | 48 | Maintainer (retired) 49 | 50 | 51 | 52 | 53 | 54 | scm:git:https://github.com/${gitHubRepo}.git 55 | scm:git:git@github.com:${gitHubRepo}.git 56 | ${scmTag} 57 | https://github.com/${gitHubRepo} 58 | 59 | 60 | 61 | 1 62 | 999999-SNAPSHOT 63 | jenkinsci/amazon-ecr-plugin 64 | 65 | 2.479 66 | ${jenkins.baseline}.3 67 | false 68 | false 69 | 70 | 71 | 72 | 73 | 74 | io.jenkins.tools.bom 75 | bom-${jenkins.baseline}.x 76 | 5054.v620b_5d2b_d5e6 77 | pom 78 | import 79 | 80 | 81 | 82 | 83 | 84 | io.jenkins.plugins.aws-java-sdk2 85 | aws-java-sdk2-ecr 86 | 87 | 88 | org.jenkins-ci.plugins 89 | aws-credentials 90 | 91 | 92 | org.jenkins-ci.plugins 93 | credentials 94 | 95 | 96 | org.jenkins-ci.plugins 97 | docker-commons 98 | 99 | 100 | org.jenkins-ci.plugins 101 | docker-workflow 102 | test 103 | 104 | 105 | org.jenkins-ci.plugins.workflow 106 | workflow-job 107 | test 108 | 109 | 110 | 111 | 112 | 113 | repo.jenkins-ci.org 114 | https://repo.jenkins-ci.org/public/ 115 | 116 | 117 | 118 | 119 | repo.jenkins-ci.org 120 | https://repo.jenkins-ci.org/public/ 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Amazon ECR Plugin 2 | 3 | [![Jenkins Plugin](https://img.shields.io/jenkins/plugin/v/amazon-ecr.svg)](https://plugins.jenkins.io/amazon-ecr) 4 | [![GitHub release](https://img.shields.io/github/release/jenkinsci/amazon-ecr-plugin.svg?label=release)](https://github.com/jenkinsci/amazon-ecr-plugin/releases/latest) 5 | [![Jenkins Plugin Installs](https://img.shields.io/jenkins/plugin/i/amazon-ecr.svg?color=blue)](https://plugins.jenkins.io/amazon-ecr) 6 | [![Build Status](https://ci.jenkins.io/buildStatus/icon?job=Plugins%2Famazon-ecr-plugin%2Fmain)](https://ci.jenkins.io/job/Plugins/job/amazon-ecr-plugin/job/main/) 7 | [![GitHub license](https://img.shields.io/github/license/jenkinsci/amazon-ecr-plugin.svg)](https://github.com/jenkinsci/amazon-ecr-plugin/blob/main/LICENSE.txt) 8 | ![GitHub last commit](https://img.shields.io/github/last-commit/jenkinsci/amazon-ecr-plugin) 9 | 10 | This plugin offers integration with [Amazon Container Registry 11 | (ECR)](https://aws.amazon.com/ecr/) as a [DockerRegistryToken] source to convert 12 | Amazon Credentials into a Docker CLI Authentication Token. 13 | 14 | [DockerRegistryToken]: https://github.com/jenkinsci/docker-commons-plugin/blob/master/src/main/java/org/jenkinsci/plugins/docker/commons/credentials/DockerRegistryToken.java 15 | 16 | ## About 17 | 18 | Amazon ECR plugin implements a Docker Token producer to convert Amazon 19 | credentials to Jenkins’ API used by (mostly) all Docker-related plugins. 20 | 21 | Thanks to this producer, you can select your existing registered Amazon 22 | credentials for various Docker operations in Jenkins, for example using the 23 | Docker Build and Publish plugin: 24 | 25 | ![](.github/build-and-publish.png) 26 | 27 | ## Installation 28 | 29 | Navigate to the "Plugin Manager" screen, install the "Amazon ECR" plugin and 30 | restart Jenkins. 31 | 32 | The plugin will use the proxy configured on Jenkins if it is set. 33 | 34 | Recommended logger for troubleshooting, you have to take care where you publish 35 | these logs could contain sensitive information 36 | 37 | - com.cloudbees.jenkins.plugins.amazonecr 38 | - com.amazonaws 39 | - org.apache.http.wire 40 | - org.jenkinsci.plugins.docker.workflow 41 | 42 | ## Docker Pipeline Usage 43 | 44 | When using the [Docker Pipeline 45 | Plugin](https://plugins.jenkins.io/docker-workflow/), in order to obtain an ECR 46 | login credential, you must use the ecr provider prefix. 47 | 48 | ```groovy 49 | docker.withRegistry("https://your.ecr.domain.amazonws.com", "ecr:us-east-1:credential-id") { 50 | docker.image("your-image-name").push() 51 | } 52 | ``` 53 | 54 | If you experience authentication issues, you would try to remove user 55 | docker configuration files on the agents before to run the docker 56 | commands, something like this pipeline script. 57 | 58 | ```groovy 59 | node { 60 | // cleanup current user docker credentials 61 | sh 'rm -f ~/.dockercfg ~/.docker/config.json || true' 62 | 63 | // configure registry 64 | docker.withRegistry('https://ID.ecr.eu-west-1.amazonaws.com', 'ecr:eu-west-1:86c8f5ec-1ce1-4e94-80c2-18e23bbd724a') { 65 | 66 | // build image 67 | def customImage = docker.build("my-image:${env.BUILD_ID}") 68 | 69 | // push image 70 | customImage.push() 71 | } 72 | } 73 | ``` 74 | 75 | ## Development 76 | 77 | ### Testing 78 | 79 | Unfortunately, testing against AWS isn't very straightforward, since you always 80 | need an AWS account with correct setup, which might incur some costs. Current 81 | tests try to make this as easy as possible. You need a user with read 82 | permission to ECR (AWS IAM policy `AmazonEC2ContainerRegistryReadOnly` should 83 | suffice) and an (empty) container registry. The test expect these details in 84 | the following environment variables: 85 | 86 | ```shell 87 | export AWS_ACCESS_KEY_ID= 88 | export AWS_SECRET_ACCESS_KEY= 89 | export AWS_REGISTRY_HOST=.dkr.ecr.us-east-1.amazonaws.com 90 | ``` 91 | 92 | When those are set correctly, `mvn test` should run those tests successfully. 93 | 94 | ### Code Style 95 | 96 | This plugin uses [Google Java Code Style], which is enforced by the [spotless] 97 | plugin. If the build fails because you were using the "wrong" style, you can 98 | fix it by running: 99 | 100 | $ mvn spotless:apply 101 | 102 | to reformat code in the proper style. 103 | 104 | [Google Java Code Style]: https://google.github.io/styleguide/javaguide.html 105 | [spotless]: https://github.com/diffplug/spotless 106 | -------------------------------------------------------------------------------- /src/main/java/com/cloudbees/jenkins/plugins/amazonecr/AmazonECSRegistryCredential.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2015, CloudBees, Inc. 5 | * Copyright (c) 2021, TobiX 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | */ 26 | 27 | package com.cloudbees.jenkins.plugins.amazonecr; 28 | 29 | import com.amazonaws.regions.Regions; 30 | import com.cloudbees.jenkins.plugins.awscredentials.AmazonWebServicesCredentials; 31 | import com.cloudbees.plugins.credentials.CredentialsProvider; 32 | import com.cloudbees.plugins.credentials.CredentialsScope; 33 | import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; 34 | import com.cloudbees.plugins.credentials.impl.BaseStandardCredentials; 35 | import edu.umd.cs.findbugs.annotations.CheckForNull; 36 | import edu.umd.cs.findbugs.annotations.NonNull; 37 | import hudson.ProxyConfiguration; 38 | import hudson.model.ItemGroup; 39 | import hudson.security.ACL; 40 | import hudson.util.Secret; 41 | import java.net.URI; 42 | import java.util.List; 43 | import java.util.logging.Level; 44 | import java.util.logging.Logger; 45 | import java.util.regex.Pattern; 46 | import java.util.stream.Collectors; 47 | import jenkins.model.Jenkins; 48 | import org.apache.commons.lang.StringUtils; 49 | import software.amazon.awssdk.http.apache.ApacheHttpClient; 50 | import software.amazon.awssdk.regions.Region; 51 | import software.amazon.awssdk.services.ecr.EcrClient; 52 | import software.amazon.awssdk.services.ecr.model.AuthorizationData; 53 | import software.amazon.awssdk.services.ecr.model.GetAuthorizationTokenRequest; 54 | import software.amazon.awssdk.services.ecr.model.GetAuthorizationTokenResponse; 55 | 56 | /** 57 | * This new kind of credential provides an embedded {@link software.amazon.awssdk.auth.credentials.AwsCredentials} when a 58 | * credential for Amazon ECS Registry end point is needed. 59 | */ 60 | public class AmazonECSRegistryCredential extends BaseStandardCredentials 61 | implements StandardUsernamePasswordCredentials { 62 | private static final Logger LOG = Logger.getLogger(AmazonECSRegistryCredential.class.getName()); 63 | 64 | private final String credentialsId; 65 | 66 | private final String region; 67 | 68 | private final ItemGroup itemGroup; 69 | 70 | public AmazonECSRegistryCredential( 71 | CredentialsScope scope, @NonNull String credentialsId, String description, ItemGroup itemGroup) { 72 | this(scope, credentialsId, Region.US_EAST_1, description, itemGroup); 73 | } 74 | 75 | @Deprecated 76 | public AmazonECSRegistryCredential( 77 | @CheckForNull CredentialsScope scope, 78 | @NonNull String credentialsId, 79 | Regions region, 80 | String description, 81 | ItemGroup itemGroup) { 82 | this(scope, credentialsId, Region.of(region.getName()), description, itemGroup); 83 | } 84 | 85 | public AmazonECSRegistryCredential( 86 | @CheckForNull CredentialsScope scope, 87 | @NonNull String credentialsId, 88 | Region region, 89 | String description, 90 | ItemGroup itemGroup) { 91 | super( 92 | scope, 93 | "ecr:" + region.id() + ":" + credentialsId, 94 | "Amazon ECR Registry:" 95 | + (StringUtils.isNotBlank(description) ? description : credentialsId) 96 | + "-" 97 | + region); 98 | this.credentialsId = credentialsId; 99 | this.region = region.id(); 100 | this.itemGroup = itemGroup; 101 | } 102 | 103 | @NonNull 104 | public String getCredentialsId() { 105 | return credentialsId; 106 | } 107 | 108 | public @CheckForNull AmazonWebServicesCredentials getCredentials() { 109 | LOG.log(Level.FINE, "Looking for Amazon web credentials ID: {0} Region: {1}", new Object[] { 110 | this.credentialsId, this.region 111 | }); 112 | List credentials = CredentialsProvider.lookupCredentialsInItemGroup( 113 | AmazonWebServicesCredentials.class, itemGroup, ACL.SYSTEM2); 114 | 115 | if (LOG.isLoggable(Level.FINEST)) { 116 | String fullStackTrace = org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(new Throwable()); 117 | LOG.log(Level.FINEST, "Trace: {0}", fullStackTrace); 118 | } 119 | 120 | if (credentials.isEmpty()) { 121 | LOG.fine("ID not found"); 122 | return null; 123 | } 124 | 125 | for (AmazonWebServicesCredentials awsCredentials : credentials) { 126 | if (awsCredentials.getId().equals(this.credentialsId)) { 127 | LOG.log(Level.FINE, "ID found {0}", this.credentialsId); 128 | return awsCredentials; 129 | } 130 | } 131 | LOG.fine("ID not found"); 132 | return null; 133 | } 134 | 135 | @NonNull 136 | @Override 137 | public String getDescription() { 138 | String description = super.getDescription(); 139 | LOG.finest(description); 140 | return description; 141 | } 142 | 143 | @NonNull 144 | @Override 145 | public Secret getPassword() { 146 | final AmazonWebServicesCredentials credentials = getCredentials(); 147 | if (credentials == null) throw new IllegalStateException("Invalid credentials"); 148 | LOG.log(Level.FINE, "Get password for {0} region : {1}", new Object[] {credentials.getDisplayName(), region}); 149 | if (LOG.isLoggable(Level.ALL)) { 150 | String fullStackTrace = org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(new Throwable()); 151 | LOG.log(Level.ALL, "Trace: {0}", fullStackTrace); 152 | } 153 | ApacheHttpClient.Builder builder = ApacheHttpClient.builder(); 154 | Jenkins instance = Jenkins.getInstanceOrNull(); 155 | ProxyConfiguration proxy = instance != null ? instance.proxy : null; 156 | if (proxy != null) { 157 | software.amazon.awssdk.http.apache.ProxyConfiguration.Builder proxyConfiguration = 158 | software.amazon.awssdk.http.apache.ProxyConfiguration.builder() 159 | .endpoint(URI.create(String.format("http://%s:%s", proxy.name, proxy.port))); 160 | if (proxy.getUserName() != null) { 161 | proxyConfiguration.username(proxy.getUserName()); 162 | proxyConfiguration.password(Secret.toString(proxy.getSecretPassword())); 163 | } 164 | List patterns = proxy.getNoProxyHostPatterns(); 165 | if (patterns != null && !patterns.isEmpty()) { 166 | proxyConfiguration.nonProxyHosts( 167 | patterns.stream().map(Pattern::pattern).collect(Collectors.toSet())); 168 | } 169 | builder.proxyConfiguration(proxyConfiguration.build()); 170 | } 171 | 172 | try (EcrClient client = EcrClient.builder() 173 | .httpClientBuilder(builder) 174 | .region(Region.of(region)) 175 | .credentialsProvider(credentials) 176 | .build()) { 177 | 178 | GetAuthorizationTokenRequest request = 179 | GetAuthorizationTokenRequest.builder().build(); 180 | final GetAuthorizationTokenResponse authorizationToken = client.getAuthorizationToken(request); 181 | final List authorizationData = authorizationToken.authorizationData(); 182 | if (authorizationData == null || authorizationData.isEmpty()) { 183 | throw new IllegalStateException("Failed to retrieve authorization token for Amazon ECR"); 184 | } 185 | LOG.fine("Success"); 186 | if (LOG.isLoggable(Level.ALL)) { 187 | LOG.finest("Auth token: " + authorizationToken); 188 | LOG.finest("Request: " + request); 189 | } 190 | return Secret.fromString(authorizationData.get(0).authorizationToken()); 191 | } 192 | } 193 | 194 | @NonNull 195 | @Override 196 | public String getUsername() { 197 | return "AWS"; 198 | } 199 | 200 | @NonNull 201 | public String getEmail() { 202 | return "nobody@example.com"; 203 | } 204 | } 205 | --------------------------------------------------------------------------------