pax_global_header00006660000000000000000000000064151731135530014516gustar00rootroot0000000000000052 comment=d70a0f36fef3678987a50b8a1cd6edf1d6d5e446 amazon-ecr-credential-helper-0.12.0/000077500000000000000000000000001517311355300172175ustar00rootroot00000000000000amazon-ecr-credential-helper-0.12.0/.codebuild/000077500000000000000000000000001517311355300212275ustar00rootroot00000000000000amazon-ecr-credential-helper-0.12.0/.codebuild/buildspec.yml000066400000000000000000000010701517311355300237220ustar00rootroot00000000000000version: 0.2 phases: install: commands: - chmod +x -R scripts - ./scripts/container_init.sh - ./scripts/hack/codepipeline-git-commit.sh - ./scripts/hack/symlink-gopath-codebuild.sh - cd /go/src/github.com/awslabs/amazon-ecr-credential-helper pre_build: commands: - echo "Starting tests..." - make test build: commands: - echo "Starting build..." - make all-variants post_build: commands: - echo "Build completed on $(date)" artifacts: files: - '**/*' base-directory: 'bin' amazon-ecr-credential-helper-0.12.0/.codebuild/source-archive.yml000066400000000000000000000006441517311355300246750ustar00rootroot00000000000000version: 0.2 phases: build: commands: - ./scripts/hack/codepipeline-source-archive.sh - ./scripts/hack/version-changelog.sh | tee archive/VERSION_CHANGELOG.md post_build: commands: - echo "Archive completed on $(date)" artifacts: files: - 'release.tar.gz' - 'release-novendor.tar.gz' - 'VERSION' - 'GITCOMMIT_SHA' - 'VERSION_CHANGELOG.md' base-directory: 'archive' amazon-ecr-credential-helper-0.12.0/.github/000077500000000000000000000000001517311355300205575ustar00rootroot00000000000000amazon-ecr-credential-helper-0.12.0/.github/CODEOWNERS000066400000000000000000000000411517311355300221450ustar00rootroot00000000000000* @awslabs/runtime @awslabs/ecr amazon-ecr-credential-helper-0.12.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001517311355300227425ustar00rootroot00000000000000amazon-ecr-credential-helper-0.12.0/.github/ISSUE_TEMPLATE/bug_report.yml000066400000000000000000000017161517311355300256420ustar00rootroot00000000000000name: Bug Report description: Submit a bug report to improve amazon-ecr-credential-helper labels: ["bug"] body: - type: textarea attributes: label: Description description: | Briefly describe the problem/bug you are experiencing. validations: required: true - type: textarea attributes: label: Steps to reproduce value: | 1. 2. 3. - type: textarea attributes: label: Describe the results you expected validations: required: true - type: textarea attributes: label: Logs / Screenshots description: Please attach logs and/or screenshots from both docker and the credential helper (found under `~/.ecr/logs`) - type: textarea attributes: label: System information value: | 1. amazon-ecr-credential-helper version: 2. docker version: 3. environment (e.g. EKS, EC2, Windows): validations: required: true amazon-ecr-credential-helper-0.12.0/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000000331517311355300247260ustar00rootroot00000000000000blank_issues_enabled: true amazon-ecr-credential-helper-0.12.0/.github/ISSUE_TEMPLATE/third_party_license_usage_request.yml000066400000000000000000000013721517311355300324570ustar00rootroot00000000000000name: 3rd Party License Request description: File a request for usage of a 3rd party license in the Amazon ECR credential helpers project. title: "[3rd Party License Request]: " labels: "license-request" body: - type: markdown attributes: value: | Thanks for taking the time to fill out this request! - type: textarea id: license-request attributes: label: License request value: | License: - type: textarea id: use-case attributes: label: Use case description: | Briefly describe the use case the dependency would resolve. validations: required: true - type: textarea id: other-solutions attributes: label: Other solutions considered amazon-ecr-credential-helper-0.12.0/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000002511517311355300243560ustar00rootroot00000000000000*Issue #, if available:* *Description of changes:* By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. amazon-ecr-credential-helper-0.12.0/.github/dependabot.yml000066400000000000000000000011221517311355300234030ustar00rootroot00000000000000version: 2 updates: # Dependencies listed in ecr-login/go.mod - package-ecosystem: "gomod" directory: "/ecr-login" schedule: interval: "daily" groups: # Group updates from github.com/aws/aws-sdk-go-v2 dependencies aws-sdk-go-v2: patterns: - "github.com/aws/aws-sdk-go-v2/*" # Dependencies listed in .github/workflows/*.yml - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" # Base image in Dockerfile - package-ecosystem: "docker" directory: "/" schedule: interval: "daily" amazon-ecr-credential-helper-0.12.0/.github/dependency-review-config.yml000066400000000000000000000003751517311355300261670ustar00rootroot00000000000000# Fail third party dependency usage if not covered by the curated set of pre-approved licenses. # # List was generated from guidance set forth by Amazon open source usage policies. allow-licenses: - 'Apache-2.0' - 'BSD-3-Clause' - 'ISC' - 'MIT' amazon-ecr-credential-helper-0.12.0/.github/new-pull-request-labels.yml000066400000000000000000000011151517311355300257710ustar00rootroot00000000000000aws_partition_change: - changed-files: - any-glob-to-any-file: ['ecr-login/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/awsrulesfn/partitions.*'] dependencies: - changed-files: - any-glob-to-any-file: ['**/go.mod', '**/go.sum'] documentation: - changed-files: - all-globs-to-all-files: ['**/*.md'] github_actions: - changed-files: - any-glob-to-any-file: ['.github/**', 'scripts/**'] go: - changed-files: - any-glob-to-any-file: ['**/*.go'] testing: - changed-files: - any-glob-to-any-file: ['integration/**', '**/*_test.go'] amazon-ecr-credential-helper-0.12.0/.github/workflows/000077500000000000000000000000001517311355300226145ustar00rootroot00000000000000amazon-ecr-credential-helper-0.12.0/.github/workflows/build.yaml000066400000000000000000000025461517311355300246060ustar00rootroot00000000000000name: Build on: push: branches: [ main ] pull_request: branches: [ main ] jobs: git-secrets: runs-on: 'ubuntu-22.04' steps: - name: Pull latest awslabs/git-secrets repo uses: actions/checkout@v6 with: repository: awslabs/git-secrets ref: 1.3.0 fetch-tags: true path: git-secrets - name: Install git secrets from source run: sudo make install working-directory: git-secrets - uses: actions/checkout@v6 - name: Scan repository for git secrets run: | git secrets --register-aws git secrets --scan-history cross-compile: runs-on: 'ubuntu-22.04' steps: - uses: actions/checkout@v6 - name: Cross-compile all variants run: make all-variants-in-docker unit-test: strategy: matrix: go: ['1.25', '1.26'] # Intentionally use specific versions instead of "latest" to # make this build reproducible. os: ['ubuntu-24.04', 'macos-15', 'windows-2025'] # Build all variants regardless of failures fail-fast: false name: unit-test (${{ matrix.os }} / Go ${{ matrix.go }}) runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v6 - uses: actions/setup-go@v6 with: go-version: ${{ matrix.go }} - run: make test amazon-ecr-credential-helper-0.12.0/.github/workflows/check-links.yml000066400000000000000000000011311517311355300255260ustar00rootroot00000000000000name: Check Links on: workflow_dispatch: schedule: - cron: "0 0 * * 3" # Every Wednesday at 00:00 UTC pull_request: paths: - ".github/workflows/check-links.yml" jobs: check: runs-on: ubuntu-22.04 if: github.repository == 'awslabs/amazon-ecr-credential-helper' name: lychee timeout-minutes: 15 steps: - uses: actions/checkout@v6 - uses: lycheeverse/lychee-action@v2.8.0 with: fail: true args: --exclude-path ecr-login/vendor --timeout 30 --no-progress './**/*.md' format: markdown jobSummary: true amazon-ecr-credential-helper-0.12.0/.github/workflows/codeql.yml000066400000000000000000000014421517311355300246070ustar00rootroot00000000000000name: "CodeQL Scan" on: push: branches: [ "main" ] pull_request: branches: [ "main" ] schedule: - cron: '25 21 * * 5' jobs: analyze: name: Analyze runs-on: ubuntu-22.04 permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'go' ] steps: - name: Checkout repository uses: actions/checkout@v6 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} - name: Build run: make build - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v4 with: category: "/language:${{matrix.language}}" amazon-ecr-credential-helper-0.12.0/.github/workflows/new-pull-requests.yml000066400000000000000000000013441517311355300267550ustar00rootroot00000000000000name: "New Pull Requests" on: # It is safe to use pull_request_target here because we are not checking out # code from the pull request branch. # # See https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ pull_request_target: permissions: contents: read jobs: label: if: github.event.pull_request.draft == false runs-on: ubuntu-22.04 permissions: pull-requests: write steps: # Use label configuration from main instead of from the pull request branch # to mitigate running untrusted workflows with write permissions. - uses: actions/labeler@v6 with: configuration-path: '.github/new-pull-request-labels.yml' sync-labels: true amazon-ecr-credential-helper-0.12.0/.github/workflows/review-dependencies.yml000066400000000000000000000011771517311355300272720ustar00rootroot00000000000000name: Review dependencies on: pull_request: branches: ['main', 'release/**'] paths: - 'ecr-login/go.*' jobs: review: runs-on: ubuntu-latest permissions: # Write permissions needed to comment review results on PR. # Pwn request risk mitigated by using pull_request workflow trigger # and external contributor workflow runs require maintainer approval. pull-requests: write steps: - uses: actions/checkout@v6 - uses: actions/dependency-review-action@v4 with: config-file: './.github/dependency-review-config.yml' comment-summary-in-pr: always amazon-ecr-credential-helper-0.12.0/.gitignore000066400000000000000000000000431517311355300212040ustar00rootroot00000000000000/bin/ GITCOMMIT_SHA *.tar.gz *.tar amazon-ecr-credential-helper-0.12.0/CHANGELOG.md000066400000000000000000000113721517311355300210340ustar00rootroot00000000000000# 0.12.0 * Support dual stack ECR public endpoint ([#1055](https://github.com/awslabs/amazon-ecr-credential-helper/pull/1055)) * Migrate MD5 to SHA256 for cache key ([#1064](https://github.com/awslabs/amazon-ecr-credential-helper/pull/1064)). * Add url redactor ([#1056](https://github.com/awslabs/amazon-ecr-credential-helper/pull/1056)). * Drop golang 1.24 support. * Upgrade dependencies. # 0.11.0 * Add support for AWS EUSC partition ([#1034](https://github.com/awslabs/amazon-ecr-credential-helper/pull/1034)). * Upgrade dependencies. # 0.10.1 * Drop golang 1.22 support. * Upgrade dependencies. # 0.10.0 * Enhancement - Updated ECR pattern for ECR dual-stack endpoints for IPv6 support. ([#967](https://github.com/awslabs/amazon-ecr-credential-helper/issues/967)) # 0.9.1 * Drop golang 1.21 support. * Upgrade dependencies. # 0.9.0 * Enhancement - Added support for environment variable `AWS_ECR_IGNORE_CREDS_STORAGE=true` to ignore ADD and DELETE requests. This makes tools that try to `docker login` work with registries managed the amazon-ecr-credential-helper. ([#102](https://github.com/awslabs/amazon-ecr-credential-helper/issues/102) and [#847](https://github.com/awslabs/amazon-ecr-credential-helper/pull/847)) * Enhancement - Updated ECR pattern for new isolated regions. ([#850](https://github.com/awslabs/amazon-ecr-credential-helper/pull/850)) * Upgraded dependencies. # 0.8.0 * Enhancement - Updated ECR pattern to match C2S environments. ([#433](https://github.com/awslabs/amazon-ecr-credential-helper/issues/433)) * Feature (Experimental) - Added support for building Windows ARM credential helper binaries. ([#795](https://github.com/awslabs/amazon-ecr-credential-helper/issues/795)) # 0.7.1 **Note: v0.7.1 is functionally equivalent to v0.7.0. We have decided to create a duplicate release to reflect a more accurate changelog, since our v0.7.0 release did not contain any direct/indirect security patches.** * Feature - Allow callers to set log output. ([#309](https://github.com/awslabs/amazon-ecr-credential-helper/pull/309) and [#312](https://github.com/awslabs/amazon-ecr-credential-helper/pull/312)) * Upgrade dependencies for bug fixes. # 0.7.0 * Feature - Allow callers to set log output. ([#309](https://github.com/awslabs/amazon-ecr-credential-helper/pull/309) and [#312](https://github.com/awslabs/amazon-ecr-credential-helper/pull/312)) * Upgrade dependencies for bug fixes and security patches. # 0.6.0 * Feature - Added support for [AWS SSO](https://aws.amazon.com/single-sign-on/) ([#229](https://github.com/awslabs/amazon-ecr-credential-helper/issues/229)) * Feature - Added support to assume roles via EC2 instance metadata. ([#282](https://github.com/awslabs/amazon-ecr-credential-helper/issues/282)) * Feature - Added support for [Apple Silicon](https://go.dev/doc/go1.16#darwin) ([#291](https://github.com/awslabs/amazon-ecr-credential-helper/pull/291)) * Enhancement - The AWS shared config file (`~/.aws/config`) is now always enabled. (`AWS_SDK_LOAD_CONFIG` environment variable is no longer supported) ([#282](https://github.com/awslabs/amazon-ecr-credential-helper/issues/282)) # 0.5.0 * Feature - Added support for [ECR Public](https://gallery.ecr.aws) ([#253](https://github.com/awslabs/amazon-ecr-credential-helper/pull/253)) * Feature - Added support for [EC2 IMDSv2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html) ([#240](https://github.com/awslabs/amazon-ecr-credential-helper/pull/240)) * Enhancement - The AWS shared config file (`~/.aws/config`) is now enabled by default. This can be disabled by setting the environment variable `AWS_SDK_LOAD_CONFIG` to `false` ([#201](https://github.com/awslabs/amazon-ecr-credential-helper/pull/201)) * Bug - Fixed an issue where output from a `credential_process` was sometimes too big for the default buffer ([#240](https://github.com/awslabs/amazon-ecr-credential-helper/pull/240)) # 0.4.0 * Feature - Added support for chaining assumed roles in the shared config file (`~/.aws/config`) defined by `source_profile` and `credential_source` ([#177](https://github.com/awslabs/amazon-ecr-credential-helper/pull/177)) * Feature - Added support for Web Identities and [IAM Roles for Service Accounts (IRSA)](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) with Kubernetes ([#183](https://github.com/awslabs/amazon-ecr-credential-helper/pull/183)) * Bug - Fixed the `make docker` target when the credential helper git repository is used as a git submodule ([#184](https://github.com/awslabs/amazon-ecr-credential-helper/issues/184)) # 0.3.1 * Bug - Log directory is now automatically created when the helper runs # 0.3.0 * Feature - Added support for PrivateLink endpoints # 0.2.0 * Feature - Added support for FIPS endpoints # 0.1.0 Initial release amazon-ecr-credential-helper-0.12.0/CODE_OF_CONDUCT.md000066400000000000000000000004671517311355300220250ustar00rootroot00000000000000## Code of Conduct This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact opensource-codeofconduct@amazon.com with any additional questions or comments. amazon-ecr-credential-helper-0.12.0/CONTRIBUTING.md000066400000000000000000000070671517311355300214620ustar00rootroot00000000000000# Contributing Guidelines Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional documentation, we greatly value feedback and contributions from our community. Please read through this document before submitting any issues or pull requests to ensure we have all the necessary information to effectively respond to your bug report or contribution. ## Reporting Bugs/Feature Requests We welcome you to use the GitHub issue tracker to report bugs or suggest features. When filing an issue, please check [existing open](https://github.com/awslabs/amazon-ecr-credential-helper/issues), or [recently closed](https://github.com/awslabs/amazon-ecr-credential-helper/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: * A reproducible test case or series of steps * The version of our code being used * Any modifications you've made relevant to the bug * Anything unusual about your environment or deployment ## Contributing via Pull Requests Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 1. You are working against the latest source on the *master* branch. 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. To send us a pull request, please: 1. Fork the repository. 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 3. Ensure local tests pass. 4. Commit to your fork using clear commit messages. 5. Send us a pull request, answering any default questions in the pull request interface. 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). ## Finding contributions to work on Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/awslabs/amazon-ecr-credential-helper/labels/help%20wanted) issues is a great place to start. ## Code of Conduct This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact opensource-codeofconduct@amazon.com with any additional questions or comments. ## Security issue notifications If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. ## Licensing See the [LICENSE](https://github.com/awslabs/amazon-ecr-credential-helper/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. amazon-ecr-credential-helper-0.12.0/Dockerfile000066400000000000000000000013771517311355300212210ustar00rootroot00000000000000# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You # may not use this file except in compliance with the License. A copy of # the License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. FROM public.ecr.aws/docker/library/golang:1.26-alpine WORKDIR /go/src/github.com/awslabs/amazon-ecr-credential-helper COPY ./scripts/container_init.sh /setup/container_init.sh RUN /setup/container_init.sh amazon-ecr-credential-helper-0.12.0/GITCOMMIT_SHA000066400000000000000000000000101517311355300211600ustar00rootroot000000000000007ff7d6b amazon-ecr-credential-helper-0.12.0/LICENSE000066400000000000000000000244621517311355300202340ustar00rootroot00000000000000Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and 2. You must cause any modified files to carry prominent notices stating that You changed the files; and 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. *** Note: Other license terms may apply to certain, identified software files contained within or distributed with the accompanying software if such terms are included in the directory containing the accompanying software. Such other license terms will then apply in lieu of the terms of the software license above. amazon-ecr-credential-helper-0.12.0/Makefile000066400000000000000000000100771517311355300206640ustar00rootroot00000000000000# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You # may not use this file except in compliance with the License. A copy of # the License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. ROOT := $(shell pwd) all: build SOURCEDIR=./ecr-login SOURCES := $(shell find $(SOURCEDIR) -name '*.go') VERSION := $(shell cat VERSION) GITFILES := $(shell test -d .git && find ".git/" -type f) UID:=$(shell id -u) GID:=$(shell id -g) BINPATH:=$(abspath ./bin) BINARY_NAME=docker-credential-ecr-login LOCAL_BINARY=$(BINPATH)/local/$(BINARY_NAME) LINUX_AMD64_BINARY=$(BINPATH)/linux-amd64/$(BINARY_NAME) LINUX_ARM64_BINARY=$(BINPATH)/linux-arm64/$(BINARY_NAME) DARWIN_AMD64_BINARY=$(BINPATH)/darwin-amd64/$(BINARY_NAME) DARWIN_ARM64_BINARY=$(BINPATH)/darwin-arm64/$(BINARY_NAME) WINDOWS_AMD64_BINARY=$(BINPATH)/windows-amd64/$(BINARY_NAME).exe WINDOWS_ARM64_BINARY=$(BINPATH)/windows-arm64/$(BINARY_NAME).exe .PHONY: docker docker: build-in-docker %-in-docker: GITCOMMIT_SHA docker run --rm \ --user $(UID):$(GID) \ --env TARGET_GOOS=$(TARGET_GOOS) \ --env TARGET_GOARCH=$(TARGET_GOARCH) \ --volume $(ROOT):/go/src/github.com/awslabs/amazon-ecr-credential-helper \ $(shell docker build -q .) \ make $(subst -in-docker,,$@) .PHONY: build build: $(LOCAL_BINARY) $(LOCAL_BINARY): $(SOURCES) GITCOMMIT_SHA ./scripts/build_binary.sh $(BINPATH)/local $(VERSION) $(shell cat GITCOMMIT_SHA) @echo "Built ecr-login" .PHONY: test test: cd $(SOURCEDIR) && go test -v -timeout 30s -short -cover ./... .PHONY: all-variants all-variants: linux-amd64 linux-arm64 darwin-amd64 darwin-arm64 windows-amd64 windows-arm64 .PHONY: linux-amd64 linux-amd64: $(LINUX_AMD64_BINARY) $(LINUX_AMD64_BINARY): $(SOURCES) GITCOMMIT_SHA ./scripts/build_variant.sh linux amd64 $(VERSION) $(shell cat GITCOMMIT_SHA) .PHONY: linux-arm64 linux-arm64: $(LINUX_ARM64_BINARY) $(LINUX_ARM64_BINARY): $(SOURCES) GITCOMMIT_SHA ./scripts/build_variant.sh linux arm64 $(VERSION) $(shell cat GITCOMMIT_SHA) .PHONY: darwin-amd64 darwin-amd64: $(DARWIN_AMD64_BINARY) $(DARWIN_AMD64_BINARY): $(SOURCES) GITCOMMIT_SHA ./scripts/build_variant.sh darwin amd64 $(VERSION) $(shell cat GITCOMMIT_SHA) .PHONY: darwin-arm64 darwin-arm64: $(DARWIN_ARM64_BINARY) $(DARWIN_ARM64_BINARY): $(SOURCES) GITCOMMIT_SHA ./scripts/build_variant.sh darwin arm64 $(VERSION) $(shell cat GITCOMMIT_SHA) .PHONY: windows-amd64 windows-amd64: $(WINDOWS_AMD64_BINARY) $(WINDOWS_AMD64_BINARY): $(SOURCES) GITCOMMIT_SHA ./scripts/build_variant.sh windows amd64 $(VERSION) $(shell cat GITCOMMIT_SHA) @mv $(BINPATH)/windows-amd64/$(BINARY_NAME) $(WINDOWS_AMD64_BINARY) .PHONY: windows-arm64 windows-arm64: $(WINDOWS_ARM64_BINARY) $(WINDOWS_ARM64_BINARY): $(SOURCES) GITCOMMIT_SHA ./scripts/build_variant.sh windows arm64 $(VERSION) $(shell cat GITCOMMIT_SHA) @mv $(BINPATH)/windows-arm64/$(BINARY_NAME) $(WINDOWS_ARM64_BINARY) GITCOMMIT_SHA: $(GITFILES) git rev-parse --short=7 HEAD > GITCOMMIT_SHA release.tar: GITCOMMIT_SHA git archive -o release.tar HEAD tar -f release.tar --append GITCOMMIT_SHA --owner 0 --group 0 .PHONY: release-tarball release-tarball: release.tar.gz release.tar.gz: release.tar gzip release.tar .PHONY: release-tarball-no-vendor release-tarball-no-vendor: release-novendor.tar.gz release-novendor.tar.gz: release.tar mv release.tar release-novendor.tar tar -f release-novendor.tar --wildcards --delete 'ecr-login/vendor/*' gzip release-novendor.tar .PHONY: gogenerate gogenerate: ./scripts/gogenerate .PHONY: get-deps get-deps: go install golang.org/x/tools/cmd/goimports@698251aaa532d49ac69d2c416b0241afb2f65ea5 .PHONY: licenses licenses: ./scripts/build_third_party_licenses.sh .PHONY: clean clean: - rm -rf ./bin - rm -f GITCOMMIT_SHA - rm -f release.tar.gz - rm -f release-novendor.tar.gz amazon-ecr-credential-helper-0.12.0/NOTICE000066400000000000000000000001411517311355300201170ustar00rootroot00000000000000Amazon ECR Credential Helper Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. amazon-ecr-credential-helper-0.12.0/README.md000066400000000000000000000405031517311355300205000ustar00rootroot00000000000000# Amazon ECR Docker Credential Helper ![Amazon ECR logo](docs/ecr.png "Amazon ECR") [![Build](https://github.com/awslabs/amazon-ecr-credential-helper/actions/workflows/build.yaml/badge.svg)](https://github.com/awslabs/amazon-ecr-credential-helper/actions/workflows/build.yaml) [![Go Report Card](https://goreportcard.com/badge/github.com/awslabs/amazon-ecr-credential-helper)](https://goreportcard.com/report/github.com/awslabs/amazon-ecr-credential-helper) [![latest packaged version(s)](https://repology.org/badge/latest-versions/amazon-ecr-credential-helper.svg)](https://repology.org/project/amazon-ecr-credential-helper/versions) The Amazon ECR Docker Credential Helper is a [credential helper](https://github.com/docker/docker-credential-helpers) for the Docker daemon that makes it easier to use [Amazon Elastic Container Registry](https://aws.amazon.com/ecr/). ## Table of Contents * [Prerequisites](#prerequisites) * [Installing](#installing) + [Amazon Linux 2023 (AL2023)](#amazon-linux-2023-al2023) + [Amazon Linux 2 (AL2)](#amazon-linux-2-al2) + [Mac OS](#mac-os) + [Debian Buster (and future versions)](#debian-buster-and-future-versions) + [Ubuntu 19.04 Disco Dingo and newer](#ubuntu-1904-disco-dingo-and-newer) + [Arch Linux](#arch-linux) + [Alpine Linux](#alpine-linux) + [Windows](#windows) + [From mise software package manager](#from-mise-software-package-manager) + [From Source](#from-source) * [Configuration](#configuration) + [Docker](#docker) + [AWS credentials](#aws-credentials) + [Amazon ECR Docker Credential Helper](#amazon-ecr-docker-credential-helper-1) * [Usage](#usage) * [Troubleshooting](#troubleshooting) * [Experimental features](#experimental-features) * [Security disclosures](#security-disclosures) * [License](#license) ## Prerequisites You must have at least Docker 1.11 installed on your system. You also must have AWS credentials available. See the [AWS credentials section](#aws-credentials) for details on how to use different AWS credentials. ## Installing ### Amazon Linux 2023 (AL2023) You can install the Amazon ECR Credential Helper from the Amazon Linux 2023 repositories. ```bash sudo dnf install -y amazon-ecr-credential-helper ``` Once you have installed the credential helper, see the [Configuration section](#configuration) for instructions on how to configure Docker to work with the helper. ### Amazon Linux 2 (AL2) You can install the Amazon ECR Credential Helper from the [`docker` or `ecs` extras](https://docs.aws.amazon.com/linux/al2/ug/al2-extras.html). ```bash sudo amazon-linux-extras enable docker sudo yum install amazon-ecr-credential-helper ``` Once you have installed the credential helper, see the [Configuration section](#configuration) for instructions on how to configure Docker to work with the helper. ### Mac OS A community-maintained Homebrew formula is available in the core tap. [![Homebrew package](https://repology.org/badge/version-for-repo/homebrew/amazon-ecr-credential-helper.svg)](https://repology.org/project/amazon-ecr-credential-helper/versions) ```bash brew install docker-credential-helper-ecr ``` On macOS, another community-maintained installation method is to use MacPorts. [![MacPorts package](https://repology.org/badge/version-for-repo/macports/amazon-ecr-credential-helper.svg)](https://repology.org/project/amazon-ecr-credential-helper/versions) ```bash sudo port install docker-credential-helper-ecr ``` Once you have installed the credential helper, see the [Configuration section](#configuration) for instructions on how to configure Docker to work with the helper. ### Debian Buster (and future versions) You can install the Amazon ECR Credential Helper from the Debian Buster archives. This package will also be included in future releases of Debian. [![Debian 11 package](https://repology.org/badge/version-for-repo/debian_11/amazon-ecr-credential-helper.svg)](https://repology.org/project/amazon-ecr-credential-helper/versions) [![Debian 12 package](https://repology.org/badge/version-for-repo/debian_12/amazon-ecr-credential-helper.svg)](https://repology.org/project/amazon-ecr-credential-helper/versions) [![Debian 13 package](https://repology.org/badge/version-for-repo/debian_13/amazon-ecr-credential-helper.svg)](https://repology.org/project/amazon-ecr-credential-helper/versions) [![Debian 14 package](https://repology.org/badge/version-for-repo/debian_14/amazon-ecr-credential-helper.svg)](https://repology.org/project/amazon-ecr-credential-helper/versions) [![Debian Unstable package](https://repology.org/badge/version-for-repo/debian_unstable/amazon-ecr-credential-helper.svg)](https://repology.org/metapackage/amazon-ecr-credential-helper/versions) ```bash sudo apt update sudo apt install amazon-ecr-credential-helper ``` Once you have installed the credential helper, see the [Configuration section](#configuration) for instructions on how to configure Docker to work with the helper. ### Ubuntu 19.04 Disco Dingo and newer You can install the Amazon ECR Credential Helper from the Ubuntu 19.04 Disco Dingo (and newer) archives. [![Ubuntu 20.04 package](https://repology.org/badge/version-for-repo/ubuntu_20_04/amazon-ecr-credential-helper.svg)](https://repology.org/project/amazon-ecr-credential-helper/versions) [![Ubuntu 22.04 package](https://repology.org/badge/version-for-repo/ubuntu_22_04/amazon-ecr-credential-helper.svg)](https://repology.org/project/amazon-ecr-credential-helper/versions) ```bash sudo apt update sudo apt install amazon-ecr-credential-helper ``` Once you have installed the credential helper, see the [Configuration section](#configuration) for instructions on how to configure Docker to work with the helper. ### Arch Linux A community-maintained package is available in the Arch User Repository. [![AUR package](https://repology.org/badge/version-for-repo/aur/amazon-ecr-credential-helper.svg)](https://repology.org/metapackage/amazon-ecr-credential-helper/versions) ```bash git clone https://aur.archlinux.org/amazon-ecr-credential-helper.git cd amazon-ecr-credential-helper makepkg -si ``` Once you have installed the credential helper, see the [Configuration section](#configuration) for instructions on how to configure Docker to work with the helper. ### Alpine Linux A community-maintained package is available in the [Alpine Linux aports Repository](https://pkgs.alpinelinux.org/packages?name=docker-credential-ecr-login). [![Alpine Linux Edge package](https://repology.org/badge/version-for-repo/alpine_edge/amazon-ecr-credential-helper.svg)](https://repology.org/project/amazon-ecr-credential-helper/versions) ```bash apk add docker-credential-ecr-login ``` > [!NOTE] > Badge only shows edge, check [repository](https://pkgs.alpinelinux.org/packages?name=docker-credential-ecr-login) for stable releases or add `--repository=http://dl-cdn.alpinelinux.org/alpine/edge/community` Once you have installed the credential helper, see the [Configuration section](#configuration) for instructions on how to configure Docker to work with the helper. ### Windows Windows executables are available via [GitHub releases](https://github.com/awslabs/amazon-ecr-credential-helper/releases). > [!NOTE] > Windows ARM support is considered [experimental](#experimental-features). > > See https://github.com/awslabs/amazon-ecr-credential-helper/issues/795 ### From mise software package manager To install from [mise](https://github.com/jdx/mise) polyglot package tool manager, you can directly install using a command like this one specifying the version you want to install: ```shell mise use -g amazon-ecr-credential-helper@latest ``` ### From Source To build and install the Amazon ECR Docker Credential Helper, we suggest Go 1.19 or later, `git` and `make` installed on your system. If you just installed Go, make sure you also have added it to your PATH or Environment Vars (Windows). For example: ``` export GOPATH=$HOME/go export PATH=$PATH:$GOPATH/bin ``` Or in Windows: ``` setx GOPATH %USERPROFILE%\go ;%USERPROFILE%\go\bin ``` If you haven't defined the PATH, the command below will fail silently, and running `docker-credential-ecr-login` will output: `command not found` You can install this via the `go` command line tool. To install run: ``` go install github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cli/docker-credential-ecr-login@latest ``` > [!WARNING] > Disclaimer: the [Dockerfile](./Dockerfile) in this repository is used to test cross-compilation of the > Amazon ECR credential helper binaries in GitHub Actions CI and as a developer utility for building locally from source. > It is a reference implementation and not security hardened for building and running production containers. If you already have Docker environment, just clone this repository anywhere and run `make build-in-docker`. This command builds the binary with Go inside the Docker container and output it to local directory. With `TARGET_GOOS` environment variable, you can also cross compile the binary. Once you have installed the credential helper, see the [Configuration section](#configuration) for instructions on how to configure Docker to work with the helper. ## Configuration ### Docker There is no need to use `docker login` or `docker logout`. Place the `docker-credential-ecr-login` binary on your `PATH`. On Windows, depending on whether the executable is ran in the User or System context, the corresponding `Path` user or system variable needs to be used. Following that the configuration for the docker client needs to be updated in `~/.docker/config.json` to use the **ecr-login** helper. Depending on the operating system and context under which docker client will be executed, this configuration can be found in different places. On Linux systems: - `/home//.docker/config.json` for **user** context - `/root/.docker/config.json` for **root** context On Windows: - `C:\Users\\.docker\config.json` for **user** context - `C:\Windows\System32\config\systemprofile\.docker\config.json` for the **SYSTEM** context Set the contents of the file to the following: ```json { "credsStore": "ecr-login" } ``` This configures the Docker daemon to use the credential helper for all Amazon ECR registries. With Docker 1.13.0 or greater, you can configure Docker to use different credential helpers for different ECR registries. To use this credential helper for a specific ECR registry, create a `credHelpers` section with the URI of your ECR registry: ```json { "credHelpers": { "public.ecr.aws": "ecr-login", ".dkr.ecr..amazonaws.com": "ecr-login" } } ``` This is useful if you use `docker` to operate on registries that use different authentication credentials. If you need to authenticate with multiple registries, including non-ECR registries, you can combine credHelpers with auths. For example: ```json { "credHelpers": { ".dkr.ecr..amazonaws.com": "ecr-login" }, "auths": { "ghcr.io": { "auth": [GITHUB_PERSONAL_ACCESS_TOKEN] }, "https://index.docker.io/v1/": { "auth": [docker.io-auth-token] }, "registry.gitlab.com": { "auth": [gitlab-auth-token] } } } ``` ### AWS credentials The Amazon ECR Docker Credential Helper allows you to use AWS credentials stored in different locations. Standard ones include: * The shared credentials file (`~/.aws/credentials`) * The `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables * An [IAM role for an Amazon ECS task](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html) * An [IAM role for Amazon EC2](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html) To use credentials associated with a different named profile in the shared credentials file (`~/.aws/credentials`), you may set the `AWS_PROFILE` environment variable. The Amazon ECR Docker Credential Helper reads and supports some configuration options specified in the AWS shared configuration file (`~/.aws/config`). To disable these options, you must set the `AWS_SDK_LOAD_CONFIG` environment variable to `false`. The supported options include: * Assumed roles specified with `role_arn` and `source_profile` * External credential processes specified with `credential_process` * Web Identities like [IAM Roles for Service Accounts in Kubernetes](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) (*Note: Kubernetes users using containers with a non-root user may encounter permission issues described in [this bug](https://github.com/kubernetes-sigs/external-dns/pull/1185) and may need to employ a workaround adjusting the Kubernetes `securityContext`.*) The Amazon ECR Docker Credential Helper uses the same credentials as the AWS CLI and the AWS SDKs. For more information about configuring AWS credentials, see [Configuration and Credential Files](http://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html) in the *AWS Command Line Interface User Guide*. The credentials must have a policy applied that [allows access to Amazon ECR](https://docs.aws.amazon.com/AmazonECR/latest/userguide/security-iam-awsmanpol.html). ### Amazon ECR Docker Credential Helper | Environment Variable | Sample Value | Description | | ---------------------------- | ------------- | ------------------------------------------------------------------ | | AWS_ECR_DISABLE_CACHE | true | Disables the local file auth cache if set to a non-empty value. When disabled, the credential helper will not store or read cached ECR authorization tokens from the local filesystem, requiring fresh credentials to be fetched from AWS for each Docker operation. This may be useful in environments where persisting credentials to disk is not desired, though it will result in additional API calls to ECR. | | AWS_ECR_CACHE_DIR | ~/.ecr | Specifies the local file auth cache directory location | | AWS_ECR_IGNORE_CREDS_STORAGE | true | Ignore calls to docker login or logout and pretend they succeeded | ## Usage `docker pull 123456789012.dkr.ecr.us-west-2.amazonaws.com/my-repository:my-tag` `docker push 123456789012.dkr.ecr.us-west-2.amazonaws.com/my-repository:my-tag` `docker pull public.ecr.aws/amazonlinux/amazonlinux:latest` If you have configured additional profiles for use with the AWS CLI, you can use those profiles by specifying the `AWS_PROFILE` environment variable when invoking `docker`. For example: `AWS_PROFILE=myprofile docker pull 123456789012.dkr.ecr.us-west-2.amazonaws.com/my-repository:my-tag` There is no need to use `docker login` or `docker logout`. ## Troubleshooting If you have previously authenticated with an ECR repository by using the `docker login` command manually then Docker may have stored an auth token which has since expired. Docker will continue to attempt to use that cached auth token instead of utilizing the credential helper. You must explicitly remove the previously cached expired token using `docker logout 123456789012.dkr.ecr.us-west-2.amazonaws.com/my-repository`. After that Docker will start utilizing the ECR credential helper to fetch fresh credentials, and you will no longer need to use `docker login` or `docker logout`. Logs from the Amazon ECR Docker Credential Helper are stored in `~/.ecr/log`. For more information about Amazon ECR, see the the [Amazon Elastic Container Registry User Guide](http://docs.aws.amazon.com/AmazonECR/latest/userguide/what-is-ecr.html). ## Experimental features Features marked as experimental are optionally made available to users to test and provide feedback. If you test any experimental feaures, you can give feedback via the feature's tracking issue regarding: * Your experience with the feature * Issues or problems * Suggested improvements Experimental features are incomplete in design and implementation. Backwards incompatible changes may be introduced at any time or support dropped entirely. Therefore experimental features are **not recommended** for use in production environments. ## Security disclosures If you think you’ve found a potential security issue, please do not post it in the Issues. Instead, please follow the instructions [here](https://aws.amazon.com/security/vulnerability-reporting/) or [email AWS security directly](mailto:aws-security@amazon.com). ## License The Amazon ECR Docker Credential Helper is licensed under the Apache 2.0 License. amazon-ecr-credential-helper-0.12.0/THIRD-PARTY-LICENSES000066400000000000000000000705331517311355300221240ustar00rootroot00000000000000** github.com/aws/aws-sdk-go-v2; version v1.30.5 - https://github.com/aws/aws-sdk-go-v2/blob/v1.30.5/LICENSE.txt ** github.com/aws/aws-sdk-go-v2/config; version v1.27.33 - https://github.com/aws/aws-sdk-go-v2/blob/config/v1.27.33/config/LICENSE.txt ** github.com/aws/aws-sdk-go-v2/credentials; version v1.17.32 - https://github.com/aws/aws-sdk-go-v2/blob/credentials/v1.17.32/credentials/LICENSE.txt ** github.com/aws/aws-sdk-go-v2/feature/ec2/imds; version v1.16.13 - https://github.com/aws/aws-sdk-go-v2/blob/feature/ec2/imds/v1.16.13/feature/ec2/imds/LICENSE.txt ** github.com/aws/aws-sdk-go-v2/internal/configsources; version v1.3.17 - https://github.com/aws/aws-sdk-go-v2/blob/internal/configsources/v1.3.17/internal/configsources/LICENSE.txt ** github.com/aws/aws-sdk-go-v2/internal/endpoints/v2; version v2.6.17 - https://github.com/aws/aws-sdk-go-v2/blob/internal/endpoints/v2.6.17/internal/endpoints/v2/LICENSE.txt ** github.com/aws/aws-sdk-go-v2/internal/ini; version v1.8.1 - https://github.com/aws/aws-sdk-go-v2/blob/internal/ini/v1.8.1/internal/ini/LICENSE.txt ** github.com/aws/aws-sdk-go-v2/service/ecr; version v1.32.4 - https://github.com/aws/aws-sdk-go-v2/blob/service/ecr/v1.32.4/service/ecr/LICENSE.txt ** github.com/aws/aws-sdk-go-v2/service/ecrpublic; version v1.25.6 - https://github.com/aws/aws-sdk-go-v2/blob/service/ecrpublic/v1.25.6/service/ecrpublic/LICENSE.txt ** github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding; version v1.11.4 - https://github.com/aws/aws-sdk-go-v2/blob/service/internal/accept-encoding/v1.11.4/service/internal/accept-encoding/LICENSE.txt ** github.com/aws/aws-sdk-go-v2/service/internal/presigned-url; version v1.11.19 - https://github.com/aws/aws-sdk-go-v2/blob/service/internal/presigned-url/v1.11.19/service/internal/presigned-url/LICENSE.txt ** github.com/aws/aws-sdk-go-v2/service/sso; version v1.22.7 - https://github.com/aws/aws-sdk-go-v2/blob/service/sso/v1.22.7/service/sso/LICENSE.txt ** github.com/aws/aws-sdk-go-v2/service/ssooidc; version v1.26.7 - https://github.com/aws/aws-sdk-go-v2/blob/service/ssooidc/v1.26.7/service/ssooidc/LICENSE.txt ** github.com/aws/aws-sdk-go-v2/service/sts; version v1.30.7 - https://github.com/aws/aws-sdk-go-v2/blob/service/sts/v1.30.7/service/sts/LICENSE.txt ** github.com/aws/smithy-go; version v1.20.4 - https://github.com/aws/smithy-go/blob/v1.20.4/LICENSE ** github.com/jmespath/go-jmespath; version v0.4.0 - https://github.com/jmespath/go-jmespath/blob/v0.4.0/LICENSE Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -------------------------------------------------------------------------------- ** github.com/aws/aws-sdk-go-v2/internal/sync/singleflight; version v1.30.5 - https://github.com/aws/aws-sdk-go-v2/blob/v1.30.5/internal/sync/singleflight/LICENSE Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- ** github.com/aws/smithy-go/internal/sync/singleflight; version v1.20.4 - https://github.com/aws/smithy-go/blob/v1.20.4/internal/sync/singleflight/LICENSE Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- ** github.com/davecgh/go-spew/spew; version v1.1.1 - https://github.com/davecgh/go-spew/blob/v1.1.1/LICENSE ISC License Copyright (c) 2012-2016 Dave Collins Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -------------------------------------------------------------------------------- ** github.com/docker/docker-credential-helpers/credentials; version v0.8.2 - https://github.com/docker/docker-credential-helpers/blob/v0.8.2/LICENSE Copyright (c) 2016 David Calavera Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- ** github.com/mitchellh/go-homedir; version v1.1.0 - https://github.com/mitchellh/go-homedir/blob/v1.1.0/LICENSE The MIT License (MIT) Copyright (c) 2013 Mitchell Hashimoto Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- ** github.com/pmezard/go-difflib/difflib; version v1.0.0 - https://github.com/pmezard/go-difflib/blob/v1.0.0/LICENSE Copyright (c) 2013, Patrick Mezard All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- ** github.com/sirupsen/logrus; version v1.9.3 - https://github.com/sirupsen/logrus/blob/v1.9.3/LICENSE The MIT License (MIT) Copyright (c) 2014 Simon Eskildsen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- ** github.com/stretchr/testify/assert; version v1.9.0 - https://github.com/stretchr/testify/blob/v1.9.0/LICENSE MIT License Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- ** golang.org/x/sys/unix; version v0.15.0 - https://cs.opensource.google/go/x/sys/+/v0.15.0:LICENSE Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- ** gopkg.in/yaml.v3; version v3.0.1 - https://github.com/go-yaml/yaml/blob/v3.0.1/LICENSE This project is covered by two different licenses: MIT and Apache. #### MIT License #### The following files were ported to Go from C files of libyaml, and thus are still covered by their original MIT license, with the additional copyright staring in 2011 when the project was ported over: apic.go emitterc.go parserc.go readerc.go scannerc.go writerc.go yamlh.go yamlprivateh.go Copyright (c) 2006-2010 Kirill Simonov Copyright (c) 2006-2011 Kirill Simonov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ### Apache License ### All the remaining project files are covered by the Apache license: Copyright (c) 2011-2019 Canonical Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. amazon-ecr-credential-helper-0.12.0/VERSION000066400000000000000000000000071517311355300202640ustar00rootroot000000000000000.12.0 amazon-ecr-credential-helper-0.12.0/docs/000077500000000000000000000000001517311355300201475ustar00rootroot00000000000000amazon-ecr-credential-helper-0.12.0/docs/docker-credential-ecr-login.1000066400000000000000000000075551517311355300255010ustar00rootroot00000000000000.\" Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. .\" .\" Licensed under the Apache License, Version 2.0 (the .\" "License"). You may not use this file except in compliance .\" with the License. A copy of the License is located at .\" .\" http://aws.amazon.com/apache2.0/ .\" .\" or in the "license" file accompanying this file. This file is .\" distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR .\" CONDITIONS OF ANY KIND, either express or implied. See the .\" License for the specific language governing permissions and .\" limitations under the License. .TH DOCKER-CREDENTIAL-ECR-LOGIN 1 2018-10-29 AMAZON-WEB-SERVICES AMAZON-ECR .SH NAME docker\-credential\-ecr\-login \- Amazon ECR Credential Helper for Docker .SH SYNOPSIS .B docker\-credential\-ecr\-login COMMAND .B docker\-credential\-ecr\-login -v .SH DESCRIPTION The Amazon ECR Credential Helper for Docker is a credential helper for the .BR docker (1) command that makes it easier to store and retrieve container images with Amazon Elastic Container Registry. .SH USAGE Place the .B docker\-credential\-ecr\-login binary on your .IR PATH and set the contents of your .IR ~/.docker/config.json to be .nf { "credsStore":"ecr-login" } .fi With Docker 1.13.0 or greater, you can configure Docker to use different credential helpers for different registries. To use this credential helper for a specific ECR registry, create a .IR credsHelper section with the URI of your ECR registry: .nf { "credHelpers": { "public.ecr.aws": "ecr-login", "aws_account_id.dkr.ecr.region.amazonaws.com": "ecr-login" } } .fi Once installed, you may use \fIdocker pull\fP and \fIdocker push\fP with ECR repositories, without running \fIdocker login\fP. .SH AWS CREDENTIALS The credential helper reads AWS credentials from standard locations, including environment variables, the shared credentials file (\fI~/.aws/credentials\fP), EC2 instance profiles, and ECS task roles. To use credentials associated with a different named profile in the shared credentials file, you may set the \fIAWS_PROFILE\fP environment variable. The credential helper reads and supports some configuration options specified in the shared configuration file (\fI~/.aws/config\fP). To disable these options, you must set the \fIAWS_SDK_LOAD_CONFIG\fP environment variable to \fIfalse\fP. The supported options include: .IP \[bu] 2 Assumed roles specified with \fIrole_arn\fP and \fIsource_profile\fP .IP \[bu] External credential processes specified with \fIcredential_process\fP .IP \[bu] Web Identities like IAM roles for Service Accounts in Kubernetes .RE The credentials must have a policy applied that allows access to Amazon ECR. See http://docs.aws.amazon.com/AmazonECR/latest/userguide/ecr_managed_policies.html for more information. .SH OPTIONS .TP 16 .BR -v Print the version and git commit used to build docker\-credential\-ecr\-login .SH COMMANDS .TP 16 .BR get Retrieve credentials for a specific Amazon ECR registry. The registry should be passed to the credential helper via .BR stdin (3) and results will be printed to .BR stdout (3) .TP 16 .BR list Retrieve the names of each registry currently known to the helper. The helper will cache the names of all requested registries and return them here. .TP 16 .BR version Return the version of the credential helper protocol implemented by this helper .TP 16 .BR store This command is not implemented and is a no-op. .TP 16 .BR erase This command is not implemented and is a no-op. .SH BUGS Kubernetes users using the credential helper inside containers with a non-root user may encounter permission issues described here: https://github.com/kubernetes-sigs/external-dns/pull/1185. You may be able to work around this bug by adjusting the Kubernetes \fIsecurityContext\fP. .SH COPYRIGHT Copyright Amazon.com, Inc. or its affiliates. All rights reserved. .SH LICENSE Licensed under the Apache License, version 2.0. amazon-ecr-credential-helper-0.12.0/docs/ecr.png000066400000000000000000000211321517311355300214250ustar00rootroot00000000000000‰PNG  IHDRô·äm¬übKGDÿÿÿ ½§“ pHYs.#.#x¥?vtIMEâôI IDATxÚíÝy|Üôÿñ—œ;ÜPŽRŽB"'@©Šs‘rÉ-š^ðK»´}´ÜG¸iº-[ØÒîB¡Ð–”B¡ËQº”…¿bq5$ƒ€…ØÂ ”†û,ØNFû‡4¶F#i4>Æ“øý|<æáÍŒ4£‘õÑç{‚ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆH}ð;Ú6ð;ÚÂûÚ!""«¡í‚1À—ìü}nŽáwØ—O ý޶Fk»‚ºˆˆºÔw ß­x¯Ñ_²Ç?±üƒ÷€£Àߘ,ò;Ú~lÉÞµãDDV†vÁ æOìŠ1íQü%»í\FëÀÏo„wû¿\b´¶ŸQ êFk»v¢ˆˆ2t ¾KôÑ&þ»^ þC@k¸Œþ¿~ÉãM€Ÿøm¯úm3€ñÊÖED”¡ËhóÇgbL_öY?Žc㔌¼ü¯Q²ì/À‘Fkû Ú³"" èR“@> cúøÏì†ÁÁØ`àçŽo#+¨—-?ø¹ÑÚþ¾ö´ˆH}Q‘ûšÌŸ˜óé;CáÿƒßŽïo€ï§û@¡ô¯_ˆ<»ùám`Ùgü޶Sû·©bxt† ¾xç«üuýǧ^ ~ø{–Õ‘û‘ÇÑ¿å»<°¼g+à,¿£­ÓïhÛ±˜º+°‹ˆŒ.¹¯ÖÁܘáûé`lV±Ž¼¤˜=WQ{Öówß7ZÛ»Ô^DDºTÈíÜ)üÅÖNþâÏýüŸ¿YþŒœ¼yùß’ñ|èô;Ú~LèÿŒÊØED”¡K® ¾çûW—‘'dæÕeä¥ë(}®ø…ÑÚ¾@¿ˆˆºÄƒ÷cÛcìò|xÇu0ŒÓ&hc0™AÃg^4tûžÑÚî«(^DD]Á|áö³ŠÁ|‡€ù`l=¼yüý¹3ò„Àᨚ Ì7ZÛ—)¨‹ˆ( í€þ×í vîÅ`ËŒ:„Œ3òØûJ–-æo§jUQ¼ˆˆúšÌœBÞK‹ýP NÃ`»’©?&å¿\Ip¯zœöa¸(H{溧­íoéQ@_½yûdÚ:)´›Ð0îS@;-•ca¥ ž#ë®IÐÏõÞÃkŒÖöU:"DDOu裹óÛ:)8“·„†«ðý¿ãû-¤Œéâ—TMëª)¿eÖeS¾ÂÔÁe²êæ©#»/ËØ³2r¿´Ž>oáA‘?‹aìkL{ìÿ‰YÓü¦‰4þs7}7MlÂà`À>ô耩Luè£ÌsÖ™—TW§¾':.{òúü’ ?y›ñJù¬•eö_/ûÀÝàcL_¼“1mÑ+þ3ƒ¸?m!}7N,^lÐwÓÄ3€ø´ã€F,"Õ3-{Pω2t©: '—gåØ>øaVœÚ%!«h=1Ÿe÷Ѻuƒ„Œ<¡\ݯ”‘§ƒq¶1ýñþÖnÆ´EôÝòƃºé»qâA¸ X;ZmPë ç:U?'2ÚÇgÒk<×Á´ì`'`Bä¿j™ç:¯¤­WǺº 6¨G£aJ×ðx°®Ô†-ÚÈ.m}eýׄ@]R ý¨Yý×K–ßÌ7¦wü¯ÿøÔþ—öý~ß~ÆouÓwÃÄÝûn˜øS`¯òuÕVx|ØŠ 9~Ñ`ðŽZ©2o<ÍÐJA×¾è¹Îc9^»¹iÙ_ö'¨²úDF†þÌ /ˆ·^5-{wÏuÑÏ®€>6zV@Í“š¦ç ”—–­'ö¬þëAû™ÐðÆÌ§Wø‹>‹1óéþu¯õè½~Òàj|6-M¶¿ÄßH*¾¨Uv¾6Жñ’F`oÓ²ïUÆ!ƒô´ç:7á-«ë6¶|X,÷\'錰8²ŽÆçÿßy®SÈñùÆûÿ.ºÅ´l xKÿ3µ/’ÑêÕŽô–øw …{îñiÙoYãö´ÑéúÜþNÆÌgOƒÂŠþK†H0ï½nÒôÞë&ý¸Ø4iÛ~ÉDpaQ¾_Ó }k`ÇØ²U±ÿ‘/ëÄ$C0¤KÔø±>¾+Ìï6ð\ç/ÀÇ)Á<¾Ž>Ïu–Óç^é¹ÎÀ7ÂŒ~ à”°:@¿¾úæ)=¾ü´îhñ[tTÕ<=áµåë‰MÓZð“>Ç«øLÃ(|ÑØå¹gýǶǘùlÉWìýݤ†Þë&]< ìSœcÛO ì#—ªGN4§Æžú€ ˆ4êÀØ{DFiÙç³Â‡‹€¯y®³j´ê­#ûvàïÑÿ+]+ ¯á½BîÇ‚[…qÜÓ2p?+»ÏYà—~îן³žÿ”1ëù%Œ€±ËóIßr=|'ÚH')ëN ìµ; 'hÑuI˜ýDmaZöyNNiA¿¸<þ¼iÙ똖½žiÙFÚ:7†ïY{1Ñe†iÙë†ë[o°ë©Ak®æ3e¿˜–=.Ü'ëŽö÷Žoß´ìM#Çìr`·b±øhOÏuÛ™Üù¼3u¯-Õ¡F†^r'Þ¼<˜9º£%½Ó¬°b‹ø´úõ`{gŸþíþõÍz¾òÅbÒªÓ–±mÖÆÑ ËÎ /t¯ˆ-?Ÿô–ðñ …¯}á¢qÀ=žëôDžŸÌve ¡TÁ´ì%À<Ïu^Šð='†¥›„ëõMË~øç:ç¤ehÅåáz&„ëØ/ü>ë3ÐÛ0-{%AC¬›=×¹<úþØçù<Õµ†Ž+y®ó¤ÏÞßø°Ð\L@LËîž®ð\綤Ï[炆ckE¶ý ðaäû¬Ì¾ l^Ü'áþ¸˜ï¹Î{£Ùz;üœ6Åê+8Æs•uÖ¢ü©Èý–°AÐ×à€ž§ïx¬WXê¼ç•Zćë-›–<©[\y ÷‡08®a·¥î .ZŒüA¼ÖÝÕL˶¨=×ù(|¾ÒÆrSMËÞx9ÇÉóöØã}MËþ 08— o{’ýMËþpNxŸfZö© Ÿµh3àlÓ²'è ørÊ÷=8Ø=ÇîÙh3-û2à[žëܘ4.f ñgøÁ¿%ÁÜ´ìYáÔ.ïÝØÇ´ìwË ›ŠA ¿Ïf ¶¢Ýºöö\ç/¦eïhZö÷C3¶up˜iÙz®sûheçá÷:)\ô.ð@v‹ŸÔ ¿¶Tä>ZA=ï©ÅbøBBùÌâw’ë×3êð#èï…'þ½v_êž2ÄÒˆ„ïÿ®i¯¡“#°0=öÔÍ‘ûóÞ:g›<†`‘Û2‚yÔÙÀ¦eß ü5#˜—|-`±iÙ¦\Þ]‘3˜ÇÝ`Zö÷8-âÏÐCdŸH0?• õö.9׳p£iÙwK!RJ>Š-k3-û×Ý»͹­MËž5Eï‘ïµ[ø÷uÏu^®Ã:ê­#÷_ÓÉ^}l󤀚ô\¸¬¬áë‹_0D{ÊEÀ{À¹ vçF {,}?(6nØ}éð|ß´@Ÿ'ø¦3X7`ë) 3Çe±·m-w¿ L‰•?|Lv?à[})Ó¾7|OOF¶þ­œ—WÅÏð&ð7‚ÙíÒ\cZöº±à1¥{F,˜ œ•r^êÞ˸Øß´ìk#¿_%§‡ÙîZ±ýûNÆþ] 8n´‚¨iÙÑ ²zíç=;r±Nøµ¥"÷ZÇs?v6Ë:÷f áJ¬ßxƨp¥ÅÛáÄ/åõëW§5´u¾ Pp&Ó°ç ¹ùÀiÅîyë×GÎÙ±Çox®-þ] <|;òš/˜–=Þs•ƒÜæ;aÀúSLW ïyÙ}áo®¼0è¬ûË^»7piÂòbº?†·…aÖÚ˵†º=ønÂû΋<¾¸/ÇeWøb$³,þÒÀ[‘ý½pqÊ:æ÷„Ÿu<Á`&×Q^‡ÿ]àJÏu®òwù#ABwdÿî üfKi†ÃΑû ëé¼^D­ÏÀ 7oz®Ó©3¾úšÑ£õÚI7a¸×þ?ÑM£ÏVš'½l>†Ï³¾ÁÑãfw>Rx`2…&Ó0»“{¨ÿ‹‘Áb*ÕY Ã+Ò(m‹ØSÆ^ç›–}g, CÐçß«¬»üˆ Óu±`Ñÿ#_{“Øû¾ä¹Î{±LÏu„ å~?·¦\Òí﹎“RZ}ü½°Á[Klv1 ‡¯»&ç‰þÓÀ Ǿžë¼ùNëÄ^÷6ðÏu^}Æ—MLË~Œ`”²xÉFÞºÛÿ-®?¶¯6-ûmàŽØ{6Äa·¡iÙ[‘hÖàEÏuúbË'Fî¿PO§µp¿|*\t’Nöµ§"÷štÊëµ yë¶É.†Ïzm!q½wáûSÇíÕùòÙÃó£çnßÈ®;¯}]úq Ë~°ì¶„e?*žÀª° ¸-Z\¬ï ooLrwo±eut›‘mÿ&Ïÿt¸ '~!‘2` À‚„õnPìZ—÷»‡O%\¨â¹Î½‘º!ÁXàq§z®ózB‹ÿþõP>‰Èæ¦eïŸ3})ºþØle„%4¯%|¯Íª<Þ!h•¿$çí¹H`ŒŠ^¨¼QyôñeÀ¯Â‡À­·AúØ êÑ;i-Ü!½%zÊLjFÎì>ü{Ǹ/tõØw4âݯœ`vž`Ö£´ø‚Y¢VźLv ú¥EÐk›–½W8*W5Åã*ÂW€Ï&çŒ÷­¬vå Æ]CÙÇ‘îq&d´¿ñ\çšXéÀVÕ\›uáá¹Îÿ˜–íRÞ¸ñÀ0ûô> ¿ÃJ‚jO&?å¶Õãn¤$[ÑŒZÌ-0Ó´ì´¡_}Ó²· ÿ—g Jç9 Ís^DúØÈГ3p?Þ*²õ¤‘Þ Rùñ5û®%Ùº‘ü:22öaž¸·£¼¥ùõñ{äþ «úÊd+ky¦ 23œç…?ÌÇæ‡'Ñ© ïÐsÞûùš„eSÂAƒ†ªÀÀxCq5A£ÈÏå¼íÌÀˆkie_µ­áQ‚ºú¤Ûcm;Nˆó“©žë| ì\úØ îy¦HMìož³ÿzÉÿ~ŽþëÃY‘YwÞ?¹Köû†1€…AäG±§>&¥5nxRz9Ì:vˆ<58iuš"2Vú°)ð‰°èxÃpÏ¿¼öëž0ÄìüÒ„¬û9àø„z{ƒ`ϸ‡r–(´',Ûˆ ¾zeìþ7=×é†õDûxoFöLlÃá…Œ„í¿Çz®ó»èE¡†|U@»A=#"Vœ"Õ¯0Ò[ɳµiFÞ¿•ŠƒÇ‘i×F6°GŠŠ=µ4ídž”V™–ýÇX@Ÿ´z®Ó±:sÓ²7&(ÆžJPŒÜ‘¾;˜¬4²CúÝG½ÌL™ Ä ¨É ^™Á2aY3CŸ‡¼½¹¿E ¶·3ÉEû+ zÅLÐõsyü¢QÐØËê—¦HMÊ®ÓGzhŸ4ÿz-®UüH)C¥Ànø9.v†$i¨×VÓ²³¥³€ýêý$ ´_¢|Ò™¸1œkdmÅËQÿ³õ3V‘t0òlÛs7ÆPñîÓ±`ûŸ#¼½åáøìI1-{‚Æ{ç™–}¿ç:ï"£JuèõÔ+ͤ–ØZì WêØK+ÚGè{Ä{Åf|cĪÂùŸ¿1Œ«Ü×´ìMV“`>5#˜¿F0HÉ#ýÜ«ºœŠlcAî¸/{®³´B07rù¤ío<†ÎFîÏÍã*tp˜ÁoC0T¯f$T@—ôô69XûUÌ“žkF·‘¾@!6zFðOìæ6<6'd8XχPäb㾄§Ÿ>í¹ÎžëìÞZ<×1Z-y·aZö:¤Ö=}°ç:N…RŒÉ£ÔµäüšÛ%e–Ô¦x­/Î^”tlcZöÆ£9 ­ç:÷×´ì9*rW@—<™n,Xûic³§d÷‰ý×ýüÜ)Ëç>¯؇ϗ(¯«} ~7ï-nŽiÙuý?dZöl )‹ÝËs—SÂ[yƒŒiÙÍ ØâEõç{®sm¥* Ïu|’ëË÷Ë™ñÍNùü+Ö¤SAd^þÝølЯGŽ—‹Ãé€uî%ªC_²õH¬+qŽ ç’ú¯5øÌ)C½–T™×¦ú¿ÅÌò\çÅ*‚ãß(m‘ý‚ÖÔËëøèÙ!aÙSžë¼–l«ÉÎ/ZcOÝá¹ÎÉU´/x+MË>‚`xU€ÓMËv=×¹-VrQËÏÁdAË º ^`Zv{qQ@—\§Â´…ú¯§¯`Èz®h£Ð‹o”MçV²I£üqj1ü²‚AFf&<}Ñ V™ÔR|SÓ²gy®³°N”'–cËÏ-ÑiZv Á÷ß$h@˜uaóIà†„§—·“¯ä¯@P<[,nÿ÷°´c›ØùéyÓ²o ³ø—T™~Îø˜ê«*®©³Â€ýdØßÿ·áoy«iÙ¿%४>áÃq®£/¼ÈûÏð7<8V}ÓÐe0=¡Î¼¤ÿ: Át˜²áž-ÐMGt4Ph˜_²Ñ*2ö’l}²‡0Pm{êaÏuþQÍÉ&|íòp¶/ÇžžKMgÙ‹LËhú+@iÙïSý b¥,߉òá^Ó¬Š^Ty®³Â´ìã †Œ^Œþ9¼Urç:ÏÔáÏ0!ì Ð\åû àýè¬k‘Ùà®1-{ǰôÉ ˜æÓ²/!˜†ö5Ó²û §B¸ÇL[»°x°Õ0fé·OÓ€y¦eÿÁsûu’®Õ¡¯I=¥Î<±5|±Eü0h:ª‹¦#ºè¹¢å« œ~V6f{ž–íIõëCËÎ!¹¸ýúj³¸ÈkOKxzÓ²Ç×[ëÞÈçÙH+þL æ' SëÌ󿀯2¸ê?ö\çûuúßyB¸?ßÄmVÚ±6>ÛŸ`bŸ¢c º$¾L0ÓÝb‚1:Â密¥6g[óÅ£~ž¢«MË^_­ÞÐe¸;¤÷_JV~yðõœßBÏ‚–Mz´Ü‡ÏÄ[` yüûê,þ~oßÝ­€ù*r¯¹¯É=¡áœOyÕvÕÁü²šŽî¢çò–ñÀyø†Áº%§#!¨Ç‹áË^Gò±ƒ·.A·žU±Ösƒ9ÉDæÉÞ—`R“è…qôÓîAéÄ +¨ÜµíTàÜÈz¨<žù?€¶Ø²SN´Ï™–½%ÁÄ2{ ÈÒù…ÀBÏuEÞv8¥}Ëÿùl/%ì×Á¡K“öo8€ÊtÓ²?KÐþag`[‚^/ÏK<×y8òÓ¶õ:A5Éø*Ï}½Àa”N[:ž`¼ûJÞ'¨ëJÅV¥Ã½¦–Âx®s1A?ðÏ„¿íÖa>!rÜ¿B0‹ÛßÂÿöÛ9½ª.ËüÜJ0ï8%µehÔVß-.•_9úkmÖŽn<à…ƒèã€9±PyðŒW'¿ï`zÓa^Žš‘W—*}&5¸ª~¿hŸ­Ùtõ4V²õ¤bøœCÅ­¸t =ÕŠËZþɇ;ði¨XTž¸,6|wÞ÷Ɉ©Ç“|¥Ï¤ÀTý~Ñ>[³©È}¬ö*Z¸¯¸¤…æc»hž×ÅŠK[šßâ­}#²ŠŒ>æd½&Þ¿6³»Šˆ( ËÔ©ÔW\<™æc;YqÉÄFw:>§`DêýHÕxR0N«#/[–R¿® ."R¹+¸—ò‹&G2ôÉ6Œ8X'­XÜOX–ú8uYŽÖð""¢€^G>®×¶â¢É4ßÉŠ‹'ï üŸv|Ö)™²:%@ÇgkÍÄãÁß Ò]!]DD½>õÝ2à.‚þ¾u§ùøNV\4ù|ö)¼9²Åƒº_¹T y™qYÓ^¯ŽôºÓ8·›Æ¹Ýo7ÎížÌ^«« ýÂÉŸÀç%Á¸¬ø¼˜­™™¸ŸUìî“U45°mÓ‘]—õ,˜¬ƒFDD½®³tçv_Ö8·{ à‚:úx°²bVíG_NjáxÅbøÒe]ÀÖMGvÖtd×K=W´ÐtT§ôúÍÒ‹½ï–‰4Îí>‰`$¬?ÔÕÍÔû3vÒ{åbøñ9¼é¨®ÉMGu½Ò³ èóÞtd—‘*¨ÛÚ(öðþSÀ}·LœÜGõ³_ _ÏêfŸþ´ÿuFißµ”÷%ts;³é˜®þÉNz´Ðt”¹ˆˆ2ôÕX˜­/nœÛ½!ðC‚ñ§G'¨çm¡^ö8a¸„õøp³SšŽé:­çÒ–þ‰^ÌEDÐ׈Œ½X¿ü‚`‚…«Gí fV´’bøÄúõ7€ñ9¨ù˜®N€¦y]4­@."¢€¾†õâ߯¹ÝËçvìÜS³ >˜e©¯éÏÖ—ásbó¼®Í›çu=…ú—‹ˆ( ‘Fs‹€ýÂÀþîˆo8«KYžì¼l]Æ/m›íº‚‰^šç)#Q@CÙz¬áÜ"`Sଠ샮;O|Ï;7×9ßè+>­`."¢€>¦ƒ{˜­¯jœÛýc`à¦IΓfUÍ[—,{ hÅgNóqO­¸x2ÍÇ©?¹ˆˆºôuèo ÿaãÜ=G†o+-ÔýêÇb8ƒ¶h>¾óÉâ3 æ"" è’ØÃû³ý°¤è íêG&]©د&7ŸÐùÓæ«ƒÞ|¼¹ˆH-i`™Õ?À÷wãún™økàp`Ý¡õb²nPì8îF|Ѓv`~ó‰Oè—Q†OC¼æÛIDAT.Cé¿~2°5Ã1Œ¬OÙ`1‘lý#  Ÿ}šOì|bÅšDEDD]†#Kþ}¯qn÷ÀàÉÁñ„Œ=ìïãsvóIë6ŸÔé}Í'ªx]DD]†=¸‡ çîfï {ðø·À¶Nî<`ù¯'Ó|’¹ˆˆºŒxƬlœÛý`CàZàê;,öšpÊÒCñyoù¯¦0áds‘z¢Fqc#[ÿ8¸ï–‰?®Ú—Œ‹p£ÿ"Ï 8÷N˜¿´`Â)KµSED”¡Ë(gë4Îí~©qn÷l‚bøû)#þmàF`g„ï/½ £Êâzy‘ñ‰£–Ÿ7E;LDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD¤æþЕǡY>IEND®B`‚amazon-ecr-credential-helper-0.12.0/docs/packaging.md000066400000000000000000000026001517311355300224130ustar00rootroot00000000000000# Packaging Amazon ECR Credential Helper Amazon maintains packages for the Amazon ECR Credential Helper on the following operating systems: * Amazon Linux 2 ([source](https://github.com/awslabs/amazon-ecr-credential-helper/tree/amazonlinux), [packaging documentation](https://github.com/awslabs/amazon-ecr-credential-helper/blob/amazonlinux/docs/packaging-amazon-linux.md)) * Debian ([source](https://github.com/awslabs/amazon-ecr-credential-helper/tree/debian)) (note: the packages in derivatives of Debian like Devuan, Ubuntu 19.04 Disco Dingo, and PureOS are derived from the Debian package with no additional modifications) There are community-maintained packages for the Amazon ECR Credential Helper on the following operating systems: * Mac OS X (with the Homebrew package manager) ([source](https://github.com/Homebrew/homebrew-core/blob/master/Formula/d/docker-credential-helper-ecr.rb)) * NixOS (and the Nix package manager) ([source](https://github.com/NixOS/nixpkgs/blob/master/pkgs/by-name/am/amazon-ecr-credential-helper/package.nix)) * Arch Linux (in the Arch User Repository) ([source](https://aur.archlinux.org/packages/amazon-ecr-credential-helper)) If you are interested in packaging the Amazon ECR Credential Helper, please get in touch! We can list your community-maintained packaging here and include installation instructions in our [README.md](../README.md). amazon-ecr-credential-helper-0.12.0/ecr-login/000077500000000000000000000000001517311355300210765ustar00rootroot00000000000000amazon-ecr-credential-helper-0.12.0/ecr-login/api/000077500000000000000000000000001517311355300216475ustar00rootroot00000000000000amazon-ecr-credential-helper-0.12.0/ecr-login/api/client.go000066400000000000000000000300731517311355300234570ustar00rootroot00000000000000// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package api import ( "context" "encoding/base64" "errors" "fmt" "net/url" "regexp" "strings" "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ecr" "github.com/aws/aws-sdk-go-v2/service/ecrpublic" "github.com/sirupsen/logrus" "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cache" "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/config" ) const ( proxyEndpointScheme = "https://" programName = "docker-credential-ecr-login" ecrPublicName = "public.ecr.aws" ecrPublicDualStackName = "ecr-public.aws.com" ) var ecrPattern = regexp.MustCompile(`^(\d{12})\.dkr[\.\-]ecr(\-fips)?\.([a-zA-Z0-9][a-zA-Z0-9-_]*)\.(amazonaws\.(?:com(?:\.cn)?|eu)|on\.(?:aws|amazonwebservices\.com\.cn)|sc2s\.sgov\.gov|c2s\.ic\.gov|cloud\.adc-e\.uk|csp\.hci\.ic\.gov)$`) type Service string const ( ServiceECR Service = "ecr" ServiceECRPublic Service = "ecr-public" ) // Registry in ECR type Registry struct { Service Service ID string FIPS bool Region string Name string } // ExtractRegistry returns the ECR registry behind a given service endpoint func ExtractRegistry(input string) (*Registry, error) { input = strings.TrimPrefix(input, proxyEndpointScheme) serverURL, err := url.Parse(proxyEndpointScheme + input) if err != nil { return nil, err } if serverURL.Hostname() == ecrPublicName || serverURL.Hostname() == ecrPublicDualStackName { return &Registry{ Service: ServiceECRPublic, Name: serverURL.Hostname(), }, nil } matches := ecrPattern.FindStringSubmatch(serverURL.Hostname()) if len(matches) == 0 { return nil, fmt.Errorf(programName + " can only be used with Amazon Elastic Container Registry.") } else if len(matches) < 3 { return nil, fmt.Errorf("%q is not a valid repository URI for Amazon Elastic Container Registry.", input) } return &Registry{ Service: ServiceECR, ID: matches[1], FIPS: matches[2] == "-fips", Region: matches[3], }, nil } // Client used for calling ECR service type Client interface { GetCredentials(serverURL string) (*Auth, error) GetCredentialsByRegistryID(registryID string) (*Auth, error) ListCredentials() ([]*Auth, error) } // Auth credentials returned by ECR service to allow docker login type Auth struct { ProxyEndpoint string Username string Password string } type defaultClient struct { ecrClient ECRAPI ecrPublicClient ECRPublicAPI credentialCache cache.CredentialsCache } type ECRAPI interface { GetAuthorizationToken(context.Context, *ecr.GetAuthorizationTokenInput, ...func(*ecr.Options)) (*ecr.GetAuthorizationTokenOutput, error) } type ECRPublicAPI interface { GetAuthorizationToken(context.Context, *ecrpublic.GetAuthorizationTokenInput, ...func(*ecrpublic.Options)) (*ecrpublic.GetAuthorizationTokenOutput, error) } // sanitizeURLError checks if an error contains a url.Error and returns // a sanitized version with sensitive URL information redacted. func sanitizeURLError(err error) error { if err == nil { return nil } var urlErr *url.Error if errors.As(err, &urlErr) { return &url.Error{ Op: urlErr.Op, URL: config.RedactURL(urlErr.URL), Err: urlErr.Err, } } return err } // ecrClientWrapper wraps an ECRAPI and sanitizes url.Error from responses. type ecrClientWrapper struct { client ECRAPI } // NewECRClientWrapper creates a new ECRAPI wrapper that sanitizes sensitive // information from url.Error before returning errors. func NewECRClientWrapper(client ECRAPI) ECRAPI { return &ecrClientWrapper{client: client} } func (w *ecrClientWrapper) GetAuthorizationToken(ctx context.Context, input *ecr.GetAuthorizationTokenInput, opts ...func(*ecr.Options)) (*ecr.GetAuthorizationTokenOutput, error) { output, err := w.client.GetAuthorizationToken(ctx, input, opts...) return output, sanitizeURLError(err) } // ecrPublicClientWrapper wraps an ECRPublicAPI and sanitizes url.Error from responses. type ecrPublicClientWrapper struct { client ECRPublicAPI } // NewECRPublicClientWrapper creates a new ECRPublicAPI wrapper that sanitizes // sensitive information from url.Error before returning errors. func NewECRPublicClientWrapper(client ECRPublicAPI) ECRPublicAPI { return &ecrPublicClientWrapper{client: client} } func (w *ecrPublicClientWrapper) GetAuthorizationToken(ctx context.Context, input *ecrpublic.GetAuthorizationTokenInput, opts ...func(*ecrpublic.Options)) (*ecrpublic.GetAuthorizationTokenOutput, error) { output, err := w.client.GetAuthorizationToken(ctx, input, opts...) return output, sanitizeURLError(err) } // GetCredentials returns username, password, and proxyEndpoint func (c *defaultClient) GetCredentials(serverURL string) (*Auth, error) { registry, err := ExtractRegistry(serverURL) if err != nil { return nil, err } logrus. WithField("service", registry.Service). WithField("registry", registry.ID). WithField("region", registry.Region). WithField("serverURL", serverURL). WithField("name", registry.Name). Debug("Retrieving credentials") switch registry.Service { case ServiceECR: return c.GetCredentialsByRegistryID(registry.ID) case ServiceECRPublic: return c.GetPublicCredentials(registry.Name) } return nil, fmt.Errorf("unknown service %q", registry.Service) } // GetCredentialsByRegistryID returns username, password, and proxyEndpoint func (c *defaultClient) GetCredentialsByRegistryID(registryID string) (*Auth, error) { cachedEntry := c.credentialCache.Get(registryID) if cachedEntry != nil { if cachedEntry.IsValid(time.Now()) { logrus.WithField("registry", registryID).Debug("Using cached token") return extractToken(cachedEntry.AuthorizationToken, cachedEntry.ProxyEndpoint) } logrus. WithField("requestedAt", cachedEntry.RequestedAt). WithField("expiresAt", cachedEntry.ExpiresAt). Debug("Cached token is no longer valid") } auth, err := c.getAuthorizationToken(registryID) // if we have a cached token, fall back to avoid failing the request. This may result an expired token // being returned, but if there is a 500 or timeout from the service side, we'd like to attempt to re-use an // old token. We invalidate tokens prior to their expiration date to help mitigate this scenario. if err != nil && cachedEntry != nil { logrus.WithError(err).Info("Got error fetching authorization token. Falling back to cached token.") return extractToken(cachedEntry.AuthorizationToken, cachedEntry.ProxyEndpoint) } return auth, err } func (c *defaultClient) GetPublicCredentials(registry string) (*Auth, error) { cachedEntry := c.credentialCache.GetPublic() if cachedEntry != nil { if cachedEntry.IsValid(time.Now()) { logrus.WithField("registry", registry).Debug("Using cached token") return extractToken(cachedEntry.AuthorizationToken, cachedEntry.ProxyEndpoint) } logrus. WithField("requestedAt", cachedEntry.RequestedAt). WithField("expiresAt", cachedEntry.ExpiresAt). Debug("Cached token is no longer valid") } auth, err := c.getPublicAuthorizationToken(registry) // if we have a cached token, fall back to avoid failing the request. This may result an expired token // being returned, but if there is a 500 or timeout from the service side, we'd like to attempt to re-use an // old token. We invalidate tokens prior to their expiration date to help mitigate this scenario. if err != nil && cachedEntry != nil { logrus.WithError(err).Info("Got error fetching authorization token. Falling back to cached token.") return extractToken(cachedEntry.AuthorizationToken, cachedEntry.ProxyEndpoint) } return auth, err } func (c *defaultClient) ListCredentials() ([]*Auth, error) { // prime the cache with default authorization tokens _, err := c.GetCredentialsByRegistryID("") if err != nil { logrus.WithError(err).Debug("couldn't get authorization token for default registry") } _, err = c.GetPublicCredentials(ecrPublicName) if err != nil { logrus.WithError(err).Debug("couldn't get authorization token for public registry") } auths := make([]*Auth, 0) for _, authEntry := range c.credentialCache.List() { auth, err := extractToken(authEntry.AuthorizationToken, authEntry.ProxyEndpoint) if err != nil { logrus.WithError(err).Debug("Could not extract token") } else { auths = append(auths, auth) } } return auths, nil } func (c *defaultClient) getAuthorizationToken(registryID string) (*Auth, error) { var input *ecr.GetAuthorizationTokenInput if registryID == "" { logrus.Debug("Calling ECR.GetAuthorizationToken for default registry") input = &ecr.GetAuthorizationTokenInput{} } else { logrus.WithField("registry", registryID).Debug("Calling ECR.GetAuthorizationToken") input = &ecr.GetAuthorizationTokenInput{ RegistryIds: []string{registryID}, } } output, err := c.ecrClient.GetAuthorizationToken(context.TODO(), input) if err != nil || output == nil { if err == nil { if registryID == "" { err = fmt.Errorf("missing AuthorizationData in ECR response for default registry") } else { err = fmt.Errorf("missing AuthorizationData in ECR response for %s", registryID) } } return nil, fmt.Errorf("ecr: Failed to get authorization token: %w", err) } for _, authData := range output.AuthorizationData { if authData.ProxyEndpoint != nil && authData.AuthorizationToken != nil { authEntry := cache.AuthEntry{ AuthorizationToken: aws.ToString(authData.AuthorizationToken), RequestedAt: time.Now(), ExpiresAt: aws.ToTime(authData.ExpiresAt), ProxyEndpoint: aws.ToString(authData.ProxyEndpoint), Service: cache.ServiceECR, } registry, err := ExtractRegistry(authEntry.ProxyEndpoint) if err != nil { return nil, fmt.Errorf("Invalid ProxyEndpoint returned by ECR: %s", authEntry.ProxyEndpoint) } auth, err := extractToken(authEntry.AuthorizationToken, authEntry.ProxyEndpoint) if err != nil { return nil, err } c.credentialCache.Set(registry.ID, &authEntry) return auth, nil } } if registryID == "" { return nil, fmt.Errorf("No AuthorizationToken found for default registry") } return nil, fmt.Errorf("No AuthorizationToken found for %s", registryID) } func (c *defaultClient) getPublicAuthorizationToken(registry string) (*Auth, error) { var input *ecrpublic.GetAuthorizationTokenInput output, err := c.ecrPublicClient.GetAuthorizationToken(context.TODO(), input) if err != nil { return nil, fmt.Errorf("ecr: failed to get authorization token: %w", err) } if output == nil || output.AuthorizationData == nil { return nil, fmt.Errorf("ecr: missing AuthorizationData in ECR Public response") } endpoint := ecrPublicEndpoint(registry) authData := output.AuthorizationData token, err := extractToken(aws.ToString(authData.AuthorizationToken), endpoint) if err != nil { return nil, err } authEntry := cache.AuthEntry{ AuthorizationToken: aws.ToString(authData.AuthorizationToken), RequestedAt: time.Now(), ExpiresAt: aws.ToTime(authData.ExpiresAt), ProxyEndpoint: endpoint, Service: cache.ServiceECRPublic, } c.credentialCache.Set(registry, &authEntry) return token, nil } func extractToken(token string, proxyEndpoint string) (*Auth, error) { decodedToken, err := base64.StdEncoding.DecodeString(token) if err != nil { return nil, fmt.Errorf("invalid token: %w", err) } parts := strings.SplitN(string(decodedToken), ":", 2) if len(parts) < 2 { return nil, fmt.Errorf("invalid token: expected two parts, got %d", len(parts)) } return &Auth{ Username: parts[0], Password: parts[1], ProxyEndpoint: proxyEndpoint, }, nil } func ecrPublicEndpoint(registry string) string { return proxyEndpointScheme + registry } amazon-ecr-credential-helper-0.12.0/ecr-login/api/client_test.go000066400000000000000000000744521517311355300245270ustar00rootroot00000000000000// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package api import ( "context" "encoding/base64" "errors" "fmt" "net/url" "testing" "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ecr" ecrtypes "github.com/aws/aws-sdk-go-v2/service/ecr/types" "github.com/aws/aws-sdk-go-v2/service/ecrpublic" ecrpublictypes "github.com/aws/aws-sdk-go-v2/service/ecrpublic/types" mock_api "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/api/mocks" "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cache" mock_cache "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cache/mocks" "github.com/stretchr/testify/assert" ) const ( registryID = "123456789012" proxyEndpoint = "123456789012.dkr.ecr.us-east-1.amazonaws.com" expectedUsername = "username" expectedPassword = "password" ) func TestExtractRegistry(t *testing.T) { testCases := []struct { serverURL string registry *Registry hasError bool }{{ serverURL: "https://123456789012.dkr.ecr.us-east-1.amazonaws.com/v2/blah/blah", registry: &Registry{ ID: "123456789012", FIPS: false, Region: "us-east-1", Service: ServiceECR, }, hasError: false, }, { serverURL: "123456789012.dkr.ecr.us-west-2.amazonaws.com", registry: &Registry{ ID: "123456789012", FIPS: false, Region: "us-west-2", Service: ServiceECR, }, hasError: false, }, { // Dual-stack endpoint serverURL: "123456789012.dkr-ecr.us-west-2.on.aws", registry: &Registry{ ID: "123456789012", FIPS: false, Region: "us-west-2", Service: ServiceECR, }, hasError: false, }, { // Dual-stack FIPS endpoint serverURL: "123456789012.dkr-ecr-fips.us-west-2.on.aws", registry: &Registry{ ID: "123456789012", FIPS: true, Region: "us-west-2", Service: ServiceECR, }, hasError: false, }, { serverURL: "210987654321.dkr.ecr.cn-north-1.amazonaws.com.cn/foo", registry: &Registry{ ID: "210987654321", FIPS: false, Region: "cn-north-1", Service: ServiceECR, }, hasError: false, }, { // IPv6 CN serverURL: "210987654321.dkr.ecr.cn-north-1.on.amazonwebservices.com.cn", registry: &Registry{ ID: "210987654321", FIPS: false, Region: "cn-north-1", Service: ServiceECR, }, hasError: false, }, { serverURL: "210987654321.dkr.ecr.us-iso-east-1.c2s.ic.gov", registry: &Registry{ ID: "210987654321", FIPS: false, Region: "us-iso-east-1", Service: ServiceECR, }, hasError: false, }, { serverURL: "123456789012.dkr.ecr.us-isob-east-1.sc2s.sgov.gov", registry: &Registry{ ID: "123456789012", FIPS: false, Region: "us-isob-east-1", Service: ServiceECR, }, hasError: false, }, { serverURL: "123456789012.dkr.ecr.eu-isoe-west-1.cloud.adc-e.uk", registry: &Registry{ ID: "123456789012", FIPS: false, Region: "eu-isoe-west-1", Service: ServiceECR, }, hasError: false, }, { serverURL: "123456789012.dkr.ecr.us-isof-east-1.csp.hci.ic.gov", registry: &Registry{ ID: "123456789012", FIPS: false, Region: "us-isof-east-1", Service: ServiceECR, }, hasError: false, }, { // IPv6 GovCloud serverURL: "123456789012.dkr-ecr.us-gov-east-1.on.aws", registry: &Registry{ ID: "123456789012", FIPS: false, Region: "us-gov-east-1", Service: ServiceECR, }, hasError: false, }, { serverURL: "123456789012.dkr.ecr-fips.us-gov-west-1.amazonaws.com", registry: &Registry{ ID: "123456789012", FIPS: true, Region: "us-gov-west-1", Service: ServiceECR, }, hasError: false, }, { // IPv6 GovCloud FIPS serverURL: "123456789012.dkr-ecr-fips.us-gov-east-1.on.aws", registry: &Registry{ ID: "123456789012", FIPS: true, Region: "us-gov-east-1", Service: ServiceECR, }, hasError: false, }, { serverURL: "123456789012.dkr.ecr.eusc-de-east-1.amazonaws.eu", registry: &Registry{ ID: "123456789012", FIPS: false, Region: "eusc-de-east-1", Service: ServiceECR, }, hasError: false, }, { serverURL: "https://public.ecr.aws", registry: &Registry{ Service: ServiceECRPublic, Name: "public.ecr.aws", }, }, { serverURL: "https://ecr-public.aws.com", registry: &Registry{ Service: ServiceECRPublic, Name: "ecr-public.aws.com", }, }, { serverURL: "public.ecr.aws", registry: &Registry{ Service: ServiceECRPublic, Name: "public.ecr.aws", }, }, { serverURL: "ecr-public.aws.com", registry: &Registry{ Service: ServiceECRPublic, Name: "ecr-public.aws.com", }, }, { serverURL: "https://public.ecr.aws/amazonlinux", registry: &Registry{ Service: ServiceECRPublic, Name: "public.ecr.aws", }, }, { serverURL: "https://ecr-public.aws.com/amazonlinux", registry: &Registry{ Service: ServiceECRPublic, Name: "ecr-public.aws.com", }, }, { serverURL: ".dkr.ecr.not-real.amazonaws.com", hasError: true, }, { serverURL: "not.ecr.io", hasError: true, }, { serverURL: "https://123456789012.dkr.ecr.us-west-2.amazonaws.com.fake.example.com/image:latest", hasError: true, }, { serverURL: "123456789012.dkr.ecr.us-west-2.amazonaws.com.fake.example.com", hasError: true, }, { serverURL: "123456789012.dkr.ecr-fips.us-gov-west-1.amazonaws.com.fake.example.com", hasError: true, }, { serverURL: "210987654321.dkr.ecr.cn-north-1.amazonaws.com.cn.fake.example.com.cn", hasError: true, }, { // China region IPv6 endpoints are "on.amazonwebservices.com.cn" serverURL: "210987654321.dkr.ecr.cn-north-1.on.aws.com.cn", hasError: true, }, { serverURL: "https://public.ecr.aws.fake.example.com", hasError: true, }, { serverURL: "public.ecr.aws.fake.example.com", hasError: true, }} for _, tc := range testCases { t.Run(tc.serverURL, func(t *testing.T) { registry, err := ExtractRegistry(tc.serverURL) if !tc.hasError { assert.NoError(t, err, "No error expected") assert.EqualValues(t, tc.registry, registry, "Registry should be equal") } else { assert.Error(t, err, "Expected error") } }) } } func TestGetAuthConfigSuccess(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ ecrClient: ecrClient, credentialCache: credentialCache, } testProxyEndpoint := proxyEndpointScheme + proxyEndpoint authorizationToken := base64.StdEncoding.EncodeToString([]byte(expectedUsername + ":" + expectedPassword)) expiresAt := time.Now().Add(12 * time.Hour) ecrClient.GetAuthorizationTokenFn = func(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { assert.NotNil(t, input, "GetAuthorizationToken input") assert.Len(t, input.RegistryIds, 1, "GetAuthorizationToken registry IDs len") return &ecr.GetAuthorizationTokenOutput{ AuthorizationData: []ecrtypes.AuthorizationData{{ ProxyEndpoint: aws.String(testProxyEndpoint), ExpiresAt: aws.Time(expiresAt), AuthorizationToken: aws.String(authorizationToken), }}, }, nil } authEntry := &cache.AuthEntry{ ProxyEndpoint: testProxyEndpoint, RequestedAt: time.Now(), ExpiresAt: expiresAt, AuthorizationToken: authorizationToken, } credentialCache.GetFn = func(_ string) *cache.AuthEntry { return nil } credentialCache.SetFn = func(_ string, actual *cache.AuthEntry) { compareAuthEntry(t, actual, authEntry) } auth, err := client.GetCredentials(proxyEndpoint) assert.Nil(t, err) assert.Equal(t, auth.Username, expectedUsername) assert.Equal(t, auth.Password, expectedPassword) assert.Equal(t, auth.ProxyEndpoint, testProxyEndpoint) } func TestGetAuthConfigNoMatchAuthorizationToken(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ ecrClient: ecrClient, credentialCache: credentialCache, } ecrClient.GetAuthorizationTokenFn = func(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { assert.NotNil(t, input, "GetAuthorizationToken input") assert.Len(t, input.RegistryIds, 1, "GetAuthorizationToken registry IDs len") return &ecr.GetAuthorizationTokenOutput{ AuthorizationData: []ecrtypes.AuthorizationData{{ ProxyEndpoint: aws.String(proxyEndpointScheme + "notproxy"), AuthorizationToken: aws.String(base64.StdEncoding.EncodeToString([]byte(expectedUsername + ":" + expectedPassword))), }}, }, nil } credentialCache.GetFn = func(_ string) *cache.AuthEntry { return nil } auth, err := client.GetCredentials(proxyEndpoint) assert.NotNil(t, err) assert.Nil(t, auth) } func TestGetAuthConfigGetCacheSuccess(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ ecrClient: ecrClient, credentialCache: credentialCache, } testProxyEndpoint := proxyEndpointScheme + proxyEndpoint authorizationToken := base64.StdEncoding.EncodeToString([]byte(expectedUsername + ":" + expectedPassword)) expiresAt := time.Now().Add(12 * time.Hour) authEntry := &cache.AuthEntry{ ProxyEndpoint: testProxyEndpoint, ExpiresAt: expiresAt, RequestedAt: time.Now(), AuthorizationToken: authorizationToken, } credentialCache.GetFn = func(r string) *cache.AuthEntry { assert.Equal(t, registryID, r, "get from cache") return authEntry } auth, err := client.GetCredentials(proxyEndpoint) assert.Nil(t, err) assert.Equal(t, auth.Username, expectedUsername) assert.Equal(t, auth.Password, expectedPassword) assert.Equal(t, auth.ProxyEndpoint, testProxyEndpoint) } func TestGetAuthConfigSuccessInvalidCacheHit(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ ecrClient: ecrClient, credentialCache: credentialCache, } testProxyEndpoint := proxyEndpointScheme + proxyEndpoint authorizationToken := base64.StdEncoding.EncodeToString([]byte(expectedUsername + ":" + expectedPassword)) expiresAt := time.Now().Add(12 * time.Hour) ecrClient.GetAuthorizationTokenFn = func(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { assert.NotNil(t, input, "GetAuthorizationToken input") assert.Len(t, input.RegistryIds, 1, "GetAuthorizationToken registry IDs len") return &ecr.GetAuthorizationTokenOutput{ AuthorizationData: []ecrtypes.AuthorizationData{{ ProxyEndpoint: aws.String(testProxyEndpoint), ExpiresAt: aws.Time(expiresAt), AuthorizationToken: aws.String(authorizationToken), }}, }, nil } expiredAuthEntry := &cache.AuthEntry{ ProxyEndpoint: testProxyEndpoint, RequestedAt: time.Now().Add(-12 * time.Hour), ExpiresAt: time.Now().Add(-6 * time.Hour), AuthorizationToken: authorizationToken, } authEntry := &cache.AuthEntry{ ProxyEndpoint: testProxyEndpoint, RequestedAt: time.Now(), ExpiresAt: expiresAt, AuthorizationToken: authorizationToken, } credentialCache.GetFn = func(r string) *cache.AuthEntry { assert.Equal(t, registryID, r, "get from cache") return expiredAuthEntry } credentialCache.SetFn = func(_ string, actual *cache.AuthEntry) { compareAuthEntry(t, actual, authEntry) } auth, err := client.GetCredentials(proxyEndpoint) assert.Nil(t, err) assert.Equal(t, auth.Username, expectedUsername) assert.Equal(t, auth.Password, expectedPassword) assert.Equal(t, auth.ProxyEndpoint, testProxyEndpoint) } func TestGetAuthConfigBadBase64(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ ecrClient: ecrClient, credentialCache: credentialCache, } ecrClient.GetAuthorizationTokenFn = func(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { assert.NotNil(t, input, "GetAuthorizationToken input") assert.Len(t, input.RegistryIds, 1, "GetAuthorizationToken registry IDs len") return &ecr.GetAuthorizationTokenOutput{ AuthorizationData: []ecrtypes.AuthorizationData{{ ProxyEndpoint: aws.String(proxyEndpointScheme + proxyEndpoint), AuthorizationToken: aws.String(expectedUsername + ":" + expectedPassword), }}, }, nil } credentialCache.GetFn = func(_ string) *cache.AuthEntry { return nil } auth, err := client.GetCredentials(proxyEndpoint) assert.NotNil(t, err) t.Log(err) assert.Nil(t, auth) } func TestGetAuthConfigMissingResponse(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ ecrClient: ecrClient, credentialCache: credentialCache, } ecrClient.GetAuthorizationTokenFn = func(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { assert.NotNil(t, input, "GetAuthorizationToken input") assert.Len(t, input.RegistryIds, 1, "GetAuthorizationToken registry IDs len") return nil, nil } credentialCache.GetFn = func(_ string) *cache.AuthEntry { return nil } auth, err := client.GetCredentials(proxyEndpoint) assert.NotNil(t, err) t.Log(err) assert.Nil(t, auth) } func TestGetAuthConfigECRError(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ ecrClient: ecrClient, credentialCache: credentialCache, } ecrClient.GetAuthorizationTokenFn = func(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { assert.NotNil(t, input, "GetAuthorizationToken input") assert.Len(t, input.RegistryIds, 1, "GetAuthorizationToken registry IDs len") return nil, errors.New("test error") } credentialCache.GetFn = func(_ string) *cache.AuthEntry { return nil } auth, err := client.GetCredentials(proxyEndpoint) assert.NotNil(t, err) t.Log(err) assert.Nil(t, auth) } func TestGetAuthConfigSuccessInvalidCacheHitFallback(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ ecrClient: ecrClient, credentialCache: credentialCache, } testProxyEndpoint := proxyEndpointScheme + proxyEndpoint authorizationToken := base64.StdEncoding.EncodeToString([]byte(expectedUsername + ":" + expectedPassword)) ecrClient.GetAuthorizationTokenFn = func(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { assert.NotNil(t, input, "GetAuthorizationToken input") assert.Len(t, input.RegistryIds, 1, "GetAuthorizationToken registry IDs len") return nil, errors.New("service error") } expiredAuthEntry := &cache.AuthEntry{ ProxyEndpoint: testProxyEndpoint, RequestedAt: time.Now().Add(-12 * time.Hour), ExpiresAt: time.Now().Add(-6 * time.Hour), AuthorizationToken: authorizationToken, } credentialCache.GetFn = func(r string) *cache.AuthEntry { assert.Equal(t, registryID, r, "get from cache") return expiredAuthEntry } auth, err := client.GetCredentials(proxyEndpoint) assert.Nil(t, err) assert.Equal(t, auth.Username, expectedUsername) assert.Equal(t, auth.Password, expectedPassword) assert.Equal(t, auth.ProxyEndpoint, testProxyEndpoint) } func TestListCredentialsSuccess(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} ecrPublicClient := &mock_api.MockECRPublicAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ credentialCache: credentialCache, ecrClient: ecrClient, ecrPublicClient: ecrPublicClient, } testProxyEndpoint := proxyEndpointScheme + proxyEndpoint authorizationToken := base64.StdEncoding.EncodeToString([]byte(expectedUsername + ":" + expectedPassword)) expiresAt := time.Now().Add(12 * time.Hour) authEntry := &cache.AuthEntry{ ProxyEndpoint: testProxyEndpoint, RequestedAt: time.Now(), ExpiresAt: expiresAt, AuthorizationToken: authorizationToken, } authEntries := []*cache.AuthEntry{authEntry} ecrClient.GetAuthorizationTokenFn = func(_ *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { return nil, errors.New("test error") } ecrPublicClient.GetAuthorizationTokenFn = func(_ *ecrpublic.GetAuthorizationTokenInput) (*ecrpublic.GetAuthorizationTokenOutput, error) { return nil, errors.New("test error") } credentialCache.GetFn = func(_ string) *cache.AuthEntry { return nil } credentialCache.GetPublicFn = func() *cache.AuthEntry { return nil } credentialCache.ListFn = func() []*cache.AuthEntry { return authEntries } auths, err := client.ListCredentials() assert.NoError(t, err) assert.NotNil(t, auths) assert.Len(t, auths, 1) auth := auths[0] assert.Equal(t, auth.Username, expectedUsername) assert.Equal(t, auth.Password, expectedPassword) assert.Equal(t, auth.ProxyEndpoint, testProxyEndpoint) } func TestListCredentialsCached(t *testing.T) { credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ credentialCache: credentialCache, } testProxyEndpoint := proxyEndpointScheme + proxyEndpoint authorizationToken := base64.StdEncoding.EncodeToString([]byte(expectedUsername + ":" + expectedPassword)) expiresAt := time.Now().Add(12 * time.Hour) authEntry1 := &cache.AuthEntry{ ProxyEndpoint: testProxyEndpoint, RequestedAt: time.Now(), ExpiresAt: expiresAt, AuthorizationToken: authorizationToken, Service: cache.ServiceECR, } authEntry2 := &cache.AuthEntry{ ProxyEndpoint: testProxyEndpoint, RequestedAt: time.Now(), ExpiresAt: expiresAt, AuthorizationToken: authorizationToken, Service: cache.ServiceECRPublic, } authEntries := []*cache.AuthEntry{authEntry1, authEntry2} credentialCache.GetFn = func(_ string) *cache.AuthEntry { return authEntry1 } credentialCache.GetPublicFn = func() *cache.AuthEntry { return authEntry2 } credentialCache.ListFn = func() []*cache.AuthEntry { return authEntries } auths, err := client.ListCredentials() assert.NoError(t, err) assert.NotNil(t, auths) assert.Len(t, auths, 2) assert.Equal(t, auths[0].Username, expectedUsername) assert.Equal(t, auths[0].Password, expectedPassword) assert.Equal(t, auths[0].ProxyEndpoint, testProxyEndpoint) assert.Equal(t, auths[1].Username, expectedUsername) assert.Equal(t, auths[1].Password, expectedPassword) assert.Equal(t, auths[1].ProxyEndpoint, testProxyEndpoint) } func TestListCredentialsEmpty(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} ecrPublicClient := &mock_api.MockECRPublicAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ credentialCache: credentialCache, ecrClient: ecrClient, ecrPublicClient: ecrPublicClient, } testProxyEndpoint := proxyEndpointScheme + proxyEndpoint authorizationToken := base64.StdEncoding.EncodeToString([]byte(expectedUsername + ":" + expectedPassword)) expiresAt := time.Now().Add(12 * time.Hour) authEntry1 := &cache.AuthEntry{ ProxyEndpoint: testProxyEndpoint, RequestedAt: time.Now(), ExpiresAt: expiresAt, AuthorizationToken: authorizationToken, Service: cache.ServiceECR, } authEntry2 := &cache.AuthEntry{ ProxyEndpoint: ecrPublicEndpoint(ecrPublicName), RequestedAt: time.Now(), ExpiresAt: expiresAt, AuthorizationToken: authorizationToken, Service: cache.ServiceECRPublic, } authEntries := []*cache.AuthEntry{authEntry1, authEntry2} ecrClient.GetAuthorizationTokenFn = func(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { assert.NotNil(t, input, "GetAuthorizationToken input") assert.Len(t, input.RegistryIds, 0, "GetAuthorizationToken registry IDs len") return &ecr.GetAuthorizationTokenOutput{ AuthorizationData: []ecrtypes.AuthorizationData{{ ProxyEndpoint: aws.String(testProxyEndpoint), ExpiresAt: aws.Time(expiresAt), AuthorizationToken: aws.String(authorizationToken), }}, }, nil } ecrPublicClient.GetAuthorizationTokenFn = func(*ecrpublic.GetAuthorizationTokenInput) (*ecrpublic.GetAuthorizationTokenOutput, error) { return &ecrpublic.GetAuthorizationTokenOutput{ AuthorizationData: &ecrpublictypes.AuthorizationData{ ExpiresAt: aws.Time(expiresAt), AuthorizationToken: aws.String(authorizationToken), }, }, nil } credentialCache.GetFn = func(_ string) *cache.AuthEntry { return nil } credentialCache.GetPublicFn = func() *cache.AuthEntry { return nil } credentialCache.ListFn = func() []*cache.AuthEntry { return authEntries } setCallCount := 0 credentialCache.SetFn = func(_ string, _ *cache.AuthEntry) { setCallCount++ } auths, err := client.ListCredentials() assert.NoError(t, err) assert.NotNil(t, auths) assert.Len(t, auths, 2) assert.Equal(t, 2, setCallCount) assert.Equal(t, auths[0].Username, expectedUsername) assert.Equal(t, auths[0].Password, expectedPassword) assert.Equal(t, auths[0].ProxyEndpoint, testProxyEndpoint) assert.Equal(t, auths[1].Username, expectedUsername) assert.Equal(t, auths[1].Password, expectedPassword) assert.Equal(t, auths[1].ProxyEndpoint, ecrPublicEndpoint(ecrPublicName)) } func TestListCredentialsBadBase64AuthToken(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} ecrPublicClient := &mock_api.MockECRPublicAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ credentialCache: credentialCache, ecrClient: ecrClient, ecrPublicClient: ecrPublicClient, } testProxyEndpoint := proxyEndpointScheme + proxyEndpoint expiresAt := time.Now().Add(12 * time.Hour) emptyCache := []*cache.AuthEntry{} credentialCache.ListFn = func() []*cache.AuthEntry { return emptyCache } credentialCache.GetFn = func(_ string) *cache.AuthEntry { return nil } credentialCache.GetPublicFn = func() *cache.AuthEntry { return nil } ecrClient.GetAuthorizationTokenFn = func(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { assert.NotNil(t, input, "GetAuthorizationToken input") assert.Len(t, input.RegistryIds, 0, "GetAuthorizationToken registry IDs len") return &ecr.GetAuthorizationTokenOutput{ AuthorizationData: []ecrtypes.AuthorizationData{{ ProxyEndpoint: aws.String(testProxyEndpoint), ExpiresAt: aws.Time(expiresAt), AuthorizationToken: aws.String("invalid:token"), }}, }, nil } ecrPublicClient.GetAuthorizationTokenFn = func(_ *ecrpublic.GetAuthorizationTokenInput) (*ecrpublic.GetAuthorizationTokenOutput, error) { return nil, errors.New("test error") } auths, err := client.ListCredentials() assert.NoError(t, err) assert.NotNil(t, auths) assert.Empty(t, auths) } func TestListCredentialsInvalidAuthToken(t *testing.T) { ecrClient := &mock_api.MockECRAPI{} ecrPublicClient := &mock_api.MockECRPublicAPI{} credentialCache := &mock_cache.MockCredentialsCache{} client := &defaultClient{ credentialCache: credentialCache, ecrClient: ecrClient, ecrPublicClient: ecrPublicClient, } testProxyEndpoint := proxyEndpointScheme + proxyEndpoint expiresAt := time.Now().Add(12 * time.Hour) emptyCache := []*cache.AuthEntry{} credentialCache.ListFn = func() []*cache.AuthEntry { return emptyCache } credentialCache.GetFn = func(_ string) *cache.AuthEntry { return nil } credentialCache.GetPublicFn = func() *cache.AuthEntry { return nil } ecrClient.GetAuthorizationTokenFn = func(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { assert.NotNil(t, input, "GetAuthorizationToken input") assert.Len(t, input.RegistryIds, 0, "GetAuthorizationToken registry IDs len") return &ecr.GetAuthorizationTokenOutput{ AuthorizationData: []ecrtypes.AuthorizationData{{ ProxyEndpoint: aws.String(testProxyEndpoint), ExpiresAt: aws.Time(expiresAt), AuthorizationToken: aws.String("invalidtoken"), }}, }, nil } ecrPublicClient.GetAuthorizationTokenFn = func(_ *ecrpublic.GetAuthorizationTokenInput) (*ecrpublic.GetAuthorizationTokenOutput, error) { return nil, errors.New("test error") } auths, err := client.ListCredentials() assert.NoError(t, err) assert.NotNil(t, auths) assert.Empty(t, auths) } func compareAuthEntry(t *testing.T, actual *cache.AuthEntry, expected *cache.AuthEntry) { assert.NotNil(t, actual) assert.Equal(t, expected.AuthorizationToken, actual.AuthorizationToken) assert.Equal(t, expected.ProxyEndpoint, actual.ProxyEndpoint) assert.Equal(t, expected.ExpiresAt, actual.ExpiresAt) assert.WithinDuration(t, expected.RequestedAt, actual.RequestedAt, 5*time.Second) } func TestSanitizeURLError(t *testing.T) { sensitiveURL := "https://ecr.amazonaws.com/v2/auth?token=secret-token-12345&session=abc123" expectedURL := "https://ecr.amazonaws.com/v2/auth?session=redacted&token=redacted" testCases := []struct { name string inputErr error expectNil bool checkFunc func(t *testing.T, result error) }{ { name: "nil error returns nil", inputErr: nil, expectNil: true, }, { name: "non-url.Error passes through unchanged", inputErr: errors.New("some other error"), checkFunc: func(t *testing.T, result error) { assert.Equal(t, "some other error", result.Error()) }, }, { name: "url.Error URL is sanitized", inputErr: &url.Error{ Op: "Get", URL: sensitiveURL, Err: errors.New("connection refused"), }, checkFunc: func(t *testing.T, result error) { var urlErr *url.Error assert.True(t, errors.As(result, &urlErr)) assert.Equal(t, "Get", urlErr.Op) assert.Equal(t, expectedURL, urlErr.URL) assert.Equal(t, "connection refused", urlErr.Err.Error()) assert.NotContains(t, result.Error(), "secret-token-12345") assert.NotContains(t, result.Error(), "abc123") }, }, { name: "wrapped url.Error is sanitized", inputErr: fmt.Errorf("failed to call service: %w", &url.Error{ Op: "Post", URL: sensitiveURL, Err: errors.New("timeout"), }), checkFunc: func(t *testing.T, result error) { var urlErr *url.Error assert.True(t, errors.As(result, &urlErr)) assert.Equal(t, expectedURL, urlErr.URL) assert.NotContains(t, result.Error(), "secret-token-12345") }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { result := sanitizeURLError(tc.inputErr) if tc.expectNil { assert.Nil(t, result) return } assert.NotNil(t, result) if tc.checkFunc != nil { tc.checkFunc(t, result) } }) } } func TestECRClientWrapper(t *testing.T) { sensitiveURL := "https://ecr.amazonaws.com/v2/auth?token=secret-token-12345" t.Run("sanitizes url.Error", func(t *testing.T) { urlErr := &url.Error{ Op: "Post", URL: sensitiveURL, Err: errors.New("connection refused"), } mock := &mock_api.MockECRAPI{ GetAuthorizationTokenFn: func(_ *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { return nil, urlErr }, } client := NewECRClientWrapper(mock) _, err := client.GetAuthorizationToken(context.Background(), &ecr.GetAuthorizationTokenInput{}) assert.Error(t, err) assert.NotContains(t, err.Error(), sensitiveURL) assert.NotContains(t, err.Error(), "secret-token-12345") assert.Contains(t, err.Error(), "Post") assert.Contains(t, err.Error(), "connection refused") // Verify it's still a url.Error var resultURLErr *url.Error assert.True(t, errors.As(err, &resultURLErr)) }) t.Run("passes through non-url.Error unchanged", func(t *testing.T) { regularErr := errors.New("some other error") mock := &mock_api.MockECRAPI{ GetAuthorizationTokenFn: func(_ *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { return nil, regularErr }, } client := NewECRClientWrapper(mock) _, err := client.GetAuthorizationToken(context.Background(), &ecr.GetAuthorizationTokenInput{}) assert.Equal(t, regularErr, err) }) t.Run("returns nil error when no error", func(t *testing.T) { mock := &mock_api.MockECRAPI{ GetAuthorizationTokenFn: func(_ *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { return nil, nil }, } client := NewECRClientWrapper(mock) _, err := client.GetAuthorizationToken(context.Background(), &ecr.GetAuthorizationTokenInput{}) assert.NoError(t, err) }) } func TestECRPublicClientWrapper(t *testing.T) { sensitiveURL := "https://api.ecr-public.us-east-1.amazonaws.com/?token=secret-token-12345" t.Run("sanitizes url.Error", func(t *testing.T) { urlErr := &url.Error{ Op: "Post", URL: sensitiveURL, Err: errors.New("connection refused"), } mock := &mock_api.MockECRPublicAPI{ GetAuthorizationTokenFn: func(_ *ecrpublic.GetAuthorizationTokenInput) (*ecrpublic.GetAuthorizationTokenOutput, error) { return nil, urlErr }, } client := NewECRPublicClientWrapper(mock) _, err := client.GetAuthorizationToken(context.Background(), &ecrpublic.GetAuthorizationTokenInput{}) assert.Error(t, err) assert.NotContains(t, err.Error(), sensitiveURL) assert.NotContains(t, err.Error(), "secret-token-12345") assert.Contains(t, err.Error(), "Post") assert.Contains(t, err.Error(), "connection refused") // Verify it's still a url.Error var resultURLErr *url.Error assert.True(t, errors.As(err, &resultURLErr)) }) t.Run("passes through non-url.Error unchanged", func(t *testing.T) { regularErr := errors.New("some other error") mock := &mock_api.MockECRPublicAPI{ GetAuthorizationTokenFn: func(_ *ecrpublic.GetAuthorizationTokenInput) (*ecrpublic.GetAuthorizationTokenOutput, error) { return nil, regularErr }, } client := NewECRPublicClientWrapper(mock) _, err := client.GetAuthorizationToken(context.Background(), &ecrpublic.GetAuthorizationTokenInput{}) assert.Equal(t, regularErr, err) }) t.Run("returns nil error when no error", func(t *testing.T) { mock := &mock_api.MockECRPublicAPI{ GetAuthorizationTokenFn: func(_ *ecrpublic.GetAuthorizationTokenInput) (*ecrpublic.GetAuthorizationTokenOutput, error) { return nil, nil }, } client := NewECRPublicClientWrapper(mock) _, err := client.GetAuthorizationToken(context.Background(), &ecrpublic.GetAuthorizationTokenInput{}) assert.NoError(t, err) }) } amazon-ecr-credential-helper-0.12.0/ecr-login/api/factory.go000066400000000000000000000072221517311355300236500ustar00rootroot00000000000000// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package api import ( "context" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/ecr" "github.com/aws/aws-sdk-go-v2/service/ecrpublic" "github.com/aws/smithy-go/middleware" "github.com/aws/smithy-go/transport/http" "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cache" "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/version" ) // Options makes the constructors more configurable type Options struct { Config aws.Config CacheDir string } // ClientFactory is a factory for creating clients to interact with ECR type ClientFactory interface { NewClient(awsConfig aws.Config) Client NewClientWithOptions(opts Options) Client NewClientFromRegion(region string) Client NewClientWithFipsEndpoint(region string) (Client, error) NewClientWithDefaults() Client } // DefaultClientFactory is a default implementation of the ClientFactory type DefaultClientFactory struct{} var userAgentLoadOption = config.WithAPIOptions([]func(*middleware.Stack) error{ http.AddHeaderValue("User-Agent", "amazon-ecr-credential-helper/"+version.Version), }) // NewClientWithDefaults creates the client and defaults region func (defaultClientFactory DefaultClientFactory) NewClientWithDefaults() Client { awsConfig, err := config.LoadDefaultConfig(context.TODO(), userAgentLoadOption) if err != nil { panic(err) } return defaultClientFactory.NewClientWithOptions(Options{Config: awsConfig}) } // NewClientWithFipsEndpoint overrides the default ECR service endpoint in a given region to use the FIPS endpoint func (defaultClientFactory DefaultClientFactory) NewClientWithFipsEndpoint(region string) (Client, error) { awsConfig, err := config.LoadDefaultConfig( context.TODO(), userAgentLoadOption, config.WithRegion(region), config.WithEndpointDiscovery(aws.EndpointDiscoveryEnabled), ) if err != nil { return nil, err } return defaultClientFactory.NewClientWithOptions(Options{Config: awsConfig}), nil } // NewClientFromRegion uses the region to create the client func (defaultClientFactory DefaultClientFactory) NewClientFromRegion(region string) Client { awsConfig, err := config.LoadDefaultConfig( context.TODO(), userAgentLoadOption, config.WithRegion(region), ) if err != nil { panic(err) } return defaultClientFactory.NewClientWithOptions(Options{ Config: awsConfig, }) } // NewClient Create new client with AWS Config func (defaultClientFactory DefaultClientFactory) NewClient(awsConfig aws.Config) Client { return defaultClientFactory.NewClientWithOptions(Options{Config: awsConfig}) } // NewClientWithOptions Create new client with Options func (defaultClientFactory DefaultClientFactory) NewClientWithOptions(opts Options) Client { // The ECR Public API is only available in us-east-1 today publicConfig := opts.Config.Copy() publicConfig.Region = "us-east-1" return &defaultClient{ ecrClient: NewECRClientWrapper(ecr.NewFromConfig(opts.Config)), ecrPublicClient: NewECRPublicClientWrapper(ecrpublic.NewFromConfig(publicConfig)), credentialCache: cache.BuildCredentialsCache(opts.Config, opts.CacheDir), } } amazon-ecr-credential-helper-0.12.0/ecr-login/api/mocks/000077500000000000000000000000001517311355300227635ustar00rootroot00000000000000amazon-ecr-credential-helper-0.12.0/ecr-login/api/mocks/api_mocks.go000066400000000000000000000031011517311355300252520ustar00rootroot00000000000000// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. // Automatically generated by MockGen. DO NOT EDIT! // Source: github.com/awslabs/amazon-ecr-credential-helper/ecr-login/api (interfaces: ECRAPI) package mock_api import ( "context" "github.com/aws/aws-sdk-go-v2/service/ecr" "github.com/aws/aws-sdk-go-v2/service/ecrpublic" ) type MockECRAPI struct { GetAuthorizationTokenFn func(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) } func (m MockECRAPI) GetAuthorizationToken(_ context.Context, input *ecr.GetAuthorizationTokenInput, _ ...func(*ecr.Options)) (*ecr.GetAuthorizationTokenOutput, error) { return m.GetAuthorizationTokenFn(input) } type MockECRPublicAPI struct { GetAuthorizationTokenFn func(input *ecrpublic.GetAuthorizationTokenInput) (*ecrpublic.GetAuthorizationTokenOutput, error) } func (m MockECRPublicAPI) GetAuthorizationToken(_ context.Context, input *ecrpublic.GetAuthorizationTokenInput, _ ...func(*ecrpublic.Options)) (*ecrpublic.GetAuthorizationTokenOutput, error) { return m.GetAuthorizationTokenFn(input) } amazon-ecr-credential-helper-0.12.0/ecr-login/cache/000077500000000000000000000000001517311355300221415ustar00rootroot00000000000000amazon-ecr-credential-helper-0.12.0/ecr-login/cache/build.go000066400000000000000000000074331517311355300235760ustar00rootroot00000000000000// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package cache import ( "context" "crypto/md5" "crypto/sha256" "encoding/base64" "fmt" "os" "strings" "github.com/aws/aws-sdk-go-v2/aws" "github.com/mitchellh/go-homedir" "github.com/sirupsen/logrus" ecrconfig "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/config" ) func BuildCredentialsCache(config aws.Config, cacheDir string) CredentialsCache { if os.Getenv("AWS_ECR_DISABLE_CACHE") != "" { logrus.Debug("Cache disabled due to AWS_ECR_DISABLE_CACHE") return NewNullCredentialsCache() } if cacheDir == "" { //Get cacheDir from env var "AWS_ECR_CACHE_DIR" or set to default cacheDir = ecrconfig.GetCacheDir() } cacheDir, err := homedir.Expand(cacheDir) if err != nil { logrus.WithError(err).Debug("Could not expand cache path, disabling cache") return NewNullCredentialsCache() } cacheFilename := "cache.json" credentials, err := config.Credentials.Retrieve(context.TODO()) if err != nil { logrus.WithError(err).Debug("Could not fetch credentials for cache prefix, disabling cache") return NewNullCredentialsCache() } // In FIPS mode, skip legacy MD5-based cache keys var legacyPrefix, legacyPublicKey string if !isFipsMode() { legacyPrefix = legacyCredentialsCachePrefix(config.Region, credentials) legacyPublicKey = legacyCredentialsPublicCacheKey(credentials) } return NewFileCredentialsCache( cacheDir, cacheFilename, credentialsCachePrefix(config.Region, credentials), credentialsPublicCacheKey(credentials), legacyPrefix, legacyPublicKey, ) } // Determine a key prefix for a credentials cache. Because auth tokens are scoped to an account and region, rely on provided // region, as well as hash of the access key. func credentialsCachePrefix(region string, credentials aws.Credentials) string { return fmt.Sprintf("%s-%s-", region, checksum(credentials.AccessKeyID)) } func credentialsPublicCacheKey(credentials aws.Credentials) string { return fmt.Sprintf("%s-%s", ServiceECRPublic, checksum(credentials.AccessKeyID)) } // Legacy cache key functions for backward compatibility with MD5-based keys func legacyCredentialsCachePrefix(region string, credentials aws.Credentials) string { return fmt.Sprintf("%s-%s-", region, md5Checksum(credentials.AccessKeyID)) } func legacyCredentialsPublicCacheKey(credentials aws.Credentials) string { return fmt.Sprintf("%s-%s", ServiceECRPublic, md5Checksum(credentials.AccessKeyID)) } // Base64 encodes a SHA-256 checksum. Used for uniqueness, not cryptographic security. func checksum(text string) string { hasher := sha256.New() hasher.Write([]byte(text)) data := hasher.Sum(nil) return base64.StdEncoding.EncodeToString(data) } // isFipsMode checks if GODEBUG=fips140=on or GODEBUG=fips140=only is set func isFipsMode() bool { godebug := os.Getenv("GODEBUG") if godebug == "" { return false } for _, setting := range strings.Split(godebug, ",") { trimmed := strings.TrimSpace(setting) if trimmed == "fips140=on" || trimmed == "fips140=only" { return true } } return false } // Deprecated: Use checksum for new cache entries. // Note: This function will panic if GODEBUG=fips140=only is set. func md5Checksum(text string) string { hasher := md5.New() data := hasher.Sum([]byte(text)) return base64.StdEncoding.EncodeToString(data) } amazon-ecr-credential-helper-0.12.0/ecr-login/cache/build_test.go000066400000000000000000000112341517311355300246270ustar00rootroot00000000000000// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package cache import ( "fmt" "os" "testing" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/credentials" "github.com/stretchr/testify/assert" ) const ( testRegion = "test-region" testCacheFilename = "cache.json" testAccessKey = "accessKey" testSecretKey = "secretKey" testToken = "token" // base64 SHA-256 sum of "accessKey" - FIPS-compatible testCredentialHash = "xOV45/s/9aT8cbO8tDicjEV1KKkfpLdKrQs0ipPGgGc=" // Legacy base64 MD5 sum of "accessKey" for backward compatibility tests testLegacyCredentialHash = "YWNjZXNzS2V51B2M2Y8AsgTpgAmY7PhCfg==" ) func TestFactoryBuildFileCache(t *testing.T) { config := aws.Config{ Region: testRegion, Credentials: credentials.NewStaticCredentialsProvider(testAccessKey, testSecretKey, testToken), } cache := BuildCredentialsCache(config, "") assert.NotNil(t, cache) fileCache, ok := cache.(*fileCredentialCache) assert.True(t, ok, "built cache is not a fileCredentialsCache") assert.Equal(t, fileCache.cachePrefixKey, fmt.Sprintf("%s-%s-", testRegion, testCredentialHash)) assert.Equal(t, fileCache.filename, testCacheFilename) } func TestFactoryBuildNullCacheWithoutCredentials(t *testing.T) { config := aws.Config{ Region: testRegion, Credentials: aws.AnonymousCredentials{}, } cache := BuildCredentialsCache(config, "") assert.NotNil(t, cache) _, ok := cache.(*nullCredentialsCache) assert.True(t, ok, "built cache is a nullCredentialsCache") } func TestFactoryBuildNullCache(t *testing.T) { os.Setenv("AWS_ECR_DISABLE_CACHE", "1") defer os.Unsetenv("AWS_ECR_DISABLE_CACHE") config := aws.Config{Region: testRegion} cache := BuildCredentialsCache(config, "") assert.NotNil(t, cache) _, ok := cache.(*nullCredentialsCache) assert.True(t, ok, "built cache is a nullCredentialsCache") } // TestCredentialsPrefixUsesNewHash verifies that credentialsCachePrefix uses SHA-256 func TestCredentialsPrefixUsesNewHash(t *testing.T) { creds := aws.Credentials{AccessKeyID: testAccessKey} prefix := credentialsCachePrefix(testRegion, creds) expectedPrefix := fmt.Sprintf("%s-%s-", testRegion, testCredentialHash) assert.Equal(t, expectedPrefix, prefix, "Cache prefix should use FIPS-compatible SHA-256 hash") } // TestIsFipsMode verifies that the isFipsMode function correctly detects FIPS mode func TestIsFipsMode(t *testing.T) { tests := []struct { name string godebug string expected bool }{ {"FIPS mode with fips140=on", "fips140=on", true}, {"FIPS mode with fips140=only", "fips140=only", true}, {"FIPS mode with other settings", "foo=bar,fips140=on,baz=qux", true}, {"No FIPS mode", "", false}, {"Different GODEBUG setting", "foo=bar", false}, {"FIPS mode disabled", "fips140=off", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Save original value original := os.Getenv("GODEBUG") defer func() { if original == "" { os.Unsetenv("GODEBUG") } else { os.Setenv("GODEBUG", original) } }() // Set test value if tt.godebug == "" { os.Unsetenv("GODEBUG") } else { os.Setenv("GODEBUG", tt.godebug) } result := isFipsMode() assert.Equal(t, tt.expected, result) }) } } // TestLegacyKeysNotGeneratedInFipsSimulation verifies the behavior of BuildCredentialsCache // when FIPS mode would be detected. func TestLegacyKeysNotGeneratedInFipsSimulation(t *testing.T) { config := aws.Config{ Region: testRegion, Credentials: credentials.NewStaticCredentialsProvider(testAccessKey, testSecretKey, testToken), } cache := BuildCredentialsCache(config, "") assert.NotNil(t, cache) fileCache, ok := cache.(*fileCredentialCache) assert.True(t, ok, "built cache should be a fileCredentialCache") assert.Equal(t, fmt.Sprintf("%s-%s-", testRegion, testCredentialHash), fileCache.cachePrefixKey) assert.Equal(t, fmt.Sprintf("%s-%s", ServiceECRPublic, testCredentialHash), fileCache.publicCacheKey) assert.NotEmpty(t, fileCache.legacyCachePrefixKey, "Legacy cache prefix should be present in non-FIPS mode") assert.NotEmpty(t, fileCache.legacyPublicCacheKey, "Legacy public cache key should be present in non-FIPS mode") } amazon-ecr-credential-helper-0.12.0/ecr-login/cache/credentials.go000066400000000000000000000026021517311355300247650ustar00rootroot00000000000000// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package cache import ( "time" ) type CredentialsCache interface { Get(registry string) *AuthEntry GetPublic() *AuthEntry Set(registry string, entry *AuthEntry) List() []*AuthEntry Clear() } type Service string const ( ServiceECR Service = "ecr" ServiceECRPublic Service = "ecr-public" ) type AuthEntry struct { AuthorizationToken string RequestedAt time.Time ExpiresAt time.Time ProxyEndpoint string Service Service } // IsValid checks if AuthEntry is still valid at testTime. AuthEntries expire at 1/2 of their original // requested window. func (authEntry *AuthEntry) IsValid(testTime time.Time) bool { validWindow := authEntry.ExpiresAt.Sub(authEntry.RequestedAt) refreshTime := authEntry.ExpiresAt.Add(-1 * validWindow / time.Duration(2)) return testTime.Before(refreshTime) } amazon-ecr-credential-helper-0.12.0/ecr-login/cache/credentials_test.go000066400000000000000000000033151517311355300260260ustar00rootroot00000000000000// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package cache import ( "testing" "time" "github.com/stretchr/testify/assert" ) func TestIsValid_NewEntry(t *testing.T) { authEntry := &AuthEntry{ RequestedAt: time.Now(), ExpiresAt: time.Now().Add(12 * time.Hour), } assert.True(t, authEntry.IsValid(time.Now())) } func TestIsValid_OldEntry(t *testing.T) { authEntry := &AuthEntry{ RequestedAt: time.Now().Add(-12 * time.Hour), ExpiresAt: time.Now(), } assert.False(t, authEntry.IsValid(time.Now())) } func TestIsValid_BeforeRefreshTime(t *testing.T) { now := time.Now() authEntry := &AuthEntry{ RequestedAt: now.Add(-6 * time.Hour), ExpiresAt: now.Add(6 * time.Hour), } assert.True(t, authEntry.IsValid(now.Add(-1*time.Second))) } func TestIsValid_AtRefreshTime(t *testing.T) { now := time.Now() authEntry := &AuthEntry{ RequestedAt: now.Add(-6 * time.Hour), ExpiresAt: now.Add(6 * time.Hour), } assert.False(t, authEntry.IsValid(now)) } func TestIsValid_AfterRefreshTime(t *testing.T) { now := time.Now() authEntry := &AuthEntry{ RequestedAt: now.Add(-6 * time.Hour), ExpiresAt: now.Add(6 * time.Hour), } assert.False(t, authEntry.IsValid(now.Add(time.Second))) } amazon-ecr-credential-helper-0.12.0/ecr-login/cache/file.go000066400000000000000000000146231517311355300234150ustar00rootroot00000000000000// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package cache import ( "encoding/json" "fmt" "os" "path/filepath" "github.com/sirupsen/logrus" ) const registryCacheVersion = "1.0" type RegistryCache struct { Registries map[string]*AuthEntry Version string } type fileCredentialCache struct { path string filename string cachePrefixKey string publicCacheKey string legacyCachePrefixKey string legacyPublicCacheKey string } func newRegistryCache() *RegistryCache { return &RegistryCache{ Registries: make(map[string]*AuthEntry), Version: registryCacheVersion, } } // NewFileCredentialsCache returns a new file credentials cache. // // path is used for temporary files during save, and filename should be a relative filename // in the same directory where the cache is serialized and deserialized. // // cachePrefixKey is used for scoping credentials for a given credential cache (i.e. region and // accessKey). legacyCachePrefixKey and legacyPublicCacheKey are used for backward compatibility // with MD5-based cache keys. func NewFileCredentialsCache(path string, filename string, cachePrefixKey string, publicCacheKey string, legacyCachePrefixKey string, legacyPublicCacheKey string) CredentialsCache { if _, err := os.Stat(path); err != nil { os.MkdirAll(path, 0700) } return &fileCredentialCache{ path: path, filename: filename, cachePrefixKey: cachePrefixKey, publicCacheKey: publicCacheKey, legacyCachePrefixKey: legacyCachePrefixKey, legacyPublicCacheKey: legacyPublicCacheKey, } } func (f *fileCredentialCache) Get(registry string) *AuthEntry { logrus.WithField("registry", registry).Debug("Checking file cache") registryCache := f.init() entry := registryCache.Registries[f.cachePrefixKey+registry] if entry != nil { return entry } if isFipsMode() { logrus.WithField("registry", registry).Debug("FIPS mode enabled, skipping legacy MD5 cache lookup") return nil } legacyEntry := registryCache.Registries[f.legacyCachePrefixKey+registry] if legacyEntry != nil { logrus.WithField("registry", registry).Debug("Found cached credentials using legacy MD5 key") return legacyEntry } logrus.WithField("registry", registry).Debug("Credentials not found") return nil } func (f *fileCredentialCache) GetPublic() *AuthEntry { logrus.Debug("Checking file cache for ECR Public") registryCache := f.init() entry := registryCache.Registries[f.publicCacheKey] if entry != nil { return entry } if isFipsMode() { logrus.Debug("FIPS mode enabled, skipping legacy MD5 cache lookup for ECR Public") return nil } legacyEntry := registryCache.Registries[f.legacyPublicCacheKey] if legacyEntry != nil { logrus.Debug("Found cached ECR Public credentials using legacy MD5 key") return legacyEntry } logrus.WithField("registry", "public").Debug("Credentials not found") return nil } func (f *fileCredentialCache) Set(registry string, entry *AuthEntry) { logrus. WithField("registry", registry). WithField("service", entry.Service). Debug("Saving credentials to file cache") registryCache := f.init() key := f.cachePrefixKey + registry if entry.Service == ServiceECRPublic { key = f.publicCacheKey } registryCache.Registries[key] = entry err := f.save(registryCache) if err != nil { logrus.WithError(err).Info("Could not save cache") } } // List returns all of the available AuthEntries (regardless of prefix) func (f *fileCredentialCache) List() []*AuthEntry { registryCache := f.init() // optimize allocation for copy entries := make([]*AuthEntry, 0, len(registryCache.Registries)) for _, entry := range registryCache.Registries { entries = append(entries, entry) } return entries } func (f *fileCredentialCache) Clear() { err := os.Remove(f.fullFilePath()) if err != nil { logrus.WithError(err).Info("Could not clear cache") } } func (f *fileCredentialCache) fullFilePath() string { return filepath.Join(f.path, f.filename) } // Saves credential cache to disk. This writes to a temporary file first, then moves the file to the config location. // This eliminates from reading partially written credential files, and reduces (but does not eliminate) concurrent // file access. There is not guarantee here for handling multiple writes at once since there is no out of process locking. func (f *fileCredentialCache) save(registryCache *RegistryCache) error { file, err := os.CreateTemp(f.path, ".config.json.tmp") if err != nil { return err } buff, err := json.MarshalIndent(registryCache, "", " ") if err != nil { file.Close() os.Remove(file.Name()) return err } _, err = file.Write(buff) if err != nil { file.Close() os.Remove(file.Name()) return err } file.Close() // note this is only atomic when relying on linux syscalls os.Rename(file.Name(), f.fullFilePath()) return err } func (f *fileCredentialCache) init() *RegistryCache { registryCache, err := f.load() if err != nil { logrus.WithError(err).Info("Could not load existing cache") f.Clear() registryCache = newRegistryCache() } return registryCache } // Loading a cache from disk will return errors for malformed or incompatible cache files. func (f *fileCredentialCache) load() (*RegistryCache, error) { registryCache := newRegistryCache() file, err := os.Open(f.fullFilePath()) if os.IsNotExist(err) { return registryCache, nil } if err != nil { return nil, err } defer file.Close() if err = json.NewDecoder(file).Decode(®istryCache); err != nil { return nil, err } if registryCache.Version != registryCacheVersion { return nil, fmt.Errorf("ecr: Registry cache version %#v is not compatible with %#v, ignoring existing cache", registryCache.Version, registryCacheVersion) } // migrate entries for key := range registryCache.Registries { if registryCache.Registries[key].Service == "" { registryCache.Registries[key].Service = ServiceECR } } return registryCache, nil } amazon-ecr-credential-helper-0.12.0/ecr-login/cache/file_test.go000066400000000000000000000260231517311355300244510ustar00rootroot00000000000000// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package cache import ( "os" "path/filepath" "testing" "time" "github.com/stretchr/testify/assert" ) const ( testRegistryName = "testRegistry" testCachePrefixKey = "prefix-" testPublicCacheKey = "public-" testLegacyCachePrefixKey = "legacy-prefix-" testLegacyPublicCacheKey = "legacy-public-" testFilename = "test.json" ) var ( testAuthEntry = AuthEntry{ AuthorizationToken: "testToken", RequestedAt: time.Now().Add(-5 * time.Hour), ExpiresAt: time.Now().Add(7 * time.Hour), ProxyEndpoint: "testEndpoint", Service: ServiceECR, } testPublicAuthEntry = AuthEntry{ AuthorizationToken: "testToken", RequestedAt: time.Now().Add(-5 * time.Hour), ExpiresAt: time.Now().Add(7 * time.Hour), ProxyEndpoint: "testEndpoint", Service: ServiceECRPublic, } testPath = os.TempDir() + "/ecr" testFullFillename = filepath.Join(testPath, testFilename) ) func TestAuthEntryValid(t *testing.T) { assert.True(t, testAuthEntry.IsValid(time.Now())) } func TestAuthEntryInValid(t *testing.T) { assert.True(t, testAuthEntry.IsValid(time.Now().Add(time.Second))) } func TestCredentials(t *testing.T) { credentialCache := NewFileCredentialsCache(testPath, testFilename, testCachePrefixKey, testPublicCacheKey, testLegacyCachePrefixKey, testLegacyPublicCacheKey) credentialCache.Set(testRegistryName, &testAuthEntry) entry := credentialCache.Get(testRegistryName) assert.Equal(t, testAuthEntry.AuthorizationToken, entry.AuthorizationToken) assert.Equal(t, testAuthEntry.ProxyEndpoint, entry.ProxyEndpoint) assert.WithinDuration(t, testAuthEntry.RequestedAt, entry.RequestedAt, 1*time.Second) assert.WithinDuration(t, testAuthEntry.ExpiresAt, entry.ExpiresAt, 1*time.Second) assert.Equal(t, testAuthEntry.Service, entry.Service) entries := credentialCache.List() assert.NotEmpty(t, entries) assert.Len(t, entries, 1) assert.Equal(t, entry, entries[0]) credentialCache.Clear() entry = credentialCache.Get(testRegistryName) assert.Nil(t, entry) } func TestCredentialsPublic(t *testing.T) { credentialCache := NewFileCredentialsCache(testPath, testFilename, testCachePrefixKey, testPublicCacheKey, testLegacyCachePrefixKey, testLegacyPublicCacheKey) credentialCache.Set(testRegistryName, &testPublicAuthEntry) entry := credentialCache.GetPublic() assert.Equal(t, testPublicAuthEntry.AuthorizationToken, entry.AuthorizationToken) assert.Equal(t, testPublicAuthEntry.ProxyEndpoint, entry.ProxyEndpoint) assert.WithinDuration(t, testPublicAuthEntry.RequestedAt, entry.RequestedAt, 1*time.Second) assert.WithinDuration(t, testPublicAuthEntry.ExpiresAt, entry.ExpiresAt, 1*time.Second) assert.Equal(t, testPublicAuthEntry.Service, entry.Service) entries := credentialCache.List() assert.NotEmpty(t, entries) assert.Len(t, entries, 1) assert.Equal(t, entry, entries[0]) credentialCache.Clear() entry = credentialCache.GetPublic() assert.Nil(t, entry) } func TestPreviousVersionCache(t *testing.T) { credentialCache := NewFileCredentialsCache(testPath, testFilename, testCachePrefixKey, testPublicCacheKey, testLegacyCachePrefixKey, testLegacyPublicCacheKey) registryCache := newRegistryCache() registryCache.Version = "0.1" registryCache.Registries[testRegistryName] = &testAuthEntry credentialCache.(*fileCredentialCache).save(registryCache) entry := credentialCache.Get(testRegistryName) assert.Nil(t, entry) credentialCache.Clear() } const testBadJson = "{nope not good json at all." func TestInvalidCache(t *testing.T) { credentialCache := NewFileCredentialsCache(testPath, testFilename, testCachePrefixKey, testPublicCacheKey, testLegacyCachePrefixKey, testLegacyPublicCacheKey) file, err := os.Create(testFullFillename) assert.NoError(t, err) file.WriteString(testBadJson) err = file.Close() assert.NoError(t, err) entry := credentialCache.Get(testRegistryName) assert.Nil(t, entry) credentialCache.Clear() } // TestLegacyKeyBackwardCompatibility tests that credentials stored with legacy MD5-based keys func TestLegacyKeyBackwardCompatibility(t *testing.T) { credentialCache := NewFileCredentialsCache(testPath, testFilename, testCachePrefixKey, testPublicCacheKey, testLegacyCachePrefixKey, testLegacyPublicCacheKey) defer credentialCache.Clear() registryCache := newRegistryCache() legacyKey := testLegacyCachePrefixKey + testRegistryName registryCache.Registries[legacyKey] = &testAuthEntry credentialCache.(*fileCredentialCache).save(registryCache) entry := credentialCache.Get(testRegistryName) assert.NotNil(t, entry, "Should be able to retrieve credentials stored with legacy MD5 key as fallback") assert.Equal(t, testAuthEntry.AuthorizationToken, entry.AuthorizationToken) assert.Equal(t, testAuthEntry.ProxyEndpoint, entry.ProxyEndpoint) assert.Equal(t, testAuthEntry.Service, entry.Service) registryCache, err := credentialCache.(*fileCredentialCache).load() assert.NoError(t, err) assert.NotNil(t, registryCache.Registries[legacyKey], "Legacy key should still exist") newKey := testCachePrefixKey + testRegistryName assert.Nil(t, registryCache.Registries[newKey], "New key should not exist (no auto-migration)") } // TestLegacyPublicKeyBackwardCompatibility tests that public credentials stored with legacy MD5-based keys func TestLegacyPublicKeyBackwardCompatibility(t *testing.T) { credentialCache := NewFileCredentialsCache(testPath, testFilename, testCachePrefixKey, testPublicCacheKey, testLegacyCachePrefixKey, testLegacyPublicCacheKey) defer credentialCache.Clear() registryCache := newRegistryCache() registryCache.Registries[testLegacyPublicCacheKey] = &testPublicAuthEntry credentialCache.(*fileCredentialCache).save(registryCache) entry := credentialCache.GetPublic() assert.NotNil(t, entry, "Should be able to retrieve public credentials stored with legacy MD5 key as fallback") assert.Equal(t, testPublicAuthEntry.AuthorizationToken, entry.AuthorizationToken) assert.Equal(t, testPublicAuthEntry.ProxyEndpoint, entry.ProxyEndpoint) assert.Equal(t, testPublicAuthEntry.Service, entry.Service) registryCache, err := credentialCache.(*fileCredentialCache).load() assert.NoError(t, err) assert.NotNil(t, registryCache.Registries[testLegacyPublicCacheKey], "Legacy public key should still exist") assert.Nil(t, registryCache.Registries[testPublicCacheKey], "New public key should not exist (no auto-migration)") } // TestNewKeyPreferredOverLegacy tests that when both new and legacy keys exist, // the new FIPS-compatible key is preferred func TestNewKeyPreferredOverLegacy(t *testing.T) { credentialCache := NewFileCredentialsCache(testPath, testFilename, testCachePrefixKey, testPublicCacheKey, testLegacyCachePrefixKey, testLegacyPublicCacheKey) defer credentialCache.Clear() registryCache := newRegistryCache() legacyKey := testLegacyCachePrefixKey + testRegistryName newKey := testCachePrefixKey + testRegistryName legacyEntry := testAuthEntry legacyEntry.AuthorizationToken = "legacyToken" registryCache.Registries[legacyKey] = &legacyEntry newEntry := testAuthEntry newEntry.AuthorizationToken = "newToken" registryCache.Registries[newKey] = &newEntry credentialCache.(*fileCredentialCache).save(registryCache) entry := credentialCache.Get(testRegistryName) assert.NotNil(t, entry) assert.Equal(t, "newToken", entry.AuthorizationToken, "Should prefer new FIPS-compatible key over legacy key") credentialCache.Clear() } // TestFipsModeOnlySkipsLegacyLookup tests that when GODEBUG=fips140=only is set, // legacy MD5-based cache lookups are skipped. func TestFipsModeOnlySkipsLegacyLookup(t *testing.T) { os.Setenv("GODEBUG", "fips140=only") defer os.Unsetenv("GODEBUG") credentialCache := NewFileCredentialsCache(testPath, testFilename, testCachePrefixKey, testPublicCacheKey, testLegacyCachePrefixKey, testLegacyPublicCacheKey) defer credentialCache.Clear() registryCache := newRegistryCache() legacyKey := testLegacyCachePrefixKey + testRegistryName registryCache.Registries[legacyKey] = &testAuthEntry credentialCache.(*fileCredentialCache).save(registryCache) entry := credentialCache.Get(testRegistryName) assert.Nil(t, entry, "Should return nil in FIPS mode when only legacy MD5 key exists") } // TestFipsModeOnSkipsLegacyLookup tests that when GODEBUG=fips140=on is set, // legacy MD5-based cache lookups are skipped. func TestFipsModeOnSkipsLegacyLookup(t *testing.T) { os.Setenv("GODEBUG", "fips140=on") defer os.Unsetenv("GODEBUG") credentialCache := NewFileCredentialsCache(testPath, testFilename, testCachePrefixKey, testPublicCacheKey, testLegacyCachePrefixKey, testLegacyPublicCacheKey) defer credentialCache.Clear() registryCache := newRegistryCache() legacyKey := testLegacyCachePrefixKey + testRegistryName registryCache.Registries[legacyKey] = &testAuthEntry credentialCache.(*fileCredentialCache).save(registryCache) // Try to retrieve - should return nil because FIPS mode skips MD5 lookup entry := credentialCache.Get(testRegistryName) assert.Nil(t, entry, "Should return nil in FIPS mode when only legacy MD5 key exists") } // TestFipsModeOnlySkipsLegacyPublicLookup tests that when GODEBUG=fips140=only is set, // legacy MD5-based public cache lookups are skipped. func TestFipsModeOnlySkipsLegacyPublicLookup(t *testing.T) { os.Setenv("GODEBUG", "fips140=only") defer os.Unsetenv("GODEBUG") credentialCache := NewFileCredentialsCache(testPath, testFilename, testCachePrefixKey, testPublicCacheKey, testLegacyCachePrefixKey, testLegacyPublicCacheKey) defer credentialCache.Clear() registryCache := newRegistryCache() registryCache.Registries[testLegacyPublicCacheKey] = &testPublicAuthEntry credentialCache.(*fileCredentialCache).save(registryCache) entry := credentialCache.GetPublic() assert.Nil(t, entry, "Should return nil in FIPS mode when only legacy MD5 key exists for public") } // TestFipsModeWithNewKey tests that FIPS mode still works with SHA-256 keys func TestFipsModeWithNewKey(t *testing.T) { os.Setenv("GODEBUG", "fips140=only") defer os.Unsetenv("GODEBUG") credentialCache := NewFileCredentialsCache(testPath, testFilename, testCachePrefixKey, testPublicCacheKey, testLegacyCachePrefixKey, testLegacyPublicCacheKey) defer credentialCache.Clear() registryCache := newRegistryCache() newKey := testCachePrefixKey + testRegistryName registryCache.Registries[newKey] = &testAuthEntry credentialCache.(*fileCredentialCache).save(registryCache) entry := credentialCache.Get(testRegistryName) assert.NotNil(t, entry, "Should find credentials with SHA-256 key in FIPS mode") assert.Equal(t, testAuthEntry.AuthorizationToken, entry.AuthorizationToken) } amazon-ecr-credential-helper-0.12.0/ecr-login/cache/mocks/000077500000000000000000000000001517311355300232555ustar00rootroot00000000000000amazon-ecr-credential-helper-0.12.0/ecr-login/cache/mocks/cache_mocks.go000066400000000000000000000025761517311355300260550ustar00rootroot00000000000000// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package mock_cache import ( "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cache" ) type MockCredentialsCache struct { GetFn func(registry string) *cache.AuthEntry GetPublicFn func() *cache.AuthEntry SetFn func(registry string, entry *cache.AuthEntry) ListFn func() []*cache.AuthEntry ClearFn func() } var _ cache.CredentialsCache = (*MockCredentialsCache)(nil) func (m MockCredentialsCache) Get(registry string) *cache.AuthEntry { return m.GetFn(registry) } func (m MockCredentialsCache) GetPublic() *cache.AuthEntry { return m.GetPublicFn() } func (m MockCredentialsCache) Set(registry string, entry *cache.AuthEntry) { m.SetFn(registry, entry) } func (m MockCredentialsCache) List() []*cache.AuthEntry { return m.ListFn() } func (m MockCredentialsCache) Clear() { m.ClearFn() } amazon-ecr-credential-helper-0.12.0/ecr-login/cache/null.go000066400000000000000000000020131517311355300234360ustar00rootroot00000000000000// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package cache type nullCredentialsCache struct{} func NewNullCredentialsCache() CredentialsCache { return &nullCredentialsCache{} } func (n *nullCredentialsCache) Get(_ string) *AuthEntry { return nil } func (n *nullCredentialsCache) GetPublic() *AuthEntry { return nil } func (n *nullCredentialsCache) Set(_ string, _ *AuthEntry) { } func (n *nullCredentialsCache) List() []*AuthEntry { return []*AuthEntry{} } func (n *nullCredentialsCache) Clear() { } amazon-ecr-credential-helper-0.12.0/ecr-login/cache/null_test.go000066400000000000000000000017661517311355300245130ustar00rootroot00000000000000// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package cache import ( "testing" "github.com/stretchr/testify/assert" ) func TestNullCache(t *testing.T) { credentialCache := NewNullCredentialsCache() entry := credentialCache.Get(testRegistryName) assert.Nil(t, entry) credentialCache.Set(testRegistryName, &testAuthEntry) entry = credentialCache.Get(testRegistryName) assert.Nil(t, entry) credentialCache.Clear() entries := credentialCache.List() assert.Empty(t, entries) } amazon-ecr-credential-helper-0.12.0/ecr-login/cli/000077500000000000000000000000001517311355300216455ustar00rootroot00000000000000amazon-ecr-credential-helper-0.12.0/ecr-login/cli/docker-credential-ecr-login/000077500000000000000000000000001517311355300271015ustar00rootroot00000000000000amazon-ecr-credential-helper-0.12.0/ecr-login/cli/docker-credential-ecr-login/main.go000066400000000000000000000030421517311355300303530ustar00rootroot00000000000000// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package main import ( "flag" "fmt" "os" ecr "github.com/awslabs/amazon-ecr-credential-helper/ecr-login" "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/config" "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/version" "github.com/docker/docker-credential-helpers/credentials" ) const banner = `amazon-ecr-credential-helper Version: %s Git commit: %s ` func init() { // Set up version information in docker-credential-helpers package credentials.Name = "docker-credential-ecr-login" credentials.Package = "github.com/awslabs/amazon-ecr-credential-helper/ecr-login" credentials.Version = version.Version credentials.Revision = version.GitCommitSHA } func main() { var versionFlag bool flag.BoolVar(&versionFlag, "v", false, "print version and exit") flag.Parse() // Exit safely when version is used if versionFlag { fmt.Printf(banner, version.Version, version.GitCommitSHA) os.Exit(0) } config.SetupLogger() credentials.Serve(ecr.NewECRHelper()) } amazon-ecr-credential-helper-0.12.0/ecr-login/config/000077500000000000000000000000001517311355300223435ustar00rootroot00000000000000amazon-ecr-credential-helper-0.12.0/ecr-login/config/cache_dir.go000066400000000000000000000013331517311355300245730ustar00rootroot00000000000000// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package config import "os" func GetCacheDir() string { if cacheDir := os.Getenv("AWS_ECR_CACHE_DIR"); cacheDir != "" { return cacheDir } return "~/.ecr" } amazon-ecr-credential-helper-0.12.0/ecr-login/config/log.go000066400000000000000000000027041517311355300234560ustar00rootroot00000000000000// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package config import ( "fmt" "os" "path/filepath" "github.com/mitchellh/go-homedir" "github.com/sirupsen/logrus" ) func SetupLogger() { logrusConfig() } func logrusConfig() { // Add URL redactor hook to sanitize sensitive URL information in logs logrus.AddHook(&URLRedactorHook{}) logdir, err := homedir.Expand(GetCacheDir() + "/log") if err != nil { fmt.Fprintf(os.Stderr, "log: failed to find directory: %v", err) logdir = os.TempDir() } // Clean the path to replace with OS-specific separators logdir = filepath.Clean(logdir) err = os.MkdirAll(logdir, os.ModeDir|0700) if err != nil { fmt.Fprintf(os.Stderr, "log: failed to create directory: %v", err) logdir = os.TempDir() } file, err := os.OpenFile(filepath.Join(logdir, "ecr-login.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) if err != nil { return } logrus.SetLevel(logrus.DebugLevel) logrus.SetOutput(file) } amazon-ecr-credential-helper-0.12.0/ecr-login/config/url_redactor.go000066400000000000000000000056501517311355300253650ustar00rootroot00000000000000// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package config import ( "errors" "net/url" "github.com/sirupsen/logrus" ) // URLRedactorHook is a logrus hook that sanitizes URL fields in log entries // to prevent leaking sensitive information like userinfo (passwords) and query parameters. type URLRedactorHook struct{} // Levels returns all log levels that this hook should be applied to func (hook *URLRedactorHook) Levels() []logrus.Level { return logrus.AllLevels } // Fire is called when a log event is fired. It sanitizes the serverURL field // and error fields that may contain URLs. func (hook *URLRedactorHook) Fire(entry *logrus.Entry) error { // Redact serverURL field if value, exists := entry.Data["serverURL"]; exists { if strValue, ok := value.(string); ok { entry.Data["serverURL"] = RedactURL(strValue) } } // Redact URLs in error field if value, exists := entry.Data[logrus.ErrorKey]; exists { if err, ok := value.(error); ok { entry.Data[logrus.ErrorKey] = RedactURLFromError(err) } } return nil } // RedactURL redacts sensitive information from a URL string including: // - Password in userinfo (user:password@host) // - Query parameter values // Returns the original string unchanged if it's not a valid URL. func RedactURL(rawURL string) string { if rawURL == "" { return rawURL } parsed, err := url.ParseRequestURI(rawURL) if err != nil { return rawURL } return redactParsedURL(parsed) } // redactParsedURL redacts password and query parameters from a parsed URL func redactParsedURL(parsed *url.URL) string { if parsed.User != nil { if _, hasPassword := parsed.User.Password(); hasPassword { parsed.User = url.UserPassword(parsed.User.Username(), "xxxxx") } } if query := parsed.Query(); len(query) > 0 { for k := range query { query.Set(k, "redacted") } parsed.RawQuery = query.Encode() } return parsed.String() } // RedactURLFromError redacts URL query parameter values from url.Error. // This handles cases where HTTP errors contain URLs with sensitive query parameters. // Returns the original error if it's not a url.Error or cannot be parsed. func RedactURLFromError(err error) error { var urlErr *url.Error if err != nil && errors.As(err, &urlErr) { parsedURL, urlParseErr := url.Parse(urlErr.URL) if urlParseErr == nil && parsedURL.Scheme != "" && parsedURL.Host != "" { urlErr.URL = redactParsedURL(parsedURL) return urlErr } } return err } amazon-ecr-credential-helper-0.12.0/ecr-login/config/url_redactor_test.go000066400000000000000000000162421517311355300264230ustar00rootroot00000000000000// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package config import ( "errors" "net/url" "testing" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" ) func TestRedactURL(t *testing.T) { tests := []struct { name string input string expected string }{ { name: "empty string", input: "", expected: "", }, { name: "URL without sensitive data", input: "https://example.com/path", expected: "https://example.com/path", }, { name: "URL with query parameters - values redacted", input: "https://example.com/path?token=secret&id=123", expected: "https://example.com/path?id=redacted&token=redacted", }, { name: "URL with username only", input: "https://user@example.com/path", expected: "https://user@example.com/path", }, { name: "ECR URL format with scheme", input: "https://123456789012.dkr.ecr.us-west-2.amazonaws.com", expected: "https://123456789012.dkr.ecr.us-west-2.amazonaws.com", }, { name: "ECR public URL", input: "https://public.ecr.aws", expected: "https://public.ecr.aws", }, { name: "HTTP URL", input: "http://example.com/path", expected: "http://example.com/path", }, { name: "HTTP URL with credentials", input: "http://user:pass@example.com/path", expected: "http://user:xxxxx@example.com/path", }, { name: "plain string - unchanged", input: "not a url", expected: "not a url", }, { name: "URL with only query params - values redacted", input: "https://example.com?secret=value", expected: "https://example.com?secret=redacted", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := RedactURL(tt.input) assert.Equal(t, tt.expected, result) }) } } func TestURLRedactorHook_Levels(t *testing.T) { hook := &URLRedactorHook{} levels := hook.Levels() assert.Equal(t, logrus.AllLevels, levels) } func TestURLRedactorHook_Fire(t *testing.T) { hook := &URLRedactorHook{} tests := []struct { name string fields logrus.Fields expectedFields logrus.Fields }{ { name: "sanitizes serverURL field with password", fields: logrus.Fields{ "serverURL": "https://user:pass@example.com", }, expectedFields: logrus.Fields{ "serverURL": "https://user:xxxxx@example.com", }, }, { name: "sanitizes serverURL field with query params", fields: logrus.Fields{ "serverURL": "https://example.com?token=secret", }, expectedFields: logrus.Fields{ "serverURL": "https://example.com?token=redacted", }, }, { name: "serverURL without credentials unchanged", fields: logrus.Fields{ "serverURL": "https://123456789012.dkr.ecr.us-west-2.amazonaws.com", }, expectedFields: logrus.Fields{ "serverURL": "https://123456789012.dkr.ecr.us-west-2.amazonaws.com", }, }, { name: "does not modify other fields", fields: logrus.Fields{ "registry": "123456789012", "region": "us-west-2", "service": "ecr", }, expectedFields: logrus.Fields{ "registry": "123456789012", "region": "us-west-2", "service": "ecr", }, }, { name: "handles serverURL with other fields", fields: logrus.Fields{ "serverURL": "https://user:secret@example.com", "registry": "123456789012", "region": "us-west-2", }, expectedFields: logrus.Fields{ "serverURL": "https://user:xxxxx@example.com", "registry": "123456789012", "region": "us-west-2", }, }, { name: "handles non-string values", fields: logrus.Fields{ "serverURL": "https://user:pass@example.com", "count": 42, "enabled": true, }, expectedFields: logrus.Fields{ "serverURL": "https://user:xxxxx@example.com", "count": 42, "enabled": true, }, }, { name: "handles empty fields", fields: logrus.Fields{}, expectedFields: logrus.Fields{}, }, { name: "handles serverURL with non-string value", fields: logrus.Fields{ "serverURL": 12345, }, expectedFields: logrus.Fields{ "serverURL": 12345, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { entry := &logrus.Entry{ Data: tt.fields, } err := hook.Fire(entry) assert.NoError(t, err) assert.Equal(t, tt.expectedFields, entry.Data) }) } } func TestRedactURLFromError(t *testing.T) { tests := []struct { name string inputErr error expectedURL string unchanged bool }{ { name: "nil error returns nil", inputErr: nil, unchanged: true, }, { name: "non-url.Error returns unchanged", inputErr: errors.New("some error"), unchanged: true, }, { name: "url.Error with query params - redacted", inputErr: &url.Error{ Op: "Get", URL: "https://example.com/path?token=secret&id=123", Err: errors.New("connection refused"), }, expectedURL: "https://example.com/path?id=redacted&token=redacted", }, { name: "url.Error without query params - unchanged", inputErr: &url.Error{ Op: "Get", URL: "https://example.com/path", Err: errors.New("connection refused"), }, expectedURL: "https://example.com/path", }, { name: "url.Error with password and query params - both redacted", inputErr: &url.Error{ Op: "Get", URL: "https://user:pass@example.com/path?token=secret", Err: errors.New("connection refused"), }, expectedURL: "https://user:xxxxx@example.com/path?token=redacted", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := RedactURLFromError(tt.inputErr) if tt.unchanged { assert.Equal(t, tt.inputErr, result) return } var urlErr *url.Error assert.True(t, errors.As(result, &urlErr)) assert.Equal(t, tt.expectedURL, urlErr.URL) }) } } func TestURLRedactorHook_Fire_WithError(t *testing.T) { hook := &URLRedactorHook{} t.Run("redacts url.Error with query params", func(t *testing.T) { urlErr := &url.Error{ Op: "Get", URL: "https://example.com/path?token=secret", Err: errors.New("connection refused"), } entry := &logrus.Entry{ Data: logrus.Fields{ logrus.ErrorKey: urlErr, }, } err := hook.Fire(entry) assert.NoError(t, err) resultErr, ok := entry.Data[logrus.ErrorKey].(*url.Error) assert.True(t, ok) assert.Equal(t, "https://example.com/path?token=redacted", resultErr.URL) }) t.Run("non-url.Error unchanged", func(t *testing.T) { plainErr := errors.New("some error") entry := &logrus.Entry{ Data: logrus.Fields{ logrus.ErrorKey: plainErr, }, } err := hook.Fire(entry) assert.NoError(t, err) assert.Equal(t, plainErr, entry.Data[logrus.ErrorKey]) }) } amazon-ecr-credential-helper-0.12.0/ecr-login/ecr.go000066400000000000000000000122221517311355300221750ustar00rootroot00000000000000// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package ecr import ( "errors" "fmt" "io" "os" "github.com/sirupsen/logrus" "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/api" "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/config" "github.com/docker/docker-credential-helpers/credentials" ) var notImplemented = errors.New("not implemented") type ECRHelper struct { clientFactory api.ClientFactory logger *logrus.Logger } type Option func(*ECRHelper) // WithClientFactory sets the ClientFactory used to make API requests. func WithClientFactory(clientFactory api.ClientFactory) Option { return func(e *ECRHelper) { e.clientFactory = clientFactory } } // WithLogger sets a new logger instance that writes to the given writer, // instead of the default writer which writes to stderr. // // This can be useful if callers want to redirect logging emitted by this tool // to another location. func WithLogger(w io.Writer) Option { return func(e *ECRHelper) { logger := logrus.New() logger.Out = w logger.AddHook(&config.URLRedactorHook{}) e.logger = logger } } // NewECRHelper returns a new ECRHelper with the given options to override // default behavior. func NewECRHelper(opts ...Option) *ECRHelper { e := &ECRHelper{ clientFactory: api.DefaultClientFactory{}, logger: logrus.StandardLogger(), } for _, o := range opts { o(e) } return e } // ensure ECRHelper adheres to the credentials.Helper interface var _ credentials.Helper = (*ECRHelper)(nil) func shouldIgnoreCredsStorage() bool { return os.Getenv("AWS_ECR_IGNORE_CREDS_STORAGE") == "true" } // Add tries to store credentials when docker requests it. This usually happens during `docker login` calls. In our context, // storing arbitrary user given credentials makes no sense. func (self ECRHelper) Add(creds *credentials.Credentials) error { if shouldIgnoreCredsStorage() { self.logger. WithField("serverURL", creds.ServerURL). Warning("Ignoring request to store credentials since AWS_ECR_IGNORE_CREDS_STORAGE env variable is set." + "ecr-login does not require 'docker login', and does not support persisting temporary ECR-issued credentials.") return nil } else { self.logger.Warning("Add() is not supported by the ecr-login credentials helper as all issued credentials are temporary. Consider setting the AWS_ECR_IGNORE_CREDS_STORAGE env variable (see documentation for details).") return notImplemented } } // Delete tries to delete credentials when docker requests it. This usually happens during `docker logout` calls. In our context, we // don't store arbitrary user given credentials so deleting them makes no sense. func (self ECRHelper) Delete(serverURL string) error { if shouldIgnoreCredsStorage() { self.logger. WithField("serverURL", serverURL). Warning("Ignoring request to store credentials since AWS_ECR_IGNORE_CREDS_STORAGE env variable is set." + "ecr-login does not require 'docker login', and does not support persisting temporary ECR-issued credentials.") return nil } else { self.logger.Warning("Delete() credentials is not supported by the ecr-login credentials helper as all issued credentials are temporary. Consider setting the AWS_ECR_IGNORE_CREDS_STORAGE env variable (see documentation for details).") return notImplemented } } func (self ECRHelper) Get(serverURL string) (string, string, error) { registry, err := api.ExtractRegistry(serverURL) if err != nil { self.logger. WithError(err). WithField("serverURL", serverURL). Error("Error parsing the serverURL") return "", "", credentials.NewErrCredentialsNotFound() } var client api.Client if registry.FIPS { client, err = self.clientFactory.NewClientWithFipsEndpoint(registry.Region) if err != nil { self.logger.WithError(err).Error("Error resolving FIPS endpoint") return "", "", credentials.NewErrCredentialsNotFound() } } else { client = self.clientFactory.NewClientFromRegion(registry.Region) } auth, err := client.GetCredentials(serverURL) if err != nil { self.logger.WithError(err).Error("Error retrieving credentials") return "", "", credentials.NewErrCredentialsNotFound() } return auth.Username, auth.Password, nil } func (self ECRHelper) List() (map[string]string, error) { self.logger.Debug("Listing credentials") client := self.clientFactory.NewClientWithDefaults() auths, err := client.ListCredentials() if err != nil { self.logger.WithError(err).Error("Error listing credentials") return nil, fmt.Errorf("ecr: could not list credentials: %v", err) } result := map[string]string{} for _, auth := range auths { serverURL := auth.ProxyEndpoint result[serverURL] = auth.Username } return result, nil } amazon-ecr-credential-helper-0.12.0/ecr-login/ecr_test.go000066400000000000000000000142231517311355300232370ustar00rootroot00000000000000// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package ecr import ( "errors" "fmt" "os" "testing" ecr "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/api" mock_api "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/mocks" "github.com/docker/docker-credential-helpers/credentials" "github.com/stretchr/testify/assert" ) const ( region = "us-east-1" proxyEndpoint = "123456789012" + ".dkr.ecr." + region + ".amazonaws.com" proxyEndpointUrl = "https://" + proxyEndpoint expectedUsername = "username" expectedPassword = "password" ) // unsetEnv unsets an environment variable and registers a cleanup function that // restores it after the test is complete. Note: tests that modify environment // variables may not be run in parallel. // // See also [testing.T.Setenv] func unsetEnv(t *testing.T, key string) { t.Helper() // save original value originalValue, wasSet := os.LookupEnv(key) // unset the environment variable os.Unsetenv(key) // restore the original value if necessary if wasSet { t.Cleanup(func() { os.Setenv(key, originalValue) }) } } func TestGetSuccess(t *testing.T) { factory := &mock_api.MockClientFactory{} client := &mock_api.MockClient{} helper := NewECRHelper(WithClientFactory(factory)) factory.NewClientFromRegionFn = func(_ string) ecr.Client { return client } client.GetCredentialsFn = func(serverURL string) (*ecr.Auth, error) { if serverURL != proxyEndpoint { return nil, fmt.Errorf("unexpected input: %s", serverURL) } return &ecr.Auth{ Username: expectedUsername, Password: expectedPassword, ProxyEndpoint: proxyEndpointUrl, }, nil } username, password, err := helper.Get(proxyEndpoint) assert.Nil(t, err) assert.Equal(t, expectedUsername, username) assert.Equal(t, expectedPassword, password) } func TestGetError(t *testing.T) { factory := &mock_api.MockClientFactory{} client := &mock_api.MockClient{} helper := NewECRHelper(WithClientFactory(factory)) factory.NewClientFromRegionFn = func(_ string) ecr.Client { return client } client.GetCredentialsFn = func(serverURL string) (*ecr.Auth, error) { return nil, errors.New("test error") } username, password, err := helper.Get(proxyEndpoint) assert.True(t, credentials.IsErrCredentialsNotFound(err)) assert.Empty(t, username) assert.Empty(t, password) } func TestGetNoMatch(t *testing.T) { helper := NewECRHelper(WithClientFactory(nil)) username, password, err := helper.Get("not-ecr-server-url") assert.True(t, credentials.IsErrCredentialsNotFound(err)) assert.Empty(t, username) assert.Empty(t, password) } func TestListSuccess(t *testing.T) { factory := &mock_api.MockClientFactory{} client := &mock_api.MockClient{} helper := NewECRHelper(WithClientFactory(factory)) factory.NewClientWithDefaultsFn = func() ecr.Client { return client } client.ListCredentialsFn = func() ([]*ecr.Auth, error) { return []*ecr.Auth{{ Username: expectedUsername, Password: expectedPassword, ProxyEndpoint: proxyEndpointUrl, }}, nil } serverList, err := helper.List() assert.NoError(t, err) assert.Len(t, serverList, 1) assert.Equal(t, expectedUsername, serverList[proxyEndpointUrl]) } func TestListFailure(t *testing.T) { factory := &mock_api.MockClientFactory{} client := &mock_api.MockClient{} helper := NewECRHelper(WithClientFactory(factory)) factory.NewClientWithDefaultsFn = func() ecr.Client { return client } client.ListCredentialsFn = func() ([]*ecr.Auth, error) { return nil, errors.New("nope") } serverList, err := helper.List() assert.Error(t, err) assert.Len(t, serverList, 0) } func TestAddIgnored(t *testing.T) { factory := &mock_api.MockClientFactory{} helper := NewECRHelper(WithClientFactory(factory)) t.Setenv("AWS_ECR_IGNORE_CREDS_STORAGE", "true") err := helper.Add(&credentials.Credentials{ ServerURL: proxyEndpoint, Username: "AWS", Secret: "supersecret", }) assert.Nil(t, err) } func TestAddNotImplemented(t *testing.T) { tests := []struct { name string setEnv func(*testing.T) }{ {"unset", func(*testing.T) { unsetEnv(t, "AWS_ECR_IGNORE_CREDS_STORAGE") }}, {"false", func(*testing.T) { t.Setenv("AWS_ECR_IGNORE_CREDS_STORAGE", "false") }}, {"0", func(*testing.T) { t.Setenv("AWS_ECR_IGNORE_CREDS_STORAGE", "0") }}, {"empty string", func(*testing.T) { t.Setenv("AWS_ECR_IGNORE_CREDS_STORAGE", "") }}, } for _, test := range tests { t.Run(test.name, func(tt *testing.T) { factory := &mock_api.MockClientFactory{} helper := NewECRHelper(WithClientFactory(factory)) test.setEnv(tt) err := helper.Add(&credentials.Credentials{ ServerURL: proxyEndpoint, Username: "AWS", Secret: "supersecret", }) assert.Error(tt, err, "not implemented") }) } } func TestDeleteIgnored(t *testing.T) { factory := &mock_api.MockClientFactory{} helper := NewECRHelper(WithClientFactory(factory)) t.Setenv("AWS_ECR_IGNORE_CREDS_STORAGE", "true") err := helper.Delete(proxyEndpoint) assert.Nil(t, err) } func TestDeleteNotImplemented(t *testing.T) { tests := []struct { name string setEnv func(*testing.T) }{ {"unset", func(*testing.T) { unsetEnv(t, "AWS_ECR_IGNORE_CREDS_STORAGE") }}, {"false", func(*testing.T) { t.Setenv("AWS_ECR_IGNORE_CREDS_STORAGE", "false") }}, {"0", func(*testing.T) { t.Setenv("AWS_ECR_IGNORE_CREDS_STORAGE", "0") }}, {"empty string", func(*testing.T) { t.Setenv("AWS_ECR_IGNORE_CREDS_STORAGE", "") }}, } for _, test := range tests { t.Run(test.name, func(tt *testing.T) { factory := &mock_api.MockClientFactory{} helper := NewECRHelper(WithClientFactory(factory)) test.setEnv(tt) err := helper.Delete(proxyEndpoint) assert.Error(tt, err, "not implemented") }) } } amazon-ecr-credential-helper-0.12.0/ecr-login/go.mod000066400000000000000000000026001517311355300222020ustar00rootroot00000000000000module github.com/awslabs/amazon-ecr-credential-helper/ecr-login go 1.24 require ( github.com/aws/aws-sdk-go-v2 v1.41.2 github.com/aws/aws-sdk-go-v2/config v1.32.10 github.com/aws/aws-sdk-go-v2/credentials v1.19.10 github.com/aws/aws-sdk-go-v2/service/ecr v1.55.3 github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.10 github.com/aws/smithy-go v1.24.1 github.com/docker/docker-credential-helpers v0.9.5 github.com/mitchellh/go-homedir v1.1.0 github.com/sirupsen/logrus v1.9.4 github.com/stretchr/testify v1.11.1 ) require ( github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 // indirect github.com/aws/aws-sdk-go-v2/service/signin v1.0.6 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.30.11 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.41.7 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/sys v0.20.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) amazon-ecr-credential-helper-0.12.0/ecr-login/go.sum000066400000000000000000000114611517311355300222340ustar00rootroot00000000000000github.com/aws/aws-sdk-go-v2 v1.41.2 h1:LuT2rzqNQsauaGkPK/7813XxcZ3o3yePY0Iy891T2ls= github.com/aws/aws-sdk-go-v2 v1.41.2/go.mod h1:IvvlAZQXvTXznUPfRVfryiG1fbzE2NGK6m9u39YQ+S4= github.com/aws/aws-sdk-go-v2/config v1.32.10 h1:9DMthfO6XWZYLfzZglAgW5Fyou2nRI5CuV44sTedKBI= github.com/aws/aws-sdk-go-v2/config v1.32.10/go.mod h1:2rUIOnA2JaiqYmSKYmRJlcMWy6qTj1vuRFscppSBMcw= github.com/aws/aws-sdk-go-v2/credentials v1.19.10 h1:EEhmEUFCE1Yhl7vDhNOI5OCL/iKMdkkYFTRpZXNw7m8= github.com/aws/aws-sdk-go-v2/credentials v1.19.10/go.mod h1:RnnlFCAlxQCkN2Q379B67USkBMu1PipEEiibzYN5UTE= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 h1:Ii4s+Sq3yDfaMLpjrJsqD6SmG/Wq/P5L/hw2qa78UAY= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18/go.mod h1:6x81qnY++ovptLE6nWQeWrpXxbnlIex+4H4eYYGcqfc= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18 h1:F43zk1vemYIqPAwhjTjYIz0irU2EY7sOb/F5eJ3HuyM= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18/go.mod h1:w1jdlZXrGKaJcNoL+Nnrj+k5wlpGXqnNrKoP22HvAug= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18 h1:xCeWVjj0ki0l3nruoyP2slHsGArMxeiiaoPN5QZH6YQ= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18/go.mod h1:r/eLGuGCBw6l36ZRWiw6PaZwPXb6YOj+i/7MizNl5/k= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= github.com/aws/aws-sdk-go-v2/service/ecr v1.55.3 h1:RtGctYMmkTerGClvdY6bHXdtly4FeYw9wz/NPz62LF8= github.com/aws/aws-sdk-go-v2/service/ecr v1.55.3/go.mod h1:vBfBu24Ka3/5UZtepbTV0gnc9VPLT8ok+0oDDaYAzn4= github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.10 h1:1A/sI3LNMi3fhRI5TFLMwwo7ALAALSFVCSGvFlr1Iys= github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.10/go.mod h1:Diyyyz0b43X13pdi1mVMqlTwDjOmRbJMvDsqnduUYWM= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 h1:CeY9LUdur+Dxoeldqoun6y4WtJ3RQtzk0JMP2gfUay0= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5/go.mod h1:AZLZf2fMaahW5s/wMRciu1sYbdsikT/UHwbUjOdEVTc= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 h1:LTRCYFlnnKFlKsyIQxKhJuDuA3ZkrDQMRYm6rXiHlLY= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18/go.mod h1:XhwkgGG6bHSd00nO/mexWTcTjgd6PjuvWQMqSn2UaEk= github.com/aws/aws-sdk-go-v2/service/signin v1.0.6 h1:MzORe+J94I+hYu2a6XmV5yC9huoTv8NRcCrUNedDypQ= github.com/aws/aws-sdk-go-v2/service/signin v1.0.6/go.mod h1:hXzcHLARD7GeWnifd8j9RWqtfIgxj4/cAtIVIK7hg8g= github.com/aws/aws-sdk-go-v2/service/sso v1.30.11 h1:7oGD8KPfBOJGXiCoRKrrrQkbvCp8N++u36hrLMPey6o= github.com/aws/aws-sdk-go-v2/service/sso v1.30.11/go.mod h1:0DO9B5EUJQlIDif+XJRWCljZRKsAFKh3gpFz7UnDtOo= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 h1:edCcNp9eGIUDUCrzoCu1jWAXLGFIizeqkdkKgRlJwWc= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15/go.mod h1:lyRQKED9xWfgkYC/wmmYfv7iVIM68Z5OQ88ZdcV1QbU= github.com/aws/aws-sdk-go-v2/service/sts v1.41.7 h1:NITQpgo9A5NrDZ57uOWj+abvXSb83BbyggcUBVksN7c= github.com/aws/aws-sdk-go-v2/service/sts v1.41.7/go.mod h1:sks5UWBhEuWYDPdwlnRFn1w7xWdH29Jcpe+/PJQefEs= github.com/aws/smithy-go v1.24.1 h1:VbyeNfmYkWoxMVpGUAbQumkODcYmfMRfZ8yQiH30SK0= github.com/aws/smithy-go v1.24.1/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= 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/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY= github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 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/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= amazon-ecr-credential-helper-0.12.0/ecr-login/mocks/000077500000000000000000000000001517311355300222125ustar00rootroot00000000000000amazon-ecr-credential-helper-0.12.0/ecr-login/mocks/ecr_mocks.go000066400000000000000000000044051517311355300245110ustar00rootroot00000000000000// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package mock_api import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/api" ) type MockClientFactory struct { NewClientFn func(awsConfig aws.Config) api.Client NewClientWithOptionsFn func(opts api.Options) api.Client NewClientFromRegionFn func(region string) api.Client NewClientWithFipsEndpointFn func(region string) (api.Client, error) NewClientWithDefaultsFn func() api.Client } func (m MockClientFactory) NewClient(awsConfig aws.Config) api.Client { return m.NewClientFn(awsConfig) } func (m MockClientFactory) NewClientWithOptions(opts api.Options) api.Client { return m.NewClientWithOptionsFn(opts) } func (m MockClientFactory) NewClientFromRegion(region string) api.Client { return m.NewClientFromRegionFn(region) } func (m MockClientFactory) NewClientWithFipsEndpoint(region string) (api.Client, error) { return m.NewClientWithFipsEndpointFn(region) } func (m MockClientFactory) NewClientWithDefaults() api.Client { return m.NewClientWithDefaultsFn() } var _ api.ClientFactory = (*MockClientFactory)(nil) type MockClient struct { GetCredentialsFn func(serverURL string) (*api.Auth, error) GetCredentialsByRegistryIDFn func(registryID string) (*api.Auth, error) ListCredentialsFn func() ([]*api.Auth, error) } var _ api.Client = (*MockClient)(nil) func (m *MockClient) GetCredentials(serverURL string) (*api.Auth, error) { return m.GetCredentialsFn(serverURL) } func (m *MockClient) GetCredentialsByRegistryID(registryID string) (*api.Auth, error) { return m.GetCredentialsByRegistryIDFn(registryID) } func (m *MockClient) ListCredentials() ([]*api.Auth, error) { return m.ListCredentialsFn() } amazon-ecr-credential-helper-0.12.0/ecr-login/version/000077500000000000000000000000001517311355300225635ustar00rootroot00000000000000amazon-ecr-credential-helper-0.12.0/ecr-login/version/version.go000066400000000000000000000003151517311355300245760ustar00rootroot00000000000000package version // Version indicates which version of the binary is running. var Version = "development" // GitCommitSHA indicates which git shorthash the binary was built off of var GitCommitSHA string amazon-ecr-credential-helper-0.12.0/scripts/000077500000000000000000000000001517311355300207065ustar00rootroot00000000000000amazon-ecr-credential-helper-0.12.0/scripts/build_binary.sh000077500000000000000000000026221517311355300237120ustar00rootroot00000000000000#!/bin/bash # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You # may not use this file except in compliance with the License. A copy of # the License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. set -euo pipefail # Normalize to working directory being build root (up one level from ./scripts) ROOT=$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd ) cd "${ROOT}" # Builds the ecr-login binary from source in the specified destination paths. mkdir -p $1 cd "${ROOT}/ecr-login" package_root="github.com/awslabs/amazon-ecr-credential-helper/ecr-login" version_ldflags="" if [[ -n "${2}" ]]; then version_ldflags="-X ${package_root}/version.Version=${2}" fi if [[ -n "${3}" ]]; then version_ldflags="$version_ldflags -X ${package_root}/version.GitCommitSHA=${3}" fi GOOS=${TARGET_GOOS:-} GOARCH=${TARGET_GOARCH:-} CGO_ENABLED=0 \ go build \ -installsuffix cgo \ -a \ -ldflags "-buildid= -s ${version_ldflags}" \ -trimpath \ -o $1/docker-credential-ecr-login \ ./cli/docker-credential-ecr-login amazon-ecr-credential-helper-0.12.0/scripts/build_third_party_licenses.sh000077500000000000000000000045531517311355300266510ustar00rootroot00000000000000#!/usr/bin/env bash # Copyright The containerd Authors. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # A script to generate a THIRD-PARTY-LICENSES file containing all the licenses that we use from third parties. # NOTE: This only adds licenses from go dependencies. For other licenses, see NOTICE. set -euo pipefail # Normalize to working directory being root (up one level from ./scripts) root=$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd ) if test -d "${root}/ecr-login/vendor"; then echo "[ERROR]: generating THIRD-PARTY-LICENSES file while dependencies are vendored will result in unknown dependency versions in the licenses file." echo "[INFO]: To resolve, remove the vendored dependencies before generating the licenses file:" echo "----------------------------------------" echo "pushd ${root}" echo "rm -rf ecr-login/vendor" echo "make licenses" echo "git restore ecr-login/vendor" echo "popd" echo "----------------------------------------" exit 1 fi license_file="${root}/THIRD-PARTY-LICENSES" pushd "${root}/ecr-login" # Remove content from the license file truncate -s 0 "${license_file}" { # The apache 2.0 license doesn't get modified with a copyright. To reduce duplication, add attribution for each project using the license, but include the license text just once. go-licenses report \ --include_tests \ --ignore github.com/awslabs/amazon-ecr-credential-helper \ --template="${root}/scripts/third_party_licenses/apache.tpl" ./... cat "${root}/scripts/third_party_licenses/APACHE_LICENSE" # For other licenses, just use the entire license text from the package. go-licenses report \ --include_tests \ --ignore github.com/awslabs/amazon-ecr-credential-helper \ --template="${root}/scripts/third_party_licenses/other.tpl" ./... } >> "${license_file}" popd amazon-ecr-credential-helper-0.12.0/scripts/build_variant.sh000077500000000000000000000023201517311355300240650ustar00rootroot00000000000000#!/bin/bash # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You # may not use this file except in compliance with the License. A copy of # the License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. set -euo pipefail # This script is used for compilation of a specific variant. # Specify GOOS as $1, GOARCH as $2 # Binaries are placed into ./bin/$GOOS-$GOARCH/docker-credential-ecr-login # Normalize to working directory being build root (up one level from ./scripts) ROOT=$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd ) cd "${ROOT}" # Export variables export TARGET_GOOS="$1" export TARGET_GOARCH="$2" ECR_LOGIN_VERSION="$3" ECR_LOGIN_GITCOMMIT_SHA="$4" ./scripts/build_binary.sh "${ROOT}/bin/${TARGET_GOOS}-${TARGET_GOARCH}" $ECR_LOGIN_VERSION $ECR_LOGIN_GITCOMMIT_SHA echo "Built ecr-login for ${TARGET_GOOS}-${TARGET_GOARCH}-${ECR_LOGIN_VERSION}" amazon-ecr-credential-helper-0.12.0/scripts/container_init.sh000077500000000000000000000021151517311355300242510ustar00rootroot00000000000000#!/bin/sh # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You # may not use this file except in compliance with the License. A copy of # the License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. # A POSIX shell script which installs the required dependencies # to build the Amazon ECR credential helper variants in a Golang # Alpine container. set -ex apk add --no-cache \ bash \ git \ make # Resolves permission issues for Go cache when # building credential helper as non-root user. mkdir /.cache && chmod 777 /.cache # Resolves dubious ownership of git directory when # building credential helper as root user. git config --global --add safe.directory /go/src/github.com/awslabs/amazon-ecr-credential-helper amazon-ecr-credential-helper-0.12.0/scripts/gogenerate000077500000000000000000000014301517311355300227520ustar00rootroot00000000000000#!/bin/bash # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You may # not use this file except in compliance with the License. A copy of the # License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is distributed # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either # express or implied. See the License for the specific language governing # permissions and limitations under the License. set -e # Normalize to working directory being build root (up one level from ./scripts) ROOT=$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd ) cd "${ROOT}" go generate -x $(go list ./ecr-login/... | grep -v '/vendor/') amazon-ecr-credential-helper-0.12.0/scripts/hack/000077500000000000000000000000001517311355300216145ustar00rootroot00000000000000amazon-ecr-credential-helper-0.12.0/scripts/hack/codepipeline-git-commit.sh000077500000000000000000000014771517311355300266730ustar00rootroot00000000000000#!/bin/bash # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You may # not use this file except in compliance with the License. A copy of the # License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is distributed # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either # express or implied. See the License for the specific language governing # permissions and limitations under the License. # This script populates the GITCOMMIT_SHA file when built inside AWS CodeBuild # and invoked from AWS CodePipeline. set -ex [[ -z "${CODEBUILD_RESOLVED_SOURCE_VERSION}" ]] && exit 1 echo "${CODEBUILD_RESOLVED_SOURCE_VERSION}" > GITCOMMIT_SHA cat GITCOMMIT_SHA amazon-ecr-credential-helper-0.12.0/scripts/hack/codepipeline-source-archive.sh000077500000000000000000000025631517311355300275360ustar00rootroot00000000000000#!/bin/bash # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You may # not use this file except in compliance with the License. A copy of the # License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is distributed # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either # express or implied. See the License for the specific language governing # permissions and limitations under the License. # This script checks out a git repository with the correct revision for # execution in AWS CodeBuild when triggered by AWS CodePipeline and produces a # source archive. # The script assumes that you have the following environment variables set: # * CODEBUILD_RESOLVED_SOURCE_VERSION - Set automatically when AWS CodePipeline # invokes AWS CodeBuild # * GIT_REMOTE - A valid git remote that will be cloned # This script is meant to be triggered only from branch-based executions of the # pipeline. It does not handle pull requests. set -ex [[ -d .git ]] && exit 1 [[ -z "${CODEBUILD_RESOLVED_SOURCE_VERSION}" ]] && exit 1 [[ -z "${GIT_REMOTE}" ]] && exit 1 mkdir archive git clone "${GIT_REMOTE}" archive cd archive git checkout "${CODEBUILD_RESOLVED_SOURCE_VERSION}" make release-tarball make release-tarball-no-vendor amazon-ecr-credential-helper-0.12.0/scripts/hack/symlink-gopath-codebuild.sh000077500000000000000000000016151517311355300270540ustar00rootroot00000000000000#!/bin/bash # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You may # not use this file except in compliance with the License. A copy of the # License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is distributed # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either # express or implied. See the License for the specific language governing # permissions and limitations under the License. # This script is meant to make the package available in the 'canonical' location # for execution in AWS CodeBuild in the golang container image. if [[ ! -d "/go/src/github.com/awslabs/amazon-ecr-credential-helper" ]]; then mkdir -p "/go/src/github.com/awslabs" ln -s "$(pwd)" "/go/src/github.com/awslabs/amazon-ecr-credential-helper" fiamazon-ecr-credential-helper-0.12.0/scripts/hack/version-changelog.sh000077500000000000000000000027101517311355300255650ustar00rootroot00000000000000#!/bin/bash # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You may # not use this file except in compliance with the License. A copy of the # License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is distributed # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either # express or implied. See the License for the specific language governing # permissions and limitations under the License. # This script is an incredibly simple parser for the CHANGELOG.md in this # repository. It expects the changelog to have the following format: # # * Sections delimited by "#" # * Section titles matching the VERSION file # # For example, consider the following sample changelog: # # # 1.2.3 # * Foo # # 0.1.2-alpha # * Bar # # If the VERSION file is set to "0.1.2-alpha", the output of this script is: # # # 0.1.2-alpha # * Bar set -e # Normalize to working directory being source root (up two levels) ROOT=$( cd "$( dirname "${BASH_SOURCE[0]}" )/../.." && pwd ) cd "${ROOT}" VERSION="$(cat VERSION)" match="" while IFS= read -r line; do if [[ "${line:0:1}" == "#" ]]; then if [[ "${line}" == "# ${VERSION}" ]]; then match="y" continue else match="" fi fi if [[ -n "${match}" ]]; then echo "$line" fi done < "CHANGELOG.md" amazon-ecr-credential-helper-0.12.0/scripts/third_party_licenses/000077500000000000000000000000001517311355300251245ustar00rootroot00000000000000amazon-ecr-credential-helper-0.12.0/scripts/third_party_licenses/APACHE_LICENSE000066400000000000000000000261371517311355300271430ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. amazon-ecr-credential-helper-0.12.0/scripts/third_party_licenses/apache.tpl000066400000000000000000000001751517311355300270710ustar00rootroot00000000000000{{range . -}} {{if eq .LicenseName "Apache-2.0" -}} ** {{.Name}}; version {{.Version}} - {{.LicenseURL}} {{end -}} {{end -}} amazon-ecr-credential-helper-0.12.0/scripts/third_party_licenses/other.tpl000066400000000000000000000003451517311355300267700ustar00rootroot00000000000000{{ range . -}} {{ if ne .LicenseName "Apache-2.0" -}} -------------------------------------------------------------------------------- ** {{.Name}}; version {{.Version}} - {{.LicenseURL}} {{ .LicenseText }} {{end -}} {{end -}}