mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-12 15:50:15 -08:00
feat(cli): add trivy auth (#7664)
Signed-off-by: knqyf263 <knqyf263@gmail.com> Co-authored-by: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com>
This commit is contained in:
@@ -12,9 +12,9 @@ Trivy_container_scanning:
|
|||||||
before_script:
|
before_script:
|
||||||
- export TRIVY_VERSION=${TRIVY_VERSION:-v0.19.2}
|
- export TRIVY_VERSION=${TRIVY_VERSION:-v0.19.2}
|
||||||
- apk add --no-cache curl docker-cli
|
- apk add --no-cache curl docker-cli
|
||||||
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
|
|
||||||
- curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin ${TRIVY_VERSION}
|
- curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin ${TRIVY_VERSION}
|
||||||
- curl -sSL -o /tmp/trivy-gitlab.tpl https://github.com/aquasecurity/trivy/raw/${TRIVY_VERSION}/contrib/gitlab.tpl
|
- curl -sSL -o /tmp/trivy-gitlab.tpl https://github.com/aquasecurity/trivy/raw/${TRIVY_VERSION}/contrib/gitlab.tpl
|
||||||
|
- trivy auth login --username "$CI_REGISTRY_USER" --password "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
|
||||||
script:
|
script:
|
||||||
- trivy --exit-code 0 --cache-dir .trivycache/ --no-progress --format template --template "@/tmp/trivy-gitlab.tpl" -o gl-container-scanning-report.json $IMAGE
|
- trivy --exit-code 0 --cache-dir .trivycache/ --no-progress --format template --template "@/tmp/trivy-gitlab.tpl" -o gl-container-scanning-report.json $IMAGE
|
||||||
cache:
|
cache:
|
||||||
|
|||||||
@@ -1,13 +1,30 @@
|
|||||||
Trivy can download images from a private registry without the need for installing Docker or any other 3rd party tools.
|
Trivy can download images from a private registry without the need for installing Docker or any other 3rd party tools.
|
||||||
This makes it easy to run within a CI process.
|
This makes it easy to run within a CI process.
|
||||||
|
|
||||||
## Credential
|
## Login
|
||||||
To use Trivy with private images, simply install it and provide your credentials:
|
You can log in to a private registry using the `trivy auth login` command.
|
||||||
|
It uses the Docker configuration file (`~/.docker/config.json`) to store the credentials under the hood, and the configuration file path can be configured by `DOCKER_CONFIG` environment variable.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ cat ~/my_password.txt | trivy auth login --username foo --password-stdin ghcr.io
|
||||||
|
$ trivy image ghcr.io/your/private_image
|
||||||
|
```
|
||||||
|
|
||||||
|
## Passing Credentials
|
||||||
|
You can also provide your credentials when scanning.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ TRIVY_USERNAME=YOUR_USERNAME TRIVY_PASSWORD=YOUR_PASSWORD trivy image YOUR_PRIVATE_IMAGE
|
$ TRIVY_USERNAME=YOUR_USERNAME TRIVY_PASSWORD=YOUR_PASSWORD trivy image YOUR_PRIVATE_IMAGE
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
When passing credentials via environment variables or CLI flags, Trivy will attempt to use these credentials for all registries encountered during scanning, regardless of the target registry.
|
||||||
|
This can potentially lead to unintended credential exposure.
|
||||||
|
To mitigate this risk:
|
||||||
|
|
||||||
|
1. Set credentials cautiously and only when necessary.
|
||||||
|
2. Prefer using `trivy auth config` to pre-configure credentials with specific registries, which ensures credentials are only sent to appropriate registries.
|
||||||
|
|
||||||
Trivy also supports providing credentials through CLI flags:
|
Trivy also supports providing credentials through CLI flags:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
@@ -17,6 +34,7 @@ $ TRIVY_PASSWORD=YOUR_PASSWORD trivy image --username YOUR_USERNAME YOUR_PRIVATE
|
|||||||
!!! warning
|
!!! warning
|
||||||
The CLI flag `--password` is available, but its use is not recommended for security reasons.
|
The CLI flag `--password` is available, but its use is not recommended for security reasons.
|
||||||
|
|
||||||
|
|
||||||
You can also store your credentials in `trivy.yaml`.
|
You can also store your credentials in `trivy.yaml`.
|
||||||
For more information, please refer to [the documentation](../../references/configuration/config-file.md).
|
For more information, please refer to [the documentation](../../references/configuration/config-file.md).
|
||||||
|
|
||||||
@@ -35,15 +53,5 @@ In the example above, Trivy attempts to use two pairs of credentials:
|
|||||||
|
|
||||||
Please note that the number of usernames and passwords must be the same.
|
Please note that the number of usernames and passwords must be the same.
|
||||||
|
|
||||||
## docker login
|
|
||||||
If you have Docker configured locally and have set up the credentials, Trivy can access them.
|
|
||||||
|
|
||||||
```shell
|
|
||||||
$ docker login ghcr.io
|
|
||||||
Username:
|
|
||||||
Password:
|
|
||||||
$ trivy image ghcr.io/your/private_image
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
`docker login` can be used with any container runtime, such as Podman.
|
`--password-stdin` doesn't support comma-separated passwords.
|
||||||
@@ -43,6 +43,7 @@ trivy [global flags] command [flags] target
|
|||||||
|
|
||||||
### SEE ALSO
|
### SEE ALSO
|
||||||
|
|
||||||
|
* [trivy auth](trivy_auth.md) - Authentication
|
||||||
* [trivy clean](trivy_clean.md) - Remove cached files
|
* [trivy clean](trivy_clean.md) - Remove cached files
|
||||||
* [trivy config](trivy_config.md) - Scan config files for misconfigurations
|
* [trivy config](trivy_config.md) - Scan config files for misconfigurations
|
||||||
* [trivy convert](trivy_convert.md) - Convert Trivy JSON report into a different format
|
* [trivy convert](trivy_convert.md) - Convert Trivy JSON report into a different format
|
||||||
|
|||||||
29
docs/docs/references/configuration/cli/trivy_auth.md
Normal file
29
docs/docs/references/configuration/cli/trivy_auth.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
## trivy auth
|
||||||
|
|
||||||
|
Authentication
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
```
|
||||||
|
-h, --help help for auth
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options inherited from parent commands
|
||||||
|
|
||||||
|
```
|
||||||
|
--cache-dir string cache directory (default "/path/to/cache")
|
||||||
|
-c, --config string config path (default "trivy.yaml")
|
||||||
|
-d, --debug debug mode
|
||||||
|
--generate-default-config write the default config to trivy-default.yaml
|
||||||
|
--insecure allow insecure server connections
|
||||||
|
-q, --quiet suppress progress bar and log output
|
||||||
|
--timeout duration timeout (default 5m0s)
|
||||||
|
-v, --version show version
|
||||||
|
```
|
||||||
|
|
||||||
|
### SEE ALSO
|
||||||
|
|
||||||
|
* [trivy](trivy.md) - Unified security scanner
|
||||||
|
* [trivy auth login](trivy_auth_login.md) - Log in to a registry
|
||||||
|
* [trivy auth logout](trivy_auth_logout.md) - Log out of a registry
|
||||||
|
|
||||||
41
docs/docs/references/configuration/cli/trivy_auth_login.md
Normal file
41
docs/docs/references/configuration/cli/trivy_auth_login.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
## trivy auth login
|
||||||
|
|
||||||
|
Log in to a registry
|
||||||
|
|
||||||
|
```
|
||||||
|
trivy auth login SERVER [flags]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
# Log in to reg.example.com
|
||||||
|
cat ~/my_password.txt | trivy auth login --username foo --password-stdin reg.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
```
|
||||||
|
-h, --help help for login
|
||||||
|
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
|
||||||
|
--password-stdin password from stdin. Comma-separated passwords are not supported.
|
||||||
|
--username strings username. Comma-separated usernames allowed.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options inherited from parent commands
|
||||||
|
|
||||||
|
```
|
||||||
|
--cache-dir string cache directory (default "/path/to/cache")
|
||||||
|
-c, --config string config path (default "trivy.yaml")
|
||||||
|
-d, --debug debug mode
|
||||||
|
--generate-default-config write the default config to trivy-default.yaml
|
||||||
|
--insecure allow insecure server connections
|
||||||
|
-q, --quiet suppress progress bar and log output
|
||||||
|
--timeout duration timeout (default 5m0s)
|
||||||
|
-v, --version show version
|
||||||
|
```
|
||||||
|
|
||||||
|
### SEE ALSO
|
||||||
|
|
||||||
|
* [trivy auth](trivy_auth.md) - Authentication
|
||||||
|
|
||||||
38
docs/docs/references/configuration/cli/trivy_auth_logout.md
Normal file
38
docs/docs/references/configuration/cli/trivy_auth_logout.md
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
## trivy auth logout
|
||||||
|
|
||||||
|
Log out of a registry
|
||||||
|
|
||||||
|
```
|
||||||
|
trivy auth logout SERVER [flags]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
# Log out of reg.example.com
|
||||||
|
trivy auth logout reg.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
```
|
||||||
|
-h, --help help for logout
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options inherited from parent commands
|
||||||
|
|
||||||
|
```
|
||||||
|
--cache-dir string cache directory (default "/path/to/cache")
|
||||||
|
-c, --config string config path (default "trivy.yaml")
|
||||||
|
-d, --debug debug mode
|
||||||
|
--generate-default-config write the default config to trivy-default.yaml
|
||||||
|
--insecure allow insecure server connections
|
||||||
|
-q, --quiet suppress progress bar and log output
|
||||||
|
--timeout duration timeout (default 5m0s)
|
||||||
|
-v, --version show version
|
||||||
|
```
|
||||||
|
|
||||||
|
### SEE ALSO
|
||||||
|
|
||||||
|
* [trivy auth](trivy_auth.md) - Authentication
|
||||||
|
|
||||||
@@ -39,6 +39,7 @@ trivy config [flags] DIR
|
|||||||
-o, --output string output file name
|
-o, --output string output file name
|
||||||
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
|
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
|
||||||
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
|
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
|
||||||
|
--password-stdin password from stdin. Comma-separated passwords are not supported.
|
||||||
--redis-ca string redis ca file location, if using redis as cache backend
|
--redis-ca string redis ca file location, if using redis as cache backend
|
||||||
--redis-cert string redis certificate file location, if using redis as cache backend
|
--redis-cert string redis certificate file location, if using redis as cache backend
|
||||||
--redis-key string redis key file location, if using redis as cache backend
|
--redis-key string redis key file location, if using redis as cache backend
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ trivy filesystem [flags] PATH
|
|||||||
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
|
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
|
||||||
--parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5)
|
--parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5)
|
||||||
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
|
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
|
||||||
|
--password-stdin password from stdin. Comma-separated passwords are not supported.
|
||||||
--pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect])
|
--pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect])
|
||||||
--pkg-types strings list of package types (os,library) (default [os,library])
|
--pkg-types strings list of package types (os,library) (default [os,library])
|
||||||
--redis-ca string redis ca file location, if using redis as cache backend
|
--redis-ca string redis ca file location, if using redis as cache backend
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ trivy image [flags] IMAGE_NAME
|
|||||||
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
|
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
|
||||||
--parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5)
|
--parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5)
|
||||||
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
|
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
|
||||||
|
--password-stdin password from stdin. Comma-separated passwords are not supported.
|
||||||
--pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect])
|
--pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect])
|
||||||
--pkg-types strings list of package types (os,library) (default [os,library])
|
--pkg-types strings list of package types (os,library) (default [os,library])
|
||||||
--platform string set platform in the form os/arch if image is multi-platform capable
|
--platform string set platform in the form os/arch if image is multi-platform capable
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ trivy kubernetes [flags] [CONTEXT]
|
|||||||
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
|
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
|
||||||
--parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5)
|
--parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5)
|
||||||
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
|
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
|
||||||
|
--password-stdin password from stdin. Comma-separated passwords are not supported.
|
||||||
--pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect])
|
--pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect])
|
||||||
--pkg-types strings list of package types (os,library) (default [os,library])
|
--pkg-types strings list of package types (os,library) (default [os,library])
|
||||||
--qps float specify the maximum QPS to the master from this client (default 5)
|
--qps float specify the maximum QPS to the master from this client (default 5)
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ trivy repository [flags] (REPO_PATH | REPO_URL)
|
|||||||
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
|
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
|
||||||
--parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5)
|
--parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5)
|
||||||
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
|
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
|
||||||
|
--password-stdin password from stdin. Comma-separated passwords are not supported.
|
||||||
--pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect])
|
--pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect])
|
||||||
--pkg-types strings list of package types (os,library) (default [os,library])
|
--pkg-types strings list of package types (os,library) (default [os,library])
|
||||||
--redis-ca string redis ca file location, if using redis as cache backend
|
--redis-ca string redis ca file location, if using redis as cache backend
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ trivy rootfs [flags] ROOTDIR
|
|||||||
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
|
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
|
||||||
--parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5)
|
--parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5)
|
||||||
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
|
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
|
||||||
|
--password-stdin password from stdin. Comma-separated passwords are not supported.
|
||||||
--pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect])
|
--pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect])
|
||||||
--pkg-types strings list of package types (os,library) (default [os,library])
|
--pkg-types strings list of package types (os,library) (default [os,library])
|
||||||
--redis-ca string redis ca file location, if using redis as cache backend
|
--redis-ca string redis ca file location, if using redis as cache backend
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ trivy sbom [flags] SBOM_PATH
|
|||||||
-o, --output string output file name
|
-o, --output string output file name
|
||||||
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
|
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
|
||||||
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
|
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
|
||||||
|
--password-stdin password from stdin. Comma-separated passwords are not supported.
|
||||||
--pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect])
|
--pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect])
|
||||||
--pkg-types strings list of package types (os,library) (default [os,library])
|
--pkg-types strings list of package types (os,library) (default [os,library])
|
||||||
--redis-ca string redis ca file location, if using redis as cache backend
|
--redis-ca string redis ca file location, if using redis as cache backend
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ trivy server [flags]
|
|||||||
--module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules")
|
--module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules")
|
||||||
--no-progress suppress progress bar
|
--no-progress suppress progress bar
|
||||||
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
|
--password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.
|
||||||
|
--password-stdin password from stdin. Comma-separated passwords are not supported.
|
||||||
--redis-ca string redis ca file location, if using redis as cache backend
|
--redis-ca string redis ca file location, if using redis as cache backend
|
||||||
--redis-cert string redis certificate file location, if using redis as cache backend
|
--redis-cert string redis certificate file location, if using redis as cache backend
|
||||||
--redis-key string redis key file location, if using redis as cache backend
|
--redis-key string redis key file location, if using redis as cache backend
|
||||||
|
|||||||
@@ -461,6 +461,9 @@ registry:
|
|||||||
# Same as '--password'
|
# Same as '--password'
|
||||||
password: []
|
password: []
|
||||||
|
|
||||||
|
# Same as '--password-stdin'
|
||||||
|
password-stdin: false
|
||||||
|
|
||||||
# Same as '--registry-token'
|
# Same as '--registry-token'
|
||||||
token: ""
|
token: ""
|
||||||
|
|
||||||
|
|||||||
@@ -297,7 +297,7 @@ Trivy supports registries that comply with the following specifications.
|
|||||||
- [Docker Registry HTTP API V2](https://docs.docker.com/registry/spec/api/)
|
- [Docker Registry HTTP API V2](https://docs.docker.com/registry/spec/api/)
|
||||||
- [OCI Distribution Specification](https://github.com/opencontainers/distribution-spec)
|
- [OCI Distribution Specification](https://github.com/opencontainers/distribution-spec)
|
||||||
|
|
||||||
You can configure credentials with `docker login`.
|
You can configure credentials with `trivy auth login`.
|
||||||
See [here](../advanced/private-registries/index.md) for the detail.
|
See [here](../advanced/private-registries/index.md) for the detail.
|
||||||
|
|
||||||
### Tar Files
|
### Tar Files
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -43,6 +43,7 @@ require (
|
|||||||
github.com/cheggaaa/pb/v3 v3.1.5
|
github.com/cheggaaa/pb/v3 v3.1.5
|
||||||
github.com/containerd/containerd v1.7.22
|
github.com/containerd/containerd v1.7.22
|
||||||
github.com/csaf-poc/csaf_distribution/v3 v3.0.0
|
github.com/csaf-poc/csaf_distribution/v3 v3.0.0
|
||||||
|
github.com/docker/cli v27.2.1+incompatible
|
||||||
github.com/docker/docker v27.3.1+incompatible
|
github.com/docker/docker v27.3.1+incompatible
|
||||||
github.com/docker/go-connections v0.5.0
|
github.com/docker/go-connections v0.5.0
|
||||||
github.com/fatih/color v1.17.0
|
github.com/fatih/color v1.17.0
|
||||||
@@ -210,7 +211,6 @@ require (
|
|||||||
github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect
|
github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect
|
||||||
github.com/distribution/reference v0.6.0 // indirect
|
github.com/distribution/reference v0.6.0 // indirect
|
||||||
github.com/dlclark/regexp2 v1.4.0 // indirect
|
github.com/dlclark/regexp2 v1.4.0 // indirect
|
||||||
github.com/docker/cli v27.2.1+incompatible // indirect
|
|
||||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||||
github.com/docker/docker-credential-helpers v0.8.2 // indirect
|
github.com/docker/docker-credential-helpers v0.8.2 // indirect
|
||||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
|
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ type registryOption struct {
|
|||||||
Username string
|
Username string
|
||||||
Password string
|
Password string
|
||||||
RegistryToken bool
|
RegistryToken bool
|
||||||
|
AuthLogin bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRegistry(t *testing.T) {
|
func TestRegistry(t *testing.T) {
|
||||||
@@ -164,7 +165,6 @@ func TestRegistry(t *testing.T) {
|
|||||||
imageFile: "testdata/fixtures/images/alpine-310.tar.gz",
|
imageFile: "testdata/fixtures/images/alpine-310.tar.gz",
|
||||||
os: "alpine 3.10.2",
|
os: "alpine 3.10.2",
|
||||||
option: registryOption{
|
option: registryOption{
|
||||||
AuthURL: authURL,
|
|
||||||
Username: authUsername,
|
Username: authUsername,
|
||||||
Password: authPassword,
|
Password: authPassword,
|
||||||
},
|
},
|
||||||
@@ -183,13 +183,24 @@ func TestRegistry(t *testing.T) {
|
|||||||
},
|
},
|
||||||
golden: "testdata/alpine-310.json.golden",
|
golden: "testdata/alpine-310.json.golden",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "authenticate with 'trivy auth login'",
|
||||||
|
imageName: "alpine:3.10",
|
||||||
|
imageFile: "testdata/fixtures/images/alpine-310.tar.gz",
|
||||||
|
os: "alpine 3.10.2",
|
||||||
|
option: registryOption{
|
||||||
|
Username: authUsername,
|
||||||
|
Password: authPassword,
|
||||||
|
AuthLogin: true,
|
||||||
|
},
|
||||||
|
golden: "testdata/alpine-310.json.golden",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "amazonlinux 2",
|
name: "amazonlinux 2",
|
||||||
imageName: "amazonlinux:2",
|
imageName: "amazonlinux:2",
|
||||||
imageFile: "testdata/fixtures/images/amazon-2.tar.gz",
|
imageFile: "testdata/fixtures/images/amazon-2.tar.gz",
|
||||||
os: "amazon 2 (Karoo)",
|
os: "amazon 2 (Karoo)",
|
||||||
option: registryOption{
|
option: registryOption{
|
||||||
AuthURL: authURL,
|
|
||||||
Username: authUsername,
|
Username: authUsername,
|
||||||
Password: authPassword,
|
Password: authPassword,
|
||||||
},
|
},
|
||||||
@@ -201,7 +212,6 @@ func TestRegistry(t *testing.T) {
|
|||||||
imageFile: "testdata/fixtures/images/debian-buster.tar.gz",
|
imageFile: "testdata/fixtures/images/debian-buster.tar.gz",
|
||||||
os: "debian 10.1",
|
os: "debian 10.1",
|
||||||
option: registryOption{
|
option: registryOption{
|
||||||
AuthURL: authURL,
|
|
||||||
Username: authUsername,
|
Username: authUsername,
|
||||||
Password: authPassword,
|
Password: authPassword,
|
||||||
},
|
},
|
||||||
@@ -226,6 +236,7 @@ func TestRegistry(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
osArgs, err := scan(t, imageRef, baseDir, tt.option)
|
osArgs, err := scan(t, imageRef, baseDir, tt.option)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Run Trivy
|
// Run Trivy
|
||||||
runTest(t, osArgs, tt.golden, "", types.FormatJSON, runOptions{
|
runTest(t, osArgs, tt.golden, "", types.FormatJSON, runOptions{
|
||||||
@@ -262,7 +273,7 @@ func scan(t *testing.T, imageRef name.Reference, baseDir string, opt registryOpt
|
|||||||
"json",
|
"json",
|
||||||
"--image-src",
|
"--image-src",
|
||||||
"remote",
|
"remote",
|
||||||
"--skip-update",
|
"--skip-db-update",
|
||||||
imageRef.Name(),
|
imageRef.Name(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,14 +284,30 @@ func setupEnv(t *testing.T, imageRef name.Reference, baseDir string, opt registr
|
|||||||
t.Setenv("TRIVY_INSECURE", "true")
|
t.Setenv("TRIVY_INSECURE", "true")
|
||||||
|
|
||||||
if opt.Username != "" && opt.Password != "" {
|
if opt.Username != "" && opt.Password != "" {
|
||||||
if opt.RegistryToken {
|
switch {
|
||||||
|
case opt.RegistryToken:
|
||||||
// Get a registry token in advance
|
// Get a registry token in advance
|
||||||
token, err := requestRegistryToken(imageRef, baseDir, opt)
|
token, err := requestRegistryToken(imageRef, baseDir, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t.Setenv("TRIVY_REGISTRY_TOKEN", token)
|
t.Setenv("TRIVY_REGISTRY_TOKEN", token)
|
||||||
} else {
|
case opt.AuthLogin:
|
||||||
|
t.Setenv("DOCKER_CONFIG", t.TempDir())
|
||||||
|
err := execute([]string{
|
||||||
|
"auth",
|
||||||
|
"login",
|
||||||
|
"--username",
|
||||||
|
opt.Username,
|
||||||
|
"--password",
|
||||||
|
opt.Password,
|
||||||
|
"--insecure",
|
||||||
|
imageRef.Context().RegistryStr(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
t.Setenv("TRIVY_USERNAME", opt.Username)
|
t.Setenv("TRIVY_USERNAME", opt.Username)
|
||||||
t.Setenv("TRIVY_PASSWORD", opt.Password)
|
t.Setenv("TRIVY_PASSWORD", opt.Password)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,6 +158,10 @@ nav:
|
|||||||
- Configuration:
|
- Configuration:
|
||||||
- CLI:
|
- CLI:
|
||||||
- Overview: docs/references/configuration/cli/trivy.md
|
- Overview: docs/references/configuration/cli/trivy.md
|
||||||
|
- Auth:
|
||||||
|
- Auth: docs/references/configuration/cli/trivy_auth.md
|
||||||
|
- Auth Login: docs/references/configuration/cli/trivy_auth_login.md
|
||||||
|
- Auth Logout: docs/references/configuration/cli/trivy_auth_logout.md
|
||||||
- Clean: docs/references/configuration/cli/trivy_clean.md
|
- Clean: docs/references/configuration/cli/trivy_clean.md
|
||||||
- Config: docs/references/configuration/cli/trivy_config.md
|
- Config: docs/references/configuration/cli/trivy_config.md
|
||||||
- Convert: docs/references/configuration/cli/trivy_convert.md
|
- Convert: docs/references/configuration/cli/trivy_convert.md
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
|
|
||||||
"github.com/aquasecurity/trivy/pkg/cache"
|
"github.com/aquasecurity/trivy/pkg/cache"
|
||||||
"github.com/aquasecurity/trivy/pkg/commands/artifact"
|
"github.com/aquasecurity/trivy/pkg/commands/artifact"
|
||||||
|
"github.com/aquasecurity/trivy/pkg/commands/auth"
|
||||||
"github.com/aquasecurity/trivy/pkg/commands/clean"
|
"github.com/aquasecurity/trivy/pkg/commands/clean"
|
||||||
"github.com/aquasecurity/trivy/pkg/commands/convert"
|
"github.com/aquasecurity/trivy/pkg/commands/convert"
|
||||||
"github.com/aquasecurity/trivy/pkg/commands/server"
|
"github.com/aquasecurity/trivy/pkg/commands/server"
|
||||||
@@ -99,6 +100,7 @@ func NewApp() *cobra.Command {
|
|||||||
NewVersionCommand(globalFlags),
|
NewVersionCommand(globalFlags),
|
||||||
NewVMCommand(globalFlags),
|
NewVMCommand(globalFlags),
|
||||||
NewCleanCommand(globalFlags),
|
NewCleanCommand(globalFlags),
|
||||||
|
NewAuthCommand(globalFlags),
|
||||||
NewVEXCommand(globalFlags),
|
NewVEXCommand(globalFlags),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1233,6 +1235,62 @@ func NewCleanCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
|
|||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewAuthCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "auth [flags]",
|
||||||
|
GroupID: groupUtility,
|
||||||
|
Short: "Authentication",
|
||||||
|
SilenceErrors: true,
|
||||||
|
SilenceUsage: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
loginFlags := &flag.Flags{
|
||||||
|
GlobalFlagGroup: globalFlags,
|
||||||
|
RegistryFlagGroup: flag.NewRegistryFlagGroup(),
|
||||||
|
}
|
||||||
|
loginFlags.RegistryFlagGroup.RegistryToken = nil // disable '--registry-token'
|
||||||
|
loginCmd := &cobra.Command{
|
||||||
|
Use: "login SERVER",
|
||||||
|
Short: "Log in to a registry",
|
||||||
|
SilenceErrors: true,
|
||||||
|
SilenceUsage: true,
|
||||||
|
Example: ` # Log in to reg.example.com
|
||||||
|
cat ~/my_password.txt | trivy auth login --username foo --password-stdin reg.example.com`,
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if err := loginFlags.Bind(cmd); err != nil {
|
||||||
|
return xerrors.Errorf("flag bind error: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
loginOpts, err := loginFlags.ToOptions(args)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("flag error: %w", err)
|
||||||
|
}
|
||||||
|
return auth.Login(cmd.Context(), args[0], loginOpts)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
logoutCmd := &cobra.Command{
|
||||||
|
Use: "logout SERVER",
|
||||||
|
Short: "Log out of a registry",
|
||||||
|
SilenceErrors: true,
|
||||||
|
SilenceUsage: true,
|
||||||
|
Example: ` # Log out of reg.example.com
|
||||||
|
trivy auth logout reg.example.com`,
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return auth.Logout(cmd.Context(), args[0])
|
||||||
|
},
|
||||||
|
}
|
||||||
|
loginFlags.AddFlags(loginCmd)
|
||||||
|
cmd.AddCommand(loginCmd, logoutCmd)
|
||||||
|
|
||||||
|
cmd.SetFlagErrorFunc(flagErrorFunc)
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
func NewVEXCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
|
func NewVEXCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
|
||||||
vexFlags := &flag.Flags{
|
vexFlags := &flag.Flags{
|
||||||
GlobalFlagGroup: globalFlags,
|
GlobalFlagGroup: globalFlags,
|
||||||
|
|||||||
109
pkg/commands/auth/run.go
Normal file
109
pkg/commands/auth/run.go
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/docker/cli/cli/config"
|
||||||
|
"github.com/docker/cli/cli/config/types"
|
||||||
|
"github.com/google/go-containerregistry/pkg/authn"
|
||||||
|
"github.com/google/go-containerregistry/pkg/name"
|
||||||
|
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||||
|
"github.com/google/go-containerregistry/pkg/v1/remote/transport"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/aquasecurity/trivy/pkg/flag"
|
||||||
|
"github.com/aquasecurity/trivy/pkg/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Login(ctx context.Context, registry string, opts flag.Options) error {
|
||||||
|
if len(opts.Credentials) == 0 {
|
||||||
|
return xerrors.New("username and password required")
|
||||||
|
} else if len(opts.Credentials) > 1 {
|
||||||
|
return xerrors.New("multiple credentials are not allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
reg, err := parseRegistry(registry, opts)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("failed to parse registry: %w", err)
|
||||||
|
}
|
||||||
|
serverAddress := reg.Name()
|
||||||
|
|
||||||
|
// Validate the credential
|
||||||
|
_, err = transport.NewWithContext(ctx, reg, &authn.Basic{
|
||||||
|
Username: opts.Credentials[0].Username,
|
||||||
|
Password: opts.Credentials[0].Password,
|
||||||
|
}, httpTransport(opts), []string{reg.Scope(transport.PullScope)})
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("failed to authenticate: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cf, err := config.Load(os.Getenv("DOCKER_CONFIG"))
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("failed to load docker config: %w", err)
|
||||||
|
}
|
||||||
|
creds := cf.GetCredentialsStore(serverAddress)
|
||||||
|
if serverAddress == name.DefaultRegistry {
|
||||||
|
serverAddress = authn.DefaultAuthKey
|
||||||
|
}
|
||||||
|
if err := creds.Store(types.AuthConfig{
|
||||||
|
ServerAddress: serverAddress,
|
||||||
|
Username: opts.Credentials[0].Username,
|
||||||
|
Password: opts.Credentials[0].Password,
|
||||||
|
}); err != nil {
|
||||||
|
return xerrors.Errorf("failed to store credentials: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cf.Save(); err != nil {
|
||||||
|
return xerrors.Errorf("failed to save docker config: %w", err)
|
||||||
|
}
|
||||||
|
log.Info("Login succeeded", log.FilePath(cf.Filename), log.String("username", opts.Credentials[0].Username))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Logout(_ context.Context, registry string) error {
|
||||||
|
reg, err := parseRegistry(registry, flag.Options{})
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("failed to parse registry: %w", err)
|
||||||
|
}
|
||||||
|
serverAddress := reg.Name()
|
||||||
|
|
||||||
|
cf, err := config.Load(os.Getenv("DOCKER_CONFIG"))
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("failed to load docker config: %w", err)
|
||||||
|
}
|
||||||
|
creds := cf.GetCredentialsStore(serverAddress)
|
||||||
|
if serverAddress == name.DefaultRegistry {
|
||||||
|
serverAddress = authn.DefaultAuthKey
|
||||||
|
}
|
||||||
|
if err := creds.Erase(serverAddress); err != nil {
|
||||||
|
return xerrors.Errorf("failed to delete credentials: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cf.Save(); err != nil {
|
||||||
|
return xerrors.Errorf("failed to save docker config: %w", err)
|
||||||
|
}
|
||||||
|
log.Info("Logged out", log.FilePath(cf.Filename))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRegistry(registry string, opts flag.Options) (name.Registry, error) {
|
||||||
|
var nameOpts []name.Option
|
||||||
|
if opts.Insecure {
|
||||||
|
nameOpts = append(nameOpts, name.Insecure)
|
||||||
|
}
|
||||||
|
reg, err := name.NewRegistry(registry, nameOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return name.Registry{}, xerrors.Errorf("failed to parse registry: %w", err)
|
||||||
|
}
|
||||||
|
return reg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func httpTransport(opts flag.Options) *http.Transport {
|
||||||
|
tr := remote.DefaultTransport.(*http.Transport).Clone()
|
||||||
|
if opts.Insecure {
|
||||||
|
tr.TLSClientConfig.InsecureSkipVerify = true
|
||||||
|
}
|
||||||
|
return tr
|
||||||
|
}
|
||||||
142
pkg/commands/auth/run_test.go
Normal file
142
pkg/commands/auth/run_test.go
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
package auth_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/samber/lo"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
testauth "github.com/aquasecurity/testdocker/auth"
|
||||||
|
"github.com/aquasecurity/testdocker/registry"
|
||||||
|
"github.com/aquasecurity/trivy/pkg/commands/auth"
|
||||||
|
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||||
|
"github.com/aquasecurity/trivy/pkg/flag"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLogin(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
registry string
|
||||||
|
opts flag.Options
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "single credential",
|
||||||
|
args: args{
|
||||||
|
opts: flag.Options{
|
||||||
|
RegistryOptions: flag.RegistryOptions{
|
||||||
|
Credentials: []types.Credential{
|
||||||
|
{
|
||||||
|
Username: "user",
|
||||||
|
Password: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple credentials",
|
||||||
|
args: args{
|
||||||
|
opts: flag.Options{
|
||||||
|
RegistryOptions: flag.RegistryOptions{
|
||||||
|
Credentials: []types.Credential{
|
||||||
|
{
|
||||||
|
Username: "user1",
|
||||||
|
Password: "pass1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Username: "user2",
|
||||||
|
Password: "pass2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: "multiple credentials are not allowed",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no credentials",
|
||||||
|
args: args{
|
||||||
|
registry: "auth.test",
|
||||||
|
opts: flag.Options{},
|
||||||
|
},
|
||||||
|
wantErr: "username and password required",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid registry",
|
||||||
|
args: args{
|
||||||
|
registry: "aaa://invalid.test",
|
||||||
|
opts: flag.Options{
|
||||||
|
RegistryOptions: flag.RegistryOptions{
|
||||||
|
Credentials: []types.Credential{
|
||||||
|
{
|
||||||
|
Username: "user",
|
||||||
|
Password: "pass",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: "registries must be valid RFC 3986 URI authorities",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
tr := registry.NewDockerRegistry(registry.Option{
|
||||||
|
Auth: testauth.Auth{
|
||||||
|
User: "user",
|
||||||
|
Password: "pass",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// Set the DOCKER_CONFIG environment variable to a temporary directory
|
||||||
|
// so that the test does not interfere with the user's configuration.
|
||||||
|
t.Setenv("DOCKER_CONFIG", filepath.Join(t.TempDir(), "config.json"))
|
||||||
|
|
||||||
|
reg := lo.Ternary(tt.args.registry == "", strings.TrimPrefix(tr.URL, "http://"), tt.args.registry)
|
||||||
|
err := auth.Login(context.Background(), reg, tt.args.opts)
|
||||||
|
if tt.wantErr != "" {
|
||||||
|
require.ErrorContains(t, err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogout(t *testing.T) {
|
||||||
|
// Set the DOCKER_CONFIG environment variable to a temporary directory
|
||||||
|
// so that the test does not interfere with the user's configuration.
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
t.Setenv("DOCKER_CONFIG", tmpDir)
|
||||||
|
|
||||||
|
t.Run("success", func(t *testing.T) {
|
||||||
|
configFile := filepath.Join(tmpDir, "config.json")
|
||||||
|
err := os.WriteFile(configFile, []byte(`{"auths": {"auth.test": {"auth": "dXNlcjpwYXNz"}}}`), 0600)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = auth.Logout(context.Background(), "auth.test")
|
||||||
|
require.NoError(t, err)
|
||||||
|
b, err := os.ReadFile(configFile)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, `{"auths": {}}`, string(b))
|
||||||
|
})
|
||||||
|
t.Run("not found", func(t *testing.T) {
|
||||||
|
err := auth.Logout(context.Background(), "notfound.test")
|
||||||
|
require.NoError(t, err) // Return an error if "credsStore" is "osxkeychain".
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("invalid registry", func(t *testing.T) {
|
||||||
|
err := auth.Logout(context.Background(), "aaa://invalid.test")
|
||||||
|
require.ErrorContains(t, err, "registries must be valid RFC 3986 URI authorities")
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package flag
|
package flag
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
@@ -19,6 +21,11 @@ var (
|
|||||||
ConfigName: "registry.password",
|
ConfigName: "registry.password",
|
||||||
Usage: "password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.",
|
Usage: "password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.",
|
||||||
}
|
}
|
||||||
|
PasswordStdinFlag = Flag[bool]{
|
||||||
|
Name: "password-stdin",
|
||||||
|
ConfigName: "registry.password-stdin",
|
||||||
|
Usage: "password from stdin. Comma-separated passwords are not supported.",
|
||||||
|
}
|
||||||
RegistryTokenFlag = Flag[string]{
|
RegistryTokenFlag = Flag[string]{
|
||||||
Name: "registry-token",
|
Name: "registry-token",
|
||||||
ConfigName: "registry.token",
|
ConfigName: "registry.token",
|
||||||
@@ -29,6 +36,7 @@ var (
|
|||||||
type RegistryFlagGroup struct {
|
type RegistryFlagGroup struct {
|
||||||
Username *Flag[[]string]
|
Username *Flag[[]string]
|
||||||
Password *Flag[[]string]
|
Password *Flag[[]string]
|
||||||
|
PasswordStdin *Flag[bool]
|
||||||
RegistryToken *Flag[string]
|
RegistryToken *Flag[string]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,6 +49,7 @@ func NewRegistryFlagGroup() *RegistryFlagGroup {
|
|||||||
return &RegistryFlagGroup{
|
return &RegistryFlagGroup{
|
||||||
Username: UsernameFlag.Clone(),
|
Username: UsernameFlag.Clone(),
|
||||||
Password: PasswordFlag.Clone(),
|
Password: PasswordFlag.Clone(),
|
||||||
|
PasswordStdin: PasswordStdinFlag.Clone(),
|
||||||
RegistryToken: RegistryTokenFlag.Clone(),
|
RegistryToken: RegistryTokenFlag.Clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,6 +62,7 @@ func (f *RegistryFlagGroup) Flags() []Flagger {
|
|||||||
return []Flagger{
|
return []Flagger{
|
||||||
f.Username,
|
f.Username,
|
||||||
f.Password,
|
f.Password,
|
||||||
|
f.PasswordStdin,
|
||||||
f.RegistryToken,
|
f.RegistryToken,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,8 +75,19 @@ func (f *RegistryFlagGroup) ToOptions() (RegistryOptions, error) {
|
|||||||
var credentials []types.Credential
|
var credentials []types.Credential
|
||||||
users := f.Username.Value()
|
users := f.Username.Value()
|
||||||
passwords := f.Password.Value()
|
passwords := f.Password.Value()
|
||||||
|
if f.PasswordStdin.Value() {
|
||||||
|
if len(passwords) != 0 {
|
||||||
|
return RegistryOptions{}, xerrors.New("'--password' and '--password-stdin' can't be used at the same time")
|
||||||
|
}
|
||||||
|
contents, err := io.ReadAll(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
return RegistryOptions{}, xerrors.Errorf("failed to read from stdin: %w", err)
|
||||||
|
}
|
||||||
|
// "--password-stdin" doesn't support comma-separated passwords
|
||||||
|
passwords = []string{strings.TrimRight(string(contents), "\r\n")}
|
||||||
|
}
|
||||||
if len(users) != len(passwords) {
|
if len(users) != len(passwords) {
|
||||||
return RegistryOptions{}, xerrors.New("the length of usernames and passwords must match")
|
return RegistryOptions{}, xerrors.New("the number of usernames and passwords must match")
|
||||||
}
|
}
|
||||||
for i, user := range users {
|
for i, user := range users {
|
||||||
credentials = append(credentials, types.Credential{
|
credentials = append(credentials, types.Credential{
|
||||||
|
|||||||
Reference in New Issue
Block a user