Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for dynamic credentials during get/put #322

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 63 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ differences:
too many ways to build and publish Docker images. It will be easier to
support many smaller resources + tasks rather than one huge interface.


## Source Configuration

<table>
Expand Down Expand Up @@ -160,7 +159,7 @@ differences:
<td>
<ul>
<li>
<code>host</code> <em>(Required)</em>:
<code>host</code> <em>(Required)</em>:
A hostname pointing to a Docker registry mirror service. Note that this
is only used if no registry hostname prefix is specified in the
<code>repository</code> key. If the <code>repository</code> contains a
Expand All @@ -169,7 +168,7 @@ differences:
registry in the <code>repository</code> key is used.
</li>
<li>
<code>username</code> and <code>password</code> <em>(Optional)</em>:
<code>username</code> and <code>password</code> <em>(Optional)</em>:
A username and password to use when authenticating to the mirror.
</li>
</ul>
Expand All @@ -180,30 +179,30 @@ differences:
<td>
<ul>
<li>
<code>server</code> <em>(Optional)</em>:
<code>server</code> <em>(Optional)</em>:
URL for the notary server. (equal to
<code>DOCKER_CONTENT_TRUST_SERVER</code>)
</li>
<li>
<code>repository_key_id</code> <em>(Required)</em>:
<code>repository_key_id</code> <em>(Required)</em>:
Target key's ID used to sign the trusted collection, could be retrieved
by <code>notary key list</code>
</li>
<li>
<code>repository_key</code> <em>(Required)</em>:
<code>repository_key</code> <em>(Required)</em>:
Target key used to sign the trusted collection.
</li>
<li>
<code>repository_passphrase</code> <em>(Required)</em>:
<code>repository_passphrase</code> <em>(Required)</em>:
The passphrase of the signing/target key. (equal to
<code>DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE</code>)
</li>
<li>
<code>tls_key</code> <em>(Optional)</em>:
<code>tls_key</code> <em>(Optional)</em>:
TLS key for the notary server.
</li>
<li>
<code>tls_cert</code> <em>(Optional)</em>:
<code>tls_cert</code> <em>(Optional)</em>:
TLS certificate for the notary server.
</li>
<li>
Expand Down Expand Up @@ -303,7 +302,7 @@ Each unique digest will be returned only once, with the most specific version
tag available. This is to handle "alias" tags like `1`, `1.2` pointing to
`1.2.3`.

Note: the initial `check` call will return *all valid versions*, which is
Note: the initial `check` call will return _all valid versions_, which is
unlike most resources which only return the latest version. This is an
intentional choice which will become the normal behavior for resources in
the future (per concourse/rfcs#38).
Expand Down Expand Up @@ -343,7 +342,7 @@ With a `variant` value specified, only semver tags with the matching variant
will be detected. With `variant` omitted, tags which include a variant are
ignored.

Note: some image tags actually include *mutliple* variants, e.g.
Note: some image tags actually include _mutliple_ variants, e.g.
`1.2.3-php7.3-apache`. With a variant of only `apache` configured, these tags
will be skipped to avoid accidentally using multiple variants. In order to
use these tags, you must specify the full variant combination, e.g.
Expand Down Expand Up @@ -387,6 +386,56 @@ this reason, the resource will only consider prerelease data starting with
`alpha`, `beta`, or `rc` as a proper prerelease, treating anything else as
a variant.

### Usage of dynamically generated credentials for `get` and `put` steps

Both the `get` and `put` steps allow you to override `username`/`password` as well
as `aws_*` fields. This is primarily beneficial when you are unable to generate
persistent credentials, and must use on-demand generated credentials.

An example of what this may look like is shown below:

```yaml
resources:
- name: src
type: git
source: {}
- name: registry
type: registry-image
check_every: never
source:
repository: ((aws-registry))
tag: latest
aws_region: ((aws-region))

jobs:
- name: push-to-ecr
plan:
- get: src
- get: repo-task-aws-deploy
params: { depth: 1 }
- task: build-image
# build your image here.
- task: get-credentials # write your credentials to credentials/example.json
config:
platform: linux
inputs: [{ name: src }]
outputs: [{ name: credentials }]
params: {}
run: { path: src/some-credential-script.sh }
- load_var: creds
file: credentials/example.json
- put: registry
params:
image: image/image.tar
aws_access_key_id: ((.:creds.aws_access_key_id))
aws_secret_access_key: ((.:creds.aws_secret_access_key))
aws_session_token: ((.:creds.aws_session_token))
get_params:
skip_download: true
aws_access_key_id: ((.:creds.aws_access_key_id))
aws_secret_access_key: ((.:creds.aws_secret_access_key))
aws_session_token: ((.:creds.aws_session_token))
```

### `get` Step (`in` script): fetch an image

Expand Down Expand Up @@ -449,7 +498,6 @@ In this format, the resource will produce the following files:

* `./image.tar`: the OCI image tarball, suitable for passing to `docker load`.


### `put` Step (`out` script): push and tag an image

Pushes an image to the registry as the specified tags.
Expand Down Expand Up @@ -531,8 +579,7 @@ Anonymous resources can specify
[a version](https://concourse-ci.org/tasks.html#schema.anonymous_resource.version),
which is the image digest. For example:


```
```yaml
image_resource:
type: docker-image
source:
Expand All @@ -548,8 +595,8 @@ going to be re-used.

### Prerequisites

* golang is *required* - version 1.11.x or above is required for go mod to work
* docker is *required* - version 17.06.x is tested; earlier versions may also
* golang is _required_ - version 1.11.x or above is required for go mod to work
* docker is _required_ - version 17.06.x is tested; earlier versions may also
work.
* go mod is used for dependency management of the golang packages.

Expand Down
6 changes: 5 additions & 1 deletion commands/in.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ func (i *In) Execute() error {

dest := i.args[1]

if req.Source.AwsRegion != "" {
// If credentials were defined in params, override the source configuration.
req.Source.BasicCredentials.Inherit(req.Params.BasicCredentials)
req.Source.AwsCredentials.Inherit(req.Params.AwsCredentials)

if req.Source.AwsAccessKeyId != "" && req.Source.AwsSecretAccessKey != "" && req.Source.AwsRegion != "" {
if !req.Source.AuthenticateToECR() {
return fmt.Errorf("cannot authenticate with ECR")
}
Expand Down
6 changes: 5 additions & 1 deletion commands/out.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ func (o *Out) Execute() error {

src := o.args[1]

if req.Source.AwsRegion != "" {
// If credentials were defined in params, override the source configuration.
req.Source.BasicCredentials.Inherit(req.Params.BasicCredentials)
req.Source.AwsCredentials.Inherit(req.Params.AwsCredentials)

if req.Source.AwsAccessKeyId != "" && req.Source.AwsSecretAccessKey != "" && req.Source.AwsRegion != "" {
if !req.Source.AuthenticateToECR() {
return fmt.Errorf("cannot authenticate with ECR")
}
Expand Down
43 changes: 43 additions & 0 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,44 @@ type AwsCredentials struct {
AwsRoleArns []string `json:"aws_role_arns,omitempty"`
}

func (c *AwsCredentials) Inherit(parent AwsCredentials) {
if c.AwsAccessKeyId == "" {
c.AwsAccessKeyId = parent.AwsAccessKeyId
}
if c.AwsSecretAccessKey == "" {
c.AwsSecretAccessKey = parent.AwsSecretAccessKey
}
if c.AwsSessionToken == "" {
c.AwsSessionToken = parent.AwsSessionToken
}
if c.AwsRegion == "" {
c.AwsRegion = parent.AwsRegion
}
if c.AWSECRRegistryId == "" {
c.AWSECRRegistryId = parent.AWSECRRegistryId
}
if c.AwsRoleArn == "" {
c.AwsRoleArn = parent.AwsRoleArn
}
if len(c.AwsRoleArns) == 0 {
c.AwsRoleArns = parent.AwsRoleArns
}
}

type BasicCredentials struct {
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
}

func (c *BasicCredentials) Inherit(parent BasicCredentials) {
if c.Username == "" {
c.Username = parent.Username
}
if c.Password == "" {
c.Password = parent.Password
}
}

type RegistryMirror struct {
Host string `json:"host,omitempty"`

Expand Down Expand Up @@ -431,6 +464,11 @@ type MetadataField struct {
}

type GetParams struct {
// Allow overriding credentials during put events, primarily beneficial for
// using short-lived credentials for cloud environments.
BasicCredentials
AwsCredentials

RawFormat string `json:"format"`
SkipDownload bool `json:"skip_download"`
}
Expand All @@ -444,6 +482,11 @@ func (p GetParams) Format() string {
}

type PutParams struct {
// Allow overriding credentials during put events, primarily beneficial for
// using short-lived credentials for cloud environments.
BasicCredentials
AwsCredentials

// Path to an OCI image tarball to push.
Image string `json:"image"`

Expand Down