mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-21 23:00:42 -08:00
feat(image): custom docker host option (#3599)
Co-authored-by: knqyf263 <knqyf263@gmail.com>
This commit is contained in:
@@ -43,6 +43,7 @@ trivy image [flags] IMAGE_NAME
|
|||||||
--custom-headers strings custom headers in client mode
|
--custom-headers strings custom headers in client mode
|
||||||
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db")
|
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db")
|
||||||
--dependency-tree [EXPERIMENTAL] show dependency origin tree of vulnerable packages
|
--dependency-tree [EXPERIMENTAL] show dependency origin tree of vulnerable packages
|
||||||
|
--docker-host string unix domain socket path to use for docker scanning
|
||||||
--download-db-only download/update vulnerability database but don't run a scan
|
--download-db-only download/update vulnerability database but don't run a scan
|
||||||
--download-java-db-only download/update Java index database but don't run a scan
|
--download-java-db-only download/update Java index database but don't run a scan
|
||||||
--enable-modules strings [EXPERIMENTAL] module names to enable
|
--enable-modules strings [EXPERIMENTAL] module names to enable
|
||||||
|
|||||||
@@ -193,6 +193,15 @@ image:
|
|||||||
# Same as '--removed-pkgs'
|
# Same as '--removed-pkgs'
|
||||||
# Default is false
|
# Default is false
|
||||||
removed-pkgs: false
|
removed-pkgs: false
|
||||||
|
|
||||||
|
# Same as '--platform'
|
||||||
|
# Default is empty
|
||||||
|
platform:
|
||||||
|
|
||||||
|
docker:
|
||||||
|
# Same as '--docker-host'
|
||||||
|
# Default is empty
|
||||||
|
host:
|
||||||
```
|
```
|
||||||
|
|
||||||
## Vulnerability Options
|
## Vulnerability Options
|
||||||
|
|||||||
@@ -482,3 +482,9 @@ Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 1)
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
### Configure Docker daemon socket to connect to.
|
||||||
|
You can configure Docker daemon socket with `DOCKER_HOST` or `--docker-host`.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ trivy image --docker-host tcp://127.0.0.1:2375 YOUR_IMAGE
|
||||||
|
```
|
||||||
|
|||||||
@@ -760,7 +760,7 @@ func NewModuleCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("flag error: %w", err)
|
return xerrors.Errorf("flag error: %w", err)
|
||||||
}
|
}
|
||||||
return module.Install(cmd.Context(), opts.ModuleDir, repo, opts.Quiet, opts.Remote())
|
return module.Install(cmd.Context(), opts.ModuleDir, repo, opts.Quiet, opts.Registry())
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&cobra.Command{
|
&cobra.Command{
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
// initializeDockerScanner is for container image scanning in standalone mode
|
// initializeDockerScanner is for container image scanning in standalone mode
|
||||||
// e.g. dockerd, container registry, podman, etc.
|
// e.g. dockerd, container registry, podman, etc.
|
||||||
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache,
|
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache,
|
||||||
localArtifactCache cache.LocalArtifactCache, remoteOpt types.RemoteOptions, artifactOption artifact.Option) (
|
localArtifactCache cache.LocalArtifactCache, imageOpt types.ImageOptions, artifactOption artifact.Option) (
|
||||||
scanner.Scanner, func(), error) {
|
scanner.Scanner, func(), error) {
|
||||||
wire.Build(scanner.StandaloneDockerSet)
|
wire.Build(scanner.StandaloneDockerSet)
|
||||||
return scanner.Scanner{}, nil, nil
|
return scanner.Scanner{}, nil, nil
|
||||||
@@ -69,7 +69,7 @@ func initializeVMScanner(ctx context.Context, filePath string, artifactCache cac
|
|||||||
// initializeRemoteDockerScanner is for container image scanning in client/server mode
|
// initializeRemoteDockerScanner is for container image scanning in client/server mode
|
||||||
// e.g. dockerd, container registry, podman, etc.
|
// e.g. dockerd, container registry, podman, etc.
|
||||||
func initializeRemoteDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache,
|
func initializeRemoteDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache,
|
||||||
remoteScanOptions client.ScannerOption, remoteOpt types.RemoteOptions, artifactOption artifact.Option) (
|
remoteScanOptions client.ScannerOption, imageOpt types.ImageOptions, artifactOption artifact.Option) (
|
||||||
scanner.Scanner, func(), error) {
|
scanner.Scanner, func(), error) {
|
||||||
wire.Build(scanner.RemoteDockerSet)
|
wire.Build(scanner.RemoteDockerSet)
|
||||||
return scanner.Scanner{}, nil, nil
|
return scanner.Scanner{}, nil, nil
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/config"
|
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/config"
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
|
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/cache"
|
"github.com/aquasecurity/trivy/pkg/fanal/cache"
|
||||||
|
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||||
"github.com/aquasecurity/trivy/pkg/flag"
|
"github.com/aquasecurity/trivy/pkg/flag"
|
||||||
"github.com/aquasecurity/trivy/pkg/javadb"
|
"github.com/aquasecurity/trivy/pkg/javadb"
|
||||||
"github.com/aquasecurity/trivy/pkg/log"
|
"github.com/aquasecurity/trivy/pkg/log"
|
||||||
@@ -314,7 +315,7 @@ func (r *runner) initDB(ctx context.Context, opts flag.Options) error {
|
|||||||
|
|
||||||
// download the database file
|
// download the database file
|
||||||
noProgress := opts.Quiet || opts.NoProgress
|
noProgress := opts.Quiet || opts.NoProgress
|
||||||
if err := operation.DownloadDB(ctx, opts.AppVersion, opts.CacheDir, opts.DBRepository, noProgress, opts.SkipDBUpdate, opts.Remote()); err != nil {
|
if err := operation.DownloadDB(ctx, opts.AppVersion, opts.CacheDir, opts.DBRepository, noProgress, opts.SkipDBUpdate, opts.Registry()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -615,8 +616,6 @@ func initScannerConfig(opts flag.Options, cacheClient cache.Cache) (ScannerConfi
|
|||||||
fileChecksum = true
|
fileChecksum = true
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteOpts := opts.Remote()
|
|
||||||
|
|
||||||
return ScannerConfig{
|
return ScannerConfig{
|
||||||
Target: target,
|
Target: target,
|
||||||
ArtifactCache: cacheClient,
|
ArtifactCache: cacheClient,
|
||||||
@@ -633,18 +632,25 @@ func initScannerConfig(opts flag.Options, cacheClient cache.Cache) (ScannerConfi
|
|||||||
FilePatterns: opts.FilePatterns,
|
FilePatterns: opts.FilePatterns,
|
||||||
Offline: opts.OfflineScan,
|
Offline: opts.OfflineScan,
|
||||||
NoProgress: opts.NoProgress || opts.Quiet,
|
NoProgress: opts.NoProgress || opts.Quiet,
|
||||||
|
Insecure: opts.Insecure,
|
||||||
RepoBranch: opts.RepoBranch,
|
RepoBranch: opts.RepoBranch,
|
||||||
RepoCommit: opts.RepoCommit,
|
RepoCommit: opts.RepoCommit,
|
||||||
RepoTag: opts.RepoTag,
|
RepoTag: opts.RepoTag,
|
||||||
SBOMSources: opts.SBOMSources,
|
SBOMSources: opts.SBOMSources,
|
||||||
RekorURL: opts.RekorURL,
|
RekorURL: opts.RekorURL,
|
||||||
Platform: opts.Platform,
|
Platform: opts.Platform,
|
||||||
|
DockerHost: opts.DockerHost,
|
||||||
Slow: opts.Slow,
|
Slow: opts.Slow,
|
||||||
AWSRegion: opts.Region,
|
AWSRegion: opts.Region,
|
||||||
FileChecksum: fileChecksum,
|
FileChecksum: fileChecksum,
|
||||||
|
|
||||||
// For OCI registries
|
// For image scanning
|
||||||
RemoteOptions: remoteOpts,
|
ImageOption: ftypes.ImageOptions{
|
||||||
|
RegistryOptions: opts.Registry(),
|
||||||
|
DockerOptions: ftypes.DockerOptions{
|
||||||
|
Host: opts.DockerHost,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// For misconfiguration scanning
|
// For misconfiguration scanning
|
||||||
MisconfScannerOption: configScannerOptions,
|
MisconfScannerOption: configScannerOptions,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
// $ trivy image alpine:3.15
|
// $ trivy image alpine:3.15
|
||||||
func imageStandaloneScanner(ctx context.Context, conf ScannerConfig) (scanner.Scanner, func(), error) {
|
func imageStandaloneScanner(ctx context.Context, conf ScannerConfig) (scanner.Scanner, func(), error) {
|
||||||
s, cleanup, err := initializeDockerScanner(ctx, conf.Target, conf.ArtifactCache, conf.LocalArtifactCache,
|
s, cleanup, err := initializeDockerScanner(ctx, conf.Target, conf.ArtifactCache, conf.LocalArtifactCache,
|
||||||
conf.ArtifactOption.RemoteOptions, conf.ArtifactOption)
|
conf.ArtifactOption.ImageOption, conf.ArtifactOption)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize a docker scanner: %w", err)
|
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize a docker scanner: %w", err)
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,7 @@ func archiveStandaloneScanner(ctx context.Context, conf ScannerConfig) (scanner.
|
|||||||
func imageRemoteScanner(ctx context.Context, conf ScannerConfig) (
|
func imageRemoteScanner(ctx context.Context, conf ScannerConfig) (
|
||||||
scanner.Scanner, func(), error) {
|
scanner.Scanner, func(), error) {
|
||||||
s, cleanup, err := initializeRemoteDockerScanner(ctx, conf.Target, conf.ArtifactCache, conf.ServerOption,
|
s, cleanup, err := initializeRemoteDockerScanner(ctx, conf.Target, conf.ArtifactCache, conf.ServerOption,
|
||||||
conf.ArtifactOption.RemoteOptions, conf.ArtifactOption)
|
conf.ArtifactOption.ImageOption, conf.ArtifactOption)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return scanner.Scanner{}, nil, xerrors.Errorf("unable to initialize the remote docker scanner: %w", err)
|
return scanner.Scanner{}, nil, xerrors.Errorf("unable to initialize the remote docker scanner: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,14 +30,14 @@ import (
|
|||||||
|
|
||||||
// initializeDockerScanner is for container image scanning in standalone mode
|
// initializeDockerScanner is for container image scanning in standalone mode
|
||||||
// e.g. dockerd, container registry, podman, etc.
|
// e.g. dockerd, container registry, podman, etc.
|
||||||
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, remoteOpt types.RemoteOptions, artifactOption artifact.Option) (scanner.Scanner, func(), error) {
|
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, imageOpt types.ImageOptions, artifactOption artifact.Option) (scanner.Scanner, func(), error) {
|
||||||
applierApplier := applier.NewApplier(localArtifactCache)
|
applierApplier := applier.NewApplier(localArtifactCache)
|
||||||
detector := ospkg.Detector{}
|
detector := ospkg.Detector{}
|
||||||
config := db.Config{}
|
config := db.Config{}
|
||||||
client := vulnerability.NewClient(config)
|
client := vulnerability.NewClient(config)
|
||||||
localScanner := local.NewScanner(applierApplier, detector, client)
|
localScanner := local.NewScanner(applierApplier, detector, client)
|
||||||
v := _wireValue
|
v := _wireValue
|
||||||
typesImage, cleanup, err := image.NewContainerImage(ctx, imageName, remoteOpt, v...)
|
typesImage, cleanup, err := image.NewContainerImage(ctx, imageName, imageOpt, v...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return scanner.Scanner{}, nil, err
|
return scanner.Scanner{}, nil, err
|
||||||
}
|
}
|
||||||
@@ -140,11 +140,11 @@ func initializeVMScanner(ctx context.Context, filePath string, artifactCache cac
|
|||||||
|
|
||||||
// initializeRemoteDockerScanner is for container image scanning in client/server mode
|
// initializeRemoteDockerScanner is for container image scanning in client/server mode
|
||||||
// e.g. dockerd, container registry, podman, etc.
|
// e.g. dockerd, container registry, podman, etc.
|
||||||
func initializeRemoteDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, remoteScanOptions client.ScannerOption, remoteOpt types.RemoteOptions, artifactOption artifact.Option) (scanner.Scanner, func(), error) {
|
func initializeRemoteDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, remoteScanOptions client.ScannerOption, imageOpt types.ImageOptions, artifactOption artifact.Option) (scanner.Scanner, func(), error) {
|
||||||
v := _wireValue2
|
v := _wireValue2
|
||||||
clientScanner := client.NewScanner(remoteScanOptions, v...)
|
clientScanner := client.NewScanner(remoteScanOptions, v...)
|
||||||
v2 := _wireValue3
|
v2 := _wireValue3
|
||||||
typesImage, cleanup, err := image.NewContainerImage(ctx, imageName, remoteOpt, v2...)
|
typesImage, cleanup, err := image.NewContainerImage(ctx, imageName, imageOpt, v2...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return scanner.Scanner{}, nil, err
|
return scanner.Scanner{}, nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ func (c Cache) ClearArtifacts() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DownloadDB downloads the DB
|
// DownloadDB downloads the DB
|
||||||
func DownloadDB(ctx context.Context, appVersion, cacheDir, dbRepository string, quiet, skipUpdate bool, opt types.RemoteOptions) error {
|
func DownloadDB(ctx context.Context, appVersion, cacheDir, dbRepository string, quiet, skipUpdate bool, opt types.RegistryOptions) error {
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
defer mu.Unlock()
|
defer mu.Unlock()
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func Run(ctx context.Context, opts flag.Options) (err error) {
|
|||||||
|
|
||||||
// download the database file
|
// download the database file
|
||||||
if err = operation.DownloadDB(ctx, opts.AppVersion, opts.CacheDir, opts.DBRepository,
|
if err = operation.DownloadDB(ctx, opts.AppVersion, opts.CacheDir, opts.DBRepository,
|
||||||
true, opts.SkipDBUpdate, opts.Remote()); err != nil {
|
true, opts.SkipDBUpdate, opts.Registry()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,6 +58,6 @@ func Run(ctx context.Context, opts flag.Options) (err error) {
|
|||||||
m.Register()
|
m.Register()
|
||||||
|
|
||||||
server := rpcServer.NewServer(opts.AppVersion, opts.Listen, opts.CacheDir, opts.Token, opts.TokenHeader,
|
server := rpcServer.NewServer(opts.AppVersion, opts.Listen, opts.CacheDir, opts.Token, opts.TokenHeader,
|
||||||
opts.DBRepository, opts.Remote())
|
opts.DBRepository, opts.Registry())
|
||||||
return server.ListenAndServe(cache, opts.SkipDBUpdate)
|
return server.ListenAndServe(cache, opts.SkipDBUpdate)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const (
|
|||||||
// Operation defines the DB operations
|
// Operation defines the DB operations
|
||||||
type Operation interface {
|
type Operation interface {
|
||||||
NeedsUpdate(cliVersion string, skip bool) (need bool, err error)
|
NeedsUpdate(cliVersion string, skip bool) (need bool, err error)
|
||||||
Download(ctx context.Context, dst string, opt types.RemoteOptions) (err error)
|
Download(ctx context.Context, dst string, opt types.RegistryOptions) (err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type options struct {
|
type options struct {
|
||||||
@@ -143,7 +143,7 @@ func (c *Client) isNewDB(meta metadata.Metadata) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Download downloads the DB file
|
// Download downloads the DB file
|
||||||
func (c *Client) Download(ctx context.Context, dst string, opt types.RemoteOptions) error {
|
func (c *Client) Download(ctx context.Context, dst string, opt types.RegistryOptions) error {
|
||||||
// Remove the metadata file under the cache directory before downloading DB
|
// Remove the metadata file under the cache directory before downloading DB
|
||||||
if err := c.metadata.Delete(); err != nil {
|
if err := c.metadata.Delete(); err != nil {
|
||||||
log.Logger.Debug("no metadata file")
|
log.Logger.Debug("no metadata file")
|
||||||
@@ -183,7 +183,7 @@ func (c *Client) updateDownloadedAt(dst string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) initOCIArtifact(opt types.RemoteOptions) (*oci.Artifact, error) {
|
func (c *Client) initOCIArtifact(opt types.RegistryOptions) (*oci.Artifact, error) {
|
||||||
if c.artifact != nil {
|
if c.artifact != nil {
|
||||||
return c.artifact, nil
|
return c.artifact, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ func TestClient_Download(t *testing.T) {
|
|||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
// Mock OCI artifact
|
// Mock OCI artifact
|
||||||
opt := ftypes.RemoteOptions{
|
opt := ftypes.RegistryOptions{
|
||||||
Insecure: false,
|
Insecure: false,
|
||||||
}
|
}
|
||||||
art, err := oci.NewArtifact("db", true, opt, oci.WithImage(img))
|
art, err := oci.NewArtifact("db", true, opt, oci.WithImage(img))
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ func (_m *MockOperation) ApplyDownloadExpectations(expectations []OperationDownl
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Download provides a mock function with given fields: ctx, dst
|
// Download provides a mock function with given fields: ctx, dst
|
||||||
func (_m *MockOperation) Download(ctx context.Context, dst string, opt types.RemoteOptions) error {
|
func (_m *MockOperation) Download(ctx context.Context, dst string, opt types.RegistryOptions) error {
|
||||||
ret := _m.Called(ctx, dst, opt)
|
ret := _m.Called(ctx, dst, opt)
|
||||||
|
|
||||||
var r0 error
|
var r0 error
|
||||||
|
|||||||
@@ -18,11 +18,13 @@ type Option struct {
|
|||||||
SkipDirs []string
|
SkipDirs []string
|
||||||
FilePatterns []string
|
FilePatterns []string
|
||||||
NoProgress bool
|
NoProgress bool
|
||||||
|
Insecure bool
|
||||||
Offline bool
|
Offline bool
|
||||||
AppDirs []string
|
AppDirs []string
|
||||||
SBOMSources []string
|
SBOMSources []string
|
||||||
RekorURL string
|
RekorURL string
|
||||||
Platform string
|
Platform string
|
||||||
|
DockerHost string
|
||||||
Slow bool // Lower CPU and memory
|
Slow bool // Lower CPU and memory
|
||||||
AWSRegion string
|
AWSRegion string
|
||||||
FileChecksum bool // For SPDX
|
FileChecksum bool // For SPDX
|
||||||
@@ -32,8 +34,8 @@ type Option struct {
|
|||||||
RepoCommit string
|
RepoCommit string
|
||||||
RepoTag string
|
RepoTag string
|
||||||
|
|
||||||
// For OCI registries
|
// For image scanning
|
||||||
types.RemoteOptions
|
ImageOption types.ImageOptions
|
||||||
|
|
||||||
MisconfScannerOption misconf.ScannerOption
|
MisconfScannerOption misconf.ScannerOption
|
||||||
SecretScannerOption analyzer.SecretScannerOption
|
SecretScannerOption analyzer.SecretScannerOption
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ func (a Artifact) inspectOCIReferrerSBOM(ctx context.Context) (ftypes.ArtifactRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fetch referrers
|
// Fetch referrers
|
||||||
index, err := remote.Referrers(ctx, digest, a.artifactOption.RemoteOptions)
|
index, err := remote.Referrers(ctx, digest, a.artifactOption.ImageOption.RegistryOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ftypes.ArtifactReference{}, xerrors.Errorf("unable to fetch referrers: %w", err)
|
return ftypes.ArtifactReference{}, xerrors.Errorf("unable to fetch referrers: %w", err)
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ func (a Artifact) inspectOCIReferrerSBOM(ctx context.Context) (ftypes.ArtifactRe
|
|||||||
func (a Artifact) parseReferrer(ctx context.Context, repo string, desc v1.Descriptor) (ftypes.ArtifactReference, error) {
|
func (a Artifact) parseReferrer(ctx context.Context, repo string, desc v1.Descriptor) (ftypes.ArtifactReference, error) {
|
||||||
const fileName string = "referrer.sbom"
|
const fileName string = "referrer.sbom"
|
||||||
repoName := fmt.Sprintf("%s@%s", repo, desc.Digest)
|
repoName := fmt.Sprintf("%s@%s", repo, desc.Digest)
|
||||||
referrer, err := oci.NewArtifact(repoName, true, a.artifactOption.RemoteOptions)
|
referrer, err := oci.NewArtifact(repoName, true, a.artifactOption.ImageOption.RegistryOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ftypes.ArtifactReference{}, xerrors.Errorf("OCI error: %w", err)
|
return ftypes.ArtifactReference{}, xerrors.Errorf("OCI error: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import (
|
|||||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func tryDockerDaemon(imageName string, ref name.Reference) (types.Image, func(), error) {
|
func tryDockerDaemon(imageName string, ref name.Reference, opt types.DockerOptions) (types.Image, func(), error) {
|
||||||
img, cleanup, err := daemon.DockerImage(ref)
|
img, cleanup, err := daemon.DockerImage(ref, opt.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,10 +11,19 @@ import (
|
|||||||
|
|
||||||
// DockerImage implements v1.Image by extending daemon.Image.
|
// DockerImage implements v1.Image by extending daemon.Image.
|
||||||
// The caller must call cleanup() to remove a temporary file.
|
// The caller must call cleanup() to remove a temporary file.
|
||||||
func DockerImage(ref name.Reference) (Image, func(), error) {
|
func DockerImage(ref name.Reference, host string) (Image, func(), error) {
|
||||||
cleanup := func() {}
|
cleanup := func() {}
|
||||||
|
|
||||||
c, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
opts := []client.Opt{
|
||||||
|
client.FromEnv,
|
||||||
|
client.WithAPIVersionNegotiation(),
|
||||||
|
}
|
||||||
|
if host != "" {
|
||||||
|
// adding host parameter to the last assuming it will pick up more preference
|
||||||
|
opts = append(opts, client.WithHost(host))
|
||||||
|
}
|
||||||
|
c, err := client.NewClientWithOpts(opts...)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, cleanup, xerrors.Errorf("failed to initialize a docker client: %w", err)
|
return nil, cleanup, xerrors.Errorf("failed to initialize a docker client: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ func TestDockerImage(t *testing.T) {
|
|||||||
ref, err := name.ParseReference(tt.imageName)
|
ref, err := name.ParseReference(tt.imageName)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, cleanup, err := DockerImage(ref)
|
_, cleanup, err := DockerImage(ref, "")
|
||||||
assert.Equal(t, tt.wantErr, err != nil, err)
|
assert.Equal(t, tt.wantErr, err != nil, err)
|
||||||
defer func() {
|
defer func() {
|
||||||
if cleanup != nil {
|
if cleanup != nil {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -17,18 +19,19 @@ import (
|
|||||||
"github.com/aquasecurity/testdocker/engine"
|
"github.com/aquasecurity/testdocker/engine"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
var imagePaths = map[string]string{
|
||||||
imagePaths := map[string]string{
|
"alpine:3.10": "../../test/testdata/alpine-310.tar.gz",
|
||||||
"alpine:3.10": "../../test/testdata/alpine-310.tar.gz",
|
"alpine:3.11": "../../test/testdata/alpine-311.tar.gz",
|
||||||
"alpine:3.11": "../../test/testdata/alpine-311.tar.gz",
|
"gcr.io/distroless/base": "../../test/testdata/distroless.tar.gz",
|
||||||
"gcr.io/distroless/base": "../../test/testdata/distroless.tar.gz",
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// for Docker
|
// for Docker
|
||||||
opt := engine.Option{
|
var opt = engine.Option{
|
||||||
APIVersion: "1.38",
|
APIVersion: "1.38",
|
||||||
ImagePaths: imagePaths,
|
ImagePaths: imagePaths,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
te := engine.NewDockerEngine(opt)
|
te := engine.NewDockerEngine(opt)
|
||||||
defer te.Close()
|
defer te.Close()
|
||||||
|
|
||||||
@@ -59,7 +62,7 @@ func Test_image_ConfigName(t *testing.T) {
|
|||||||
ref, err := name.ParseReference(tt.imageName)
|
ref, err := name.ParseReference(tt.imageName)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
img, cleanup, err := DockerImage(ref)
|
img, cleanup, err := DockerImage(ref, "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
@@ -70,6 +73,50 @@ func Test_image_ConfigName(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_image_ConfigNameWithCustomDockerHost(t *testing.T) {
|
||||||
|
|
||||||
|
ref, err := name.ParseReference("alpine:3.11")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
eo := engine.Option{
|
||||||
|
APIVersion: opt.APIVersion,
|
||||||
|
ImagePaths: opt.ImagePaths,
|
||||||
|
}
|
||||||
|
|
||||||
|
var dockerHostParam string
|
||||||
|
|
||||||
|
if runtime.GOOS != "windows" {
|
||||||
|
runtimeDir, err := ioutil.TempDir("", "daemon")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
dir := filepath.Join(runtimeDir, "image")
|
||||||
|
err = os.MkdirAll(dir, os.ModePerm)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
customDockerHost := filepath.Join(dir, "image-test-unix-socket.sock")
|
||||||
|
eo.UnixDomainSocket = customDockerHost
|
||||||
|
dockerHostParam = "unix://" + customDockerHost
|
||||||
|
}
|
||||||
|
|
||||||
|
te := engine.NewDockerEngine(eo)
|
||||||
|
defer te.Close()
|
||||||
|
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
dockerHostParam = te.Listener.Addr().Network() + "://" + te.Listener.Addr().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
img, cleanup, err := DockerImage(ref, dockerHostParam)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
conf, err := img.ConfigName()
|
||||||
|
assert.Equal(t, v1.Hash{
|
||||||
|
Algorithm: "sha256",
|
||||||
|
Hex: "a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72",
|
||||||
|
}, conf)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
func Test_image_ConfigFile(t *testing.T) {
|
func Test_image_ConfigFile(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -156,7 +203,7 @@ func Test_image_ConfigFile(t *testing.T) {
|
|||||||
ref, err := name.ParseReference(tt.imageName)
|
ref, err := name.ParseReference(tt.imageName)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
img, cleanup, err := DockerImage(ref)
|
img, cleanup, err := DockerImage(ref, "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
@@ -201,7 +248,7 @@ func Test_image_LayerByDiffID(t *testing.T) {
|
|||||||
ref, err := name.ParseReference(tt.imageName)
|
ref, err := name.ParseReference(tt.imageName)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
img, cleanup, err := DockerImage(ref)
|
img, cleanup, err := DockerImage(ref, "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
@@ -230,7 +277,7 @@ func Test_image_RawConfigFile(t *testing.T) {
|
|||||||
ref, err := name.ParseReference(tt.imageName)
|
ref, err := name.ParseReference(tt.imageName)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
img, cleanup, err := DockerImage(ref)
|
img, cleanup, err := DockerImage(ref, "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ func DisableRemote() Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContainerImage(ctx context.Context, imageName string, opt types.RemoteOptions, opts ...Option) (types.Image, func(), error) {
|
func NewContainerImage(ctx context.Context, imageName string, opt types.ImageOptions, opts ...Option) (types.Image, func(), error) {
|
||||||
o := &options{
|
o := &options{
|
||||||
dockerd: true,
|
dockerd: true,
|
||||||
podman: true,
|
podman: true,
|
||||||
@@ -57,7 +57,7 @@ func NewContainerImage(ctx context.Context, imageName string, opt types.RemoteOp
|
|||||||
|
|
||||||
var errs error
|
var errs error
|
||||||
var nameOpts []name.Option
|
var nameOpts []name.Option
|
||||||
if opt.Insecure {
|
if opt.RegistryOptions.Insecure {
|
||||||
nameOpts = append(nameOpts, name.Insecure)
|
nameOpts = append(nameOpts, name.Insecure)
|
||||||
}
|
}
|
||||||
ref, err := name.ParseReference(imageName, nameOpts...)
|
ref, err := name.ParseReference(imageName, nameOpts...)
|
||||||
@@ -67,7 +67,7 @@ func NewContainerImage(ctx context.Context, imageName string, opt types.RemoteOp
|
|||||||
|
|
||||||
// Try accessing Docker Daemon
|
// Try accessing Docker Daemon
|
||||||
if o.dockerd {
|
if o.dockerd {
|
||||||
img, cleanup, err := tryDockerDaemon(imageName, ref)
|
img, cleanup, err := tryDockerDaemon(imageName, ref, opt.DockerOptions)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// Return v1.Image if the image is found in Docker Engine
|
// Return v1.Image if the image is found in Docker Engine
|
||||||
return img, cleanup, nil
|
return img, cleanup, nil
|
||||||
@@ -97,7 +97,7 @@ func NewContainerImage(ctx context.Context, imageName string, opt types.RemoteOp
|
|||||||
|
|
||||||
// Try accessing Docker Registry
|
// Try accessing Docker Registry
|
||||||
if o.remote {
|
if o.remote {
|
||||||
img, err := tryRemote(ctx, imageName, ref, opt)
|
img, err := tryRemote(ctx, imageName, ref, opt.RegistryOptions)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// Return v1.Image if the image is found in a remote registry
|
// Return v1.Image if the image is found in a remote registry
|
||||||
return img, func() {}, nil
|
return img, func() {}, nil
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ func TestNewDockerImage(t *testing.T) {
|
|||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
imageName string
|
imageName string
|
||||||
option types.RemoteOptions
|
option types.ImageOptions
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -205,14 +205,16 @@ func TestNewDockerImage(t *testing.T) {
|
|||||||
name: "happy path with insecure Docker Registry",
|
name: "happy path with insecure Docker Registry",
|
||||||
args: args{
|
args: args{
|
||||||
imageName: fmt.Sprintf("%s/library/alpine:3.10", serverAddr),
|
imageName: fmt.Sprintf("%s/library/alpine:3.10", serverAddr),
|
||||||
option: types.RemoteOptions{
|
option: types.ImageOptions{
|
||||||
Credentials: []types.Credential{
|
RegistryOptions: types.RegistryOptions{
|
||||||
{
|
Credentials: []types.Credential{
|
||||||
Username: "test",
|
{
|
||||||
Password: "test",
|
Username: "test",
|
||||||
|
Password: "test",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
Insecure: true,
|
||||||
},
|
},
|
||||||
Insecure: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantID: "sha256:af341ccd2df8b0e2d67cf8dd32e087bfda4e5756ebd1c76bbf3efa0dc246590e",
|
wantID: "sha256:af341ccd2df8b0e2d67cf8dd32e087bfda4e5756ebd1c76bbf3efa0dc246590e",
|
||||||
@@ -331,7 +333,7 @@ func TestNewDockerImageWithPrivateRegistry(t *testing.T) {
|
|||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
imageName string
|
imageName string
|
||||||
option types.RemoteOptions
|
option types.ImageOptions
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -343,14 +345,16 @@ func TestNewDockerImageWithPrivateRegistry(t *testing.T) {
|
|||||||
name: "happy path with private Docker Registry",
|
name: "happy path with private Docker Registry",
|
||||||
args: args{
|
args: args{
|
||||||
imageName: fmt.Sprintf("%s/library/alpine:3.10", serverAddr),
|
imageName: fmt.Sprintf("%s/library/alpine:3.10", serverAddr),
|
||||||
option: types.RemoteOptions{
|
option: types.ImageOptions{
|
||||||
Credentials: []types.Credential{
|
RegistryOptions: types.RegistryOptions{
|
||||||
{
|
Credentials: []types.Credential{
|
||||||
Username: "test",
|
{
|
||||||
Password: "testpass",
|
Username: "test",
|
||||||
|
Password: "testpass",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
Insecure: true,
|
||||||
},
|
},
|
||||||
Insecure: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -358,9 +362,11 @@ func TestNewDockerImageWithPrivateRegistry(t *testing.T) {
|
|||||||
name: "happy path with registry token",
|
name: "happy path with registry token",
|
||||||
args: args{
|
args: args{
|
||||||
imageName: fmt.Sprintf("%s/library/alpine:3.10", serverAddr),
|
imageName: fmt.Sprintf("%s/library/alpine:3.10", serverAddr),
|
||||||
option: types.RemoteOptions{
|
option: types.ImageOptions{
|
||||||
RegistryToken: registryToken,
|
RegistryOptions: types.RegistryOptions{
|
||||||
Insecure: true,
|
RegistryToken: registryToken,
|
||||||
|
Insecure: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -375,9 +381,11 @@ func TestNewDockerImageWithPrivateRegistry(t *testing.T) {
|
|||||||
name: "sad path with invalid registry token",
|
name: "sad path with invalid registry token",
|
||||||
args: args{
|
args: args{
|
||||||
imageName: fmt.Sprintf("%s/library/alpine:3.11", serverAddr),
|
imageName: fmt.Sprintf("%s/library/alpine:3.11", serverAddr),
|
||||||
option: types.RemoteOptions{
|
option: types.ImageOptions{
|
||||||
RegistryToken: registryToken + "invalid",
|
RegistryOptions: types.RegistryOptions{
|
||||||
Insecure: true,
|
RegistryToken: registryToken + "invalid",
|
||||||
|
Insecure: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantErr: "signature is invalid",
|
wantErr: "signature is invalid",
|
||||||
@@ -501,7 +509,7 @@ func TestDockerPlatformArguments(t *testing.T) {
|
|||||||
serverAddr := tr.Listener.Addr().String()
|
serverAddr := tr.Listener.Addr().String()
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
option types.RemoteOptions
|
option types.ImageOptions
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -512,15 +520,17 @@ func TestDockerPlatformArguments(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "happy path with valid platform",
|
name: "happy path with valid platform",
|
||||||
args: args{
|
args: args{
|
||||||
option: types.RemoteOptions{
|
option: types.ImageOptions{
|
||||||
Credentials: []types.Credential{
|
RegistryOptions: types.RegistryOptions{
|
||||||
{
|
Credentials: []types.Credential{
|
||||||
Username: "test",
|
{
|
||||||
Password: "testpass",
|
Username: "test",
|
||||||
|
Password: "testpass",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
Insecure: true,
|
||||||
|
Platform: "arm/linux",
|
||||||
},
|
},
|
||||||
Insecure: true,
|
|
||||||
Platform: "arm/linux",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ type Registry struct {
|
|||||||
|
|
||||||
const azureURL = "azurecr.io"
|
const azureURL = "azurecr.io"
|
||||||
|
|
||||||
func (r *Registry) CheckOptions(domain string, _ types.RemoteOptions) error {
|
func (r *Registry) CheckOptions(domain string, _ types.RegistryOptions) error {
|
||||||
if !strings.HasSuffix(domain, azureURL) {
|
if !strings.HasSuffix(domain, azureURL) {
|
||||||
return xerrors.Errorf("Azure registry: %w", types.InvalidURLPattern)
|
return xerrors.Errorf("Azure registry: %w", types.InvalidURLPattern)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func TestRegistry_CheckOptions(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
r := azure.Registry{}
|
r := azure.Registry{}
|
||||||
err := r.CheckOptions(tt.domain, types.RemoteOptions{})
|
err := r.CheckOptions(tt.domain, types.RegistryOptions{})
|
||||||
if tt.wantErr != "" {
|
if tt.wantErr != "" {
|
||||||
assert.EqualError(t, err, tt.wantErr)
|
assert.EqualError(t, err, tt.wantErr)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ type ECR struct {
|
|||||||
Client ecriface.ECRAPI
|
Client ecriface.ECRAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSession(option types.RemoteOptions) (*session.Session, error) {
|
func getSession(option types.RegistryOptions) (*session.Session, error) {
|
||||||
// create custom credential information if option is valid
|
// create custom credential information if option is valid
|
||||||
if option.AWSSecretKey != "" && option.AWSAccessKey != "" && option.AWSRegion != "" {
|
if option.AWSSecretKey != "" && option.AWSAccessKey != "" && option.AWSRegion != "" {
|
||||||
return session.NewSessionWithOptions(
|
return session.NewSessionWithOptions(
|
||||||
@@ -45,7 +45,7 @@ func getSession(option types.RemoteOptions) (*session.Session, error) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ECR) CheckOptions(domain string, option types.RemoteOptions) error {
|
func (e *ECR) CheckOptions(domain string, option types.RegistryOptions) error {
|
||||||
if !strings.HasSuffix(domain, ecrURL) {
|
if !strings.HasSuffix(domain, ecrURL) {
|
||||||
return xerrors.Errorf("ECR : %w", types.InvalidURLPattern)
|
return xerrors.Errorf("ECR : %w", types.InvalidURLPattern)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func TestCheckOptions(t *testing.T) {
|
|||||||
|
|
||||||
for testname, v := range tests {
|
for testname, v := range tests {
|
||||||
a := &ECR{}
|
a := &ECR{}
|
||||||
err := a.CheckOptions(v.domain, types.RemoteOptions{})
|
err := a.CheckOptions(v.domain, types.RegistryOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.Is(err, v.wantErr) {
|
if !errors.Is(err, v.wantErr) {
|
||||||
t.Errorf("[%s]\nexpected error based on %v\nactual : %v", testname, v.wantErr, err)
|
t.Errorf("[%s]\nexpected error based on %v\nactual : %v", testname, v.wantErr, err)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const gcrURL = "gcr.io"
|
|||||||
// Google artifact registry
|
// Google artifact registry
|
||||||
const garURL = "docker.pkg.dev"
|
const garURL = "docker.pkg.dev"
|
||||||
|
|
||||||
func (g *Registry) CheckOptions(domain string, option types.RemoteOptions) error {
|
func (g *Registry) CheckOptions(domain string, option types.RegistryOptions) error {
|
||||||
if !strings.HasSuffix(domain, gcrURL) && !strings.HasSuffix(domain, garURL) {
|
if !strings.HasSuffix(domain, gcrURL) && !strings.HasSuffix(domain, garURL) {
|
||||||
return xerrors.Errorf("Google registry: %w", types.InvalidURLPattern)
|
return xerrors.Errorf("Google registry: %w", types.InvalidURLPattern)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,23 +13,21 @@ import (
|
|||||||
func TestCheckOptions(t *testing.T) {
|
func TestCheckOptions(t *testing.T) {
|
||||||
var tests = map[string]struct {
|
var tests = map[string]struct {
|
||||||
domain string
|
domain string
|
||||||
opt types.RemoteOptions
|
opt types.RegistryOptions
|
||||||
gcr *Registry
|
gcr *Registry
|
||||||
wantErr error
|
wantErr error
|
||||||
}{
|
}{
|
||||||
"InvalidURL": {
|
"InvalidURL": {
|
||||||
domain: "alpine:3.9",
|
domain: "alpine:3.9",
|
||||||
opt: types.RemoteOptions{},
|
|
||||||
wantErr: types.InvalidURLPattern,
|
wantErr: types.InvalidURLPattern,
|
||||||
},
|
},
|
||||||
"NoOption": {
|
"NoOption": {
|
||||||
domain: "gcr.io",
|
domain: "gcr.io",
|
||||||
opt: types.RemoteOptions{},
|
|
||||||
gcr: &Registry{domain: "gcr.io"},
|
gcr: &Registry{domain: "gcr.io"},
|
||||||
},
|
},
|
||||||
"CredOption": {
|
"CredOption": {
|
||||||
domain: "gcr.io",
|
domain: "gcr.io",
|
||||||
opt: types.RemoteOptions{GCPCredPath: "/path/to/file.json"},
|
opt: types.RegistryOptions{GCPCredPath: "/path/to/file.json"},
|
||||||
gcr: &Registry{
|
gcr: &Registry{
|
||||||
domain: "gcr.io",
|
domain: "gcr.io",
|
||||||
Store: store.NewGCRCredStore("/path/to/file.json"),
|
Store: store.NewGCRCredStore("/path/to/file.json"),
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Registry interface {
|
type Registry interface {
|
||||||
CheckOptions(domain string, option types.RemoteOptions) error
|
CheckOptions(domain string, option types.RegistryOptions) error
|
||||||
GetCredential(ctx context.Context) (string, string, error)
|
GetCredential(ctx context.Context) (string, string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ func RegisterRegistry(registry Registry) {
|
|||||||
registries = append(registries, registry)
|
registries = append(registries, registry)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetToken(ctx context.Context, domain string, opt types.RemoteOptions) (auth authn.Basic) {
|
func GetToken(ctx context.Context, domain string, opt types.RegistryOptions) (auth authn.Basic) {
|
||||||
// check registry which particular to get credential
|
// check registry which particular to get credential
|
||||||
for _, registry := range registries {
|
for _, registry := range registries {
|
||||||
err := registry.CheckOptions(domain, opt)
|
err := registry.CheckOptions(domain, opt)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
func TestGetToken(t *testing.T) {
|
func TestGetToken(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
domain string
|
domain string
|
||||||
opt types.RemoteOptions
|
opt types.RegistryOptions
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
"github.com/aquasecurity/trivy/pkg/remote"
|
"github.com/aquasecurity/trivy/pkg/remote"
|
||||||
)
|
)
|
||||||
|
|
||||||
func tryRemote(ctx context.Context, imageName string, ref name.Reference, option types.RemoteOptions) (types.Image, error) {
|
func tryRemote(ctx context.Context, imageName string, ref name.Reference, option types.RegistryOptions) (types.Image, error) {
|
||||||
desc, err := remote.Get(ctx, ref, option)
|
desc, err := remote.Get(ctx, ref, option)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ func TestContainerd_SearchLocalStoreByNameOrDigest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
img, cleanup, err := image.NewContainerImage(ctx, tt.searchName, types.RemoteOptions{},
|
img, cleanup, err := image.NewContainerImage(ctx, tt.searchName, types.ImageOptions{},
|
||||||
image.DisableDockerd(), image.DisablePodman(), image.DisableRemote())
|
image.DisableDockerd(), image.DisablePodman(), image.DisableRemote())
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
if tt.expectErr {
|
if tt.expectErr {
|
||||||
@@ -679,7 +679,7 @@ func localImageTestWithNamespace(t *testing.T, namespace string) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Enable only containerd
|
// Enable only containerd
|
||||||
img, cleanup, err := image.NewContainerImage(ctx, tt.imageName, types.RemoteOptions{},
|
img, cleanup, err := image.NewContainerImage(ctx, tt.imageName, types.ImageOptions{},
|
||||||
image.DisableDockerd(), image.DisablePodman(), image.DisableRemote())
|
image.DisableDockerd(), image.DisablePodman(), image.DisableRemote())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
@@ -814,7 +814,7 @@ func TestContainerd_PullImage(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Enable only containerd
|
// Enable only containerd
|
||||||
img, cleanup, err := image.NewContainerImage(ctx, tt.imageName, types.RemoteOptions{},
|
img, cleanup, err := image.NewContainerImage(ctx, tt.imageName, types.ImageOptions{},
|
||||||
image.DisableDockerd(), image.DisablePodman(), image.DisableRemote())
|
image.DisableDockerd(), image.DisablePodman(), image.DisableRemote())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ func TestFanal_Library_DockerLessMode(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Enable only registry scanning
|
// Enable only registry scanning
|
||||||
img, cleanup, err := image.NewContainerImage(ctx, tt.remoteImageName, types.RemoteOptions{},
|
img, cleanup, err := image.NewContainerImage(ctx, tt.remoteImageName, types.ImageOptions{},
|
||||||
image.DisableDockerd(), image.DisablePodman(), image.DisableContainerd())
|
image.DisableDockerd(), image.DisablePodman(), image.DisableContainerd())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
@@ -200,7 +200,7 @@ func TestFanal_Library_DockerMode(t *testing.T) {
|
|||||||
require.NoError(t, err, tt.name)
|
require.NoError(t, err, tt.name)
|
||||||
|
|
||||||
// Enable only dockerd scanning
|
// Enable only dockerd scanning
|
||||||
img, cleanup, err := image.NewContainerImage(ctx, tt.remoteImageName, types.RemoteOptions{},
|
img, cleanup, err := image.NewContainerImage(ctx, tt.remoteImageName, types.ImageOptions{},
|
||||||
image.DisablePodman(), image.DisableContainerd(), image.DisableRemote())
|
image.DisablePodman(), image.DisableContainerd(), image.DisableRemote())
|
||||||
require.NoError(t, err, tt.name)
|
require.NoError(t, err, tt.name)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
|
||||||
|
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -21,6 +19,7 @@ import (
|
|||||||
testcontainers "github.com/testcontainers/testcontainers-go"
|
testcontainers "github.com/testcontainers/testcontainers-go"
|
||||||
"github.com/testcontainers/testcontainers-go/wait"
|
"github.com/testcontainers/testcontainers-go/wait"
|
||||||
|
|
||||||
|
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
||||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/all"
|
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/all"
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/applier"
|
"github.com/aquasecurity/trivy/pkg/fanal/applier"
|
||||||
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
|
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
|
||||||
@@ -83,7 +82,7 @@ func TestTLSRegistry(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
imageName string
|
imageName string
|
||||||
imageFile string
|
imageFile string
|
||||||
option types.RemoteOptions
|
option types.ImageOptions
|
||||||
login bool
|
login bool
|
||||||
expectedOS types.OS
|
expectedOS types.OS
|
||||||
expectedRepo types.Repository
|
expectedRepo types.Repository
|
||||||
@@ -93,14 +92,16 @@ func TestTLSRegistry(t *testing.T) {
|
|||||||
name: "happy path",
|
name: "happy path",
|
||||||
imageName: "ghcr.io/aquasecurity/trivy-test-images:alpine-310",
|
imageName: "ghcr.io/aquasecurity/trivy-test-images:alpine-310",
|
||||||
imageFile: "../../../../integration/testdata/fixtures/images/alpine-310.tar.gz",
|
imageFile: "../../../../integration/testdata/fixtures/images/alpine-310.tar.gz",
|
||||||
option: types.RemoteOptions{
|
option: types.ImageOptions{
|
||||||
Credentials: []types.Credential{
|
RegistryOptions: types.RegistryOptions{
|
||||||
{
|
Credentials: []types.Credential{
|
||||||
Username: registryUsername,
|
{
|
||||||
Password: registryPassword,
|
Username: registryUsername,
|
||||||
|
Password: registryPassword,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
Insecure: true,
|
||||||
},
|
},
|
||||||
Insecure: true,
|
|
||||||
},
|
},
|
||||||
expectedOS: types.OS{
|
expectedOS: types.OS{
|
||||||
Name: "3.10.2",
|
Name: "3.10.2",
|
||||||
@@ -116,8 +117,10 @@ func TestTLSRegistry(t *testing.T) {
|
|||||||
name: "happy path with docker login",
|
name: "happy path with docker login",
|
||||||
imageName: "ghcr.io/aquasecurity/trivy-test-images:alpine-310",
|
imageName: "ghcr.io/aquasecurity/trivy-test-images:alpine-310",
|
||||||
imageFile: "../../../../integration/testdata/fixtures/images/alpine-310.tar.gz",
|
imageFile: "../../../../integration/testdata/fixtures/images/alpine-310.tar.gz",
|
||||||
option: types.RemoteOptions{
|
option: types.ImageOptions{
|
||||||
Insecure: true,
|
RegistryOptions: types.RegistryOptions{
|
||||||
|
Insecure: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
login: true,
|
login: true,
|
||||||
expectedOS: types.OS{
|
expectedOS: types.OS{
|
||||||
@@ -134,11 +137,13 @@ func TestTLSRegistry(t *testing.T) {
|
|||||||
name: "sad path: tls verify",
|
name: "sad path: tls verify",
|
||||||
imageName: "ghcr.io/aquasecurity/trivy-test-images:alpine-310",
|
imageName: "ghcr.io/aquasecurity/trivy-test-images:alpine-310",
|
||||||
imageFile: "../../../../integration/testdata/fixtures/images/alpine-310.tar.gz",
|
imageFile: "../../../../integration/testdata/fixtures/images/alpine-310.tar.gz",
|
||||||
option: types.RemoteOptions{
|
option: types.ImageOptions{
|
||||||
Credentials: []types.Credential{
|
RegistryOptions: types.RegistryOptions{
|
||||||
{
|
Credentials: []types.Credential{
|
||||||
Username: registryUsername,
|
{
|
||||||
Password: registryPassword,
|
Username: registryUsername,
|
||||||
|
Password: registryPassword,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -148,8 +153,10 @@ func TestTLSRegistry(t *testing.T) {
|
|||||||
name: "sad path: no credential",
|
name: "sad path: no credential",
|
||||||
imageName: "ghcr.io/aquasecurity/trivy-test-images:alpine-310",
|
imageName: "ghcr.io/aquasecurity/trivy-test-images:alpine-310",
|
||||||
imageFile: "../../../../integration/testdata/fixtures/images/alpine-310.tar.gz",
|
imageFile: "../../../../integration/testdata/fixtures/images/alpine-310.tar.gz",
|
||||||
option: types.RemoteOptions{
|
option: types.ImageOptions{
|
||||||
Insecure: true,
|
RegistryOptions: types.RegistryOptions{
|
||||||
|
Insecure: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
@@ -200,7 +207,7 @@ func getRegistryURL(ctx context.Context, registryC testcontainers.Container, exp
|
|||||||
return url.Parse(urlStr)
|
return url.Parse(urlStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func analyze(ctx context.Context, imageRef string, opt types.RemoteOptions) (*types.ArtifactDetail, error) {
|
func analyze(ctx context.Context, imageRef string, opt types.ImageOptions) (*types.ArtifactDetail, error) {
|
||||||
d, err := ioutil.TempDir("", "TestRegistry-*")
|
d, err := ioutil.TempDir("", "TestRegistry-*")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -13,3 +13,50 @@ type ImageExtension interface {
|
|||||||
RepoTags() []string
|
RepoTags() []string
|
||||||
RepoDigests() []string
|
RepoDigests() []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ImageOptions struct {
|
||||||
|
RegistryOptions RegistryOptions
|
||||||
|
DockerOptions DockerOptions
|
||||||
|
PodmanOptions PodmanOptions
|
||||||
|
ContainerdOptions ContainerdOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
type DockerOptions struct {
|
||||||
|
Host string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PodmanOptions struct {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContainerdOptions struct {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
type RegistryOptions struct {
|
||||||
|
// Auth for registries
|
||||||
|
Credentials []Credential
|
||||||
|
|
||||||
|
// RegistryToken is a bearer token to be sent to a registry
|
||||||
|
RegistryToken string
|
||||||
|
|
||||||
|
// SSL/TLS
|
||||||
|
Insecure bool
|
||||||
|
|
||||||
|
// Architecture
|
||||||
|
Platform string
|
||||||
|
|
||||||
|
// ECR
|
||||||
|
AWSAccessKey string
|
||||||
|
AWSSecretKey string
|
||||||
|
AWSSessionToken string
|
||||||
|
AWSRegion string
|
||||||
|
|
||||||
|
// GCP
|
||||||
|
GCPCredPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Credential struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
type RemoteOptions struct {
|
|
||||||
// Auth for registries
|
|
||||||
Credentials []Credential
|
|
||||||
|
|
||||||
// RegistryToken is a bearer token to be sent to a registry
|
|
||||||
RegistryToken string
|
|
||||||
|
|
||||||
// SSL/TLS
|
|
||||||
Insecure bool
|
|
||||||
|
|
||||||
// Architecture
|
|
||||||
Platform string
|
|
||||||
|
|
||||||
// ECR
|
|
||||||
AWSAccessKey string
|
|
||||||
AWSSecretKey string
|
|
||||||
AWSSessionToken string
|
|
||||||
AWSRegion string
|
|
||||||
|
|
||||||
// GCP
|
|
||||||
GCPCredPath string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Credential struct {
|
|
||||||
Username string
|
|
||||||
Password string
|
|
||||||
}
|
|
||||||
@@ -36,6 +36,12 @@ var (
|
|||||||
Value: "",
|
Value: "",
|
||||||
Usage: "set platform in the form os/arch if image is multi-platform capable",
|
Usage: "set platform in the form os/arch if image is multi-platform capable",
|
||||||
}
|
}
|
||||||
|
DockerHostFlag = Flag{
|
||||||
|
Name: "docker-host",
|
||||||
|
ConfigName: "image.docker.host",
|
||||||
|
Value: "",
|
||||||
|
Usage: "unix domain socket path to use for docker scanning",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
type ImageFlagGroup struct {
|
type ImageFlagGroup struct {
|
||||||
@@ -43,6 +49,7 @@ type ImageFlagGroup struct {
|
|||||||
ImageConfigScanners *Flag
|
ImageConfigScanners *Flag
|
||||||
ScanRemovedPkgs *Flag
|
ScanRemovedPkgs *Flag
|
||||||
Platform *Flag
|
Platform *Flag
|
||||||
|
DockerHost *Flag
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImageOptions struct {
|
type ImageOptions struct {
|
||||||
@@ -50,6 +57,7 @@ type ImageOptions struct {
|
|||||||
ImageConfigScanners types.Scanners
|
ImageConfigScanners types.Scanners
|
||||||
ScanRemovedPkgs bool
|
ScanRemovedPkgs bool
|
||||||
Platform string
|
Platform string
|
||||||
|
DockerHost string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewImageFlagGroup() *ImageFlagGroup {
|
func NewImageFlagGroup() *ImageFlagGroup {
|
||||||
@@ -58,6 +66,7 @@ func NewImageFlagGroup() *ImageFlagGroup {
|
|||||||
ImageConfigScanners: &ImageConfigScannersFlag,
|
ImageConfigScanners: &ImageConfigScannersFlag,
|
||||||
ScanRemovedPkgs: &ScanRemovedPkgsFlag,
|
ScanRemovedPkgs: &ScanRemovedPkgsFlag,
|
||||||
Platform: &PlatformFlag,
|
Platform: &PlatformFlag,
|
||||||
|
DockerHost: &DockerHostFlag,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +75,13 @@ func (f *ImageFlagGroup) Name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *ImageFlagGroup) Flags() []*Flag {
|
func (f *ImageFlagGroup) Flags() []*Flag {
|
||||||
return []*Flag{f.Input, f.ImageConfigScanners, f.ScanRemovedPkgs, f.Platform}
|
return []*Flag{
|
||||||
|
f.Input,
|
||||||
|
f.ImageConfigScanners,
|
||||||
|
f.ScanRemovedPkgs,
|
||||||
|
f.Platform,
|
||||||
|
f.DockerHost,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *ImageFlagGroup) ToOptions() (ImageOptions, error) {
|
func (f *ImageFlagGroup) ToOptions() (ImageOptions, error) {
|
||||||
@@ -79,5 +94,6 @@ func (f *ImageFlagGroup) ToOptions() (ImageOptions, error) {
|
|||||||
ImageConfigScanners: scanners,
|
ImageConfigScanners: scanners,
|
||||||
ScanRemovedPkgs: getBool(f.ScanRemovedPkgs),
|
ScanRemovedPkgs: getBool(f.ScanRemovedPkgs),
|
||||||
Platform: getString(f.Platform),
|
Platform: getString(f.Platform),
|
||||||
|
DockerHost: getString(f.DockerHost),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,9 +122,9 @@ func (o *Options) Align() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remote returns options for OCI registries
|
// Registry returns options for OCI registries
|
||||||
func (o *Options) Remote() ftypes.RemoteOptions {
|
func (o *Options) Registry() ftypes.RegistryOptions {
|
||||||
return ftypes.RemoteOptions{
|
return ftypes.RegistryOptions{
|
||||||
Credentials: o.Credentials,
|
Credentials: o.Credentials,
|
||||||
RegistryToken: o.RegistryToken,
|
RegistryToken: o.RegistryToken,
|
||||||
Insecure: o.Insecure,
|
Insecure: o.Insecure,
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ func (u *Updater) Update() error {
|
|||||||
|
|
||||||
// TODO: support remote options
|
// TODO: support remote options
|
||||||
var a *oci.Artifact
|
var a *oci.Artifact
|
||||||
if a, err = oci.NewArtifact(u.repo, u.quiet, ftypes.RemoteOptions{Insecure: u.insecure}); err != nil {
|
if a, err = oci.NewArtifact(u.repo, u.quiet, ftypes.RegistryOptions{Insecure: u.insecure}); err != nil {
|
||||||
return xerrors.Errorf("oci error: %w", err)
|
return xerrors.Errorf("oci error: %w", err)
|
||||||
}
|
}
|
||||||
if err = a.Download(context.Background(), dbDir, oci.DownloadOption{MediaType: mediaType}); err != nil {
|
if err = a.Download(context.Background(), dbDir, oci.DownloadOption{MediaType: mediaType}); err != nil {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import (
|
|||||||
const mediaType = "application/vnd.module.wasm.content.layer.v1+wasm"
|
const mediaType = "application/vnd.module.wasm.content.layer.v1+wasm"
|
||||||
|
|
||||||
// Install installs a module
|
// Install installs a module
|
||||||
func Install(ctx context.Context, dir, repo string, quiet bool, opt types.RemoteOptions) error {
|
func Install(ctx context.Context, dir, repo string, quiet bool, opt types.RegistryOptions) error {
|
||||||
ref, err := name.ParseReference(repo)
|
ref, err := name.ParseReference(repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("repository parse error: %w", err)
|
return xerrors.Errorf("repository parse error: %w", err)
|
||||||
|
|||||||
@@ -51,17 +51,17 @@ type Artifact struct {
|
|||||||
quiet bool
|
quiet bool
|
||||||
|
|
||||||
// For OCI registries
|
// For OCI registries
|
||||||
types.RemoteOptions
|
types.RegistryOptions
|
||||||
|
|
||||||
image v1.Image // For testing
|
image v1.Image // For testing
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewArtifact returns a new artifact
|
// NewArtifact returns a new artifact
|
||||||
func NewArtifact(repo string, quiet bool, remoteOpt types.RemoteOptions, opts ...Option) (*Artifact, error) {
|
func NewArtifact(repo string, quiet bool, registryOpt types.RegistryOptions, opts ...Option) (*Artifact, error) {
|
||||||
art := &Artifact{
|
art := &Artifact{
|
||||||
repository: repo,
|
repository: repo,
|
||||||
quiet: quiet,
|
quiet: quiet,
|
||||||
RemoteOptions: remoteOpt,
|
RegistryOptions: registryOpt,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
@@ -70,7 +70,7 @@ func NewArtifact(repo string, quiet bool, remoteOpt types.RemoteOptions, opts ..
|
|||||||
return art, nil
|
return art, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Artifact) populate(ctx context.Context, opt types.RemoteOptions) error {
|
func (a *Artifact) populate(ctx context.Context, opt types.RegistryOptions) error {
|
||||||
if a.image != nil {
|
if a.image != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -96,7 +96,7 @@ type DownloadOption struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *Artifact) Download(ctx context.Context, dir string, opt DownloadOption) error {
|
func (a *Artifact) Download(ctx context.Context, dir string, opt DownloadOption) error {
|
||||||
if err := a.populate(ctx, a.RemoteOptions); err != nil {
|
if err := a.populate(ctx, a.RegistryOptions); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +191,7 @@ func (a *Artifact) download(ctx context.Context, layer v1.Layer, fileName, dir s
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *Artifact) Digest(ctx context.Context) (string, error) {
|
func (a *Artifact) Digest(ctx context.Context) (string, error) {
|
||||||
if err := a.populate(ctx, a.RemoteOptions); err != nil {
|
if err := a.populate(ctx, a.RegistryOptions); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ func TestArtifact_Download(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
artifact, err := oci.NewArtifact("repo", true, ftypes.RemoteOptions{}, oci.WithImage(img))
|
artifact, err := oci.NewArtifact("repo", true, ftypes.RegistryOptions{}, oci.WithImage(img))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = artifact.Download(context.Background(), tempDir, oci.DownloadOption{
|
err = artifact.Download(context.Background(), tempDir, oci.DownloadOption{
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ func NewClient(cacheDir string, quiet bool, opts ...Option) (*Client, error) {
|
|||||||
func (c *Client) populateOCIArtifact() error {
|
func (c *Client) populateOCIArtifact() error {
|
||||||
if c.artifact == nil {
|
if c.artifact == nil {
|
||||||
repo := fmt.Sprintf("%s:%d", bundleRepository, bundleVersion)
|
repo := fmt.Sprintf("%s:%d", bundleRepository, bundleVersion)
|
||||||
art, err := oci.NewArtifact(repo, c.quiet, types.RemoteOptions{})
|
art, err := oci.NewArtifact(repo, c.quiet, types.RegistryOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("OCI artifact error: %w", err)
|
return xerrors.Errorf("OCI artifact error: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ func TestClient_LoadBuiltinPolicies(t *testing.T) {
|
|||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
// Mock OCI artifact
|
// Mock OCI artifact
|
||||||
art, err := oci.NewArtifact("repo", true, ftypes.RemoteOptions{}, oci.WithImage(img))
|
art, err := oci.NewArtifact("repo", true, ftypes.RegistryOptions{}, oci.WithImage(img))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
c, err := policy.NewClient(tt.cacheDir, true, policy.WithOCIArtifact(art))
|
c, err := policy.NewClient(tt.cacheDir, true, policy.WithOCIArtifact(art))
|
||||||
@@ -257,7 +257,7 @@ func TestClient_NeedsUpdate(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
art, err := oci.NewArtifact("repo", true, ftypes.RemoteOptions{}, oci.WithImage(img))
|
art, err := oci.NewArtifact("repo", true, ftypes.RegistryOptions{}, oci.WithImage(img))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
c, err := policy.NewClient(tmpDir, true, policy.WithOCIArtifact(art), policy.WithClock(tt.clock))
|
c, err := policy.NewClient(tmpDir, true, policy.WithOCIArtifact(art), policy.WithClock(tt.clock))
|
||||||
@@ -361,7 +361,7 @@ func TestClient_DownloadBuiltinPolicies(t *testing.T) {
|
|||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
// Mock OCI artifact
|
// Mock OCI artifact
|
||||||
art, err := oci.NewArtifact("repo", true, ftypes.RemoteOptions{}, oci.WithImage(img))
|
art, err := oci.NewArtifact("repo", true, ftypes.RegistryOptions{}, oci.WithImage(img))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
c, err := policy.NewClient(tempDir, true, policy.WithClock(tt.clock), policy.WithOCIArtifact(art))
|
c, err := policy.NewClient(tempDir, true, policy.WithClock(tt.clock), policy.WithOCIArtifact(art))
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ type Descriptor = remote.Descriptor
|
|||||||
|
|
||||||
// Get is a wrapper of google/go-containerregistry/pkg/v1/remote.Get
|
// Get is a wrapper of google/go-containerregistry/pkg/v1/remote.Get
|
||||||
// so that it can try multiple authentication methods.
|
// so that it can try multiple authentication methods.
|
||||||
func Get(ctx context.Context, ref name.Reference, option types.RemoteOptions) (*Descriptor, error) {
|
func Get(ctx context.Context, ref name.Reference, option types.RegistryOptions) (*Descriptor, error) {
|
||||||
transport := httpTransport(option.Insecure)
|
transport := httpTransport(option.Insecure)
|
||||||
|
|
||||||
var errs error
|
var errs error
|
||||||
@@ -63,7 +63,7 @@ func Get(ctx context.Context, ref name.Reference, option types.RemoteOptions) (*
|
|||||||
|
|
||||||
// Image is a wrapper of google/go-containerregistry/pkg/v1/remote.Image
|
// Image is a wrapper of google/go-containerregistry/pkg/v1/remote.Image
|
||||||
// so that it can try multiple authentication methods.
|
// so that it can try multiple authentication methods.
|
||||||
func Image(ctx context.Context, ref name.Reference, option types.RemoteOptions) (v1.Image, error) {
|
func Image(ctx context.Context, ref name.Reference, option types.RegistryOptions) (v1.Image, error) {
|
||||||
transport := httpTransport(option.Insecure)
|
transport := httpTransport(option.Insecure)
|
||||||
|
|
||||||
var errs error
|
var errs error
|
||||||
@@ -87,7 +87,7 @@ func Image(ctx context.Context, ref name.Reference, option types.RemoteOptions)
|
|||||||
|
|
||||||
// Referrers is a wrapper of google/go-containerregistry/pkg/v1/remote.Referrers
|
// Referrers is a wrapper of google/go-containerregistry/pkg/v1/remote.Referrers
|
||||||
// so that it can try multiple authentication methods.
|
// so that it can try multiple authentication methods.
|
||||||
func Referrers(ctx context.Context, d name.Digest, option types.RemoteOptions) (*v1.IndexManifest, error) {
|
func Referrers(ctx context.Context, d name.Digest, option types.RegistryOptions) (*v1.IndexManifest, error) {
|
||||||
transport := httpTransport(option.Insecure)
|
transport := httpTransport(option.Insecure)
|
||||||
|
|
||||||
var errs error
|
var errs error
|
||||||
@@ -121,7 +121,7 @@ func httpTransport(insecure bool) *http.Transport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func authOptions(ctx context.Context, ref name.Reference, option types.RemoteOptions) []remote.Option {
|
func authOptions(ctx context.Context, ref name.Reference, option types.RegistryOptions) []remote.Option {
|
||||||
var opts []remote.Option
|
var opts []remote.Option
|
||||||
for _, cred := range option.Credentials {
|
for _, cred := range option.Credentials {
|
||||||
opts = append(opts, remote.WithAuth(&authn.Basic{
|
opts = append(opts, remote.WithAuth(&authn.Basic{
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ func TestGet(t *testing.T) {
|
|||||||
type args struct {
|
type args struct {
|
||||||
imageName string
|
imageName string
|
||||||
config string
|
config string
|
||||||
option types.RemoteOptions
|
option types.RegistryOptions
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -74,7 +74,7 @@ func TestGet(t *testing.T) {
|
|||||||
name: "single credential",
|
name: "single credential",
|
||||||
args: args{
|
args: args{
|
||||||
imageName: fmt.Sprintf("%s/library/alpine:3.10", serverAddr),
|
imageName: fmt.Sprintf("%s/library/alpine:3.10", serverAddr),
|
||||||
option: types.RemoteOptions{
|
option: types.RegistryOptions{
|
||||||
Credentials: []types.Credential{
|
Credentials: []types.Credential{
|
||||||
{
|
{
|
||||||
Username: "test",
|
Username: "test",
|
||||||
@@ -89,7 +89,7 @@ func TestGet(t *testing.T) {
|
|||||||
name: "multiple credential",
|
name: "multiple credential",
|
||||||
args: args{
|
args: args{
|
||||||
imageName: fmt.Sprintf("%s/library/alpine:3.10", serverAddr),
|
imageName: fmt.Sprintf("%s/library/alpine:3.10", serverAddr),
|
||||||
option: types.RemoteOptions{
|
option: types.RegistryOptions{
|
||||||
Credentials: []types.Credential{
|
Credentials: []types.Credential{
|
||||||
{
|
{
|
||||||
Username: "foo",
|
Username: "foo",
|
||||||
@@ -109,7 +109,7 @@ func TestGet(t *testing.T) {
|
|||||||
args: args{
|
args: args{
|
||||||
imageName: fmt.Sprintf("%s/library/alpine:3.10", serverAddr),
|
imageName: fmt.Sprintf("%s/library/alpine:3.10", serverAddr),
|
||||||
config: fmt.Sprintf(`{"auths": {"%s": {"auth": %q}}}`, serverAddr, encode("test", "testpass")),
|
config: fmt.Sprintf(`{"auths": {"%s": {"auth": %q}}}`, serverAddr, encode("test", "testpass")),
|
||||||
option: types.RemoteOptions{
|
option: types.RegistryOptions{
|
||||||
Insecure: true,
|
Insecure: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -118,7 +118,7 @@ func TestGet(t *testing.T) {
|
|||||||
name: "platform",
|
name: "platform",
|
||||||
args: args{
|
args: args{
|
||||||
imageName: fmt.Sprintf("%s/library/alpine:3.10", serverAddr),
|
imageName: fmt.Sprintf("%s/library/alpine:3.10", serverAddr),
|
||||||
option: types.RemoteOptions{
|
option: types.RegistryOptions{
|
||||||
Credentials: []types.Credential{
|
Credentials: []types.Credential{
|
||||||
{
|
{
|
||||||
Username: "test",
|
Username: "test",
|
||||||
@@ -134,7 +134,7 @@ func TestGet(t *testing.T) {
|
|||||||
name: "bad credential",
|
name: "bad credential",
|
||||||
args: args{
|
args: args{
|
||||||
imageName: fmt.Sprintf("%s/library/alpine:3.10", serverAddr),
|
imageName: fmt.Sprintf("%s/library/alpine:3.10", serverAddr),
|
||||||
option: types.RemoteOptions{
|
option: types.RegistryOptions{
|
||||||
Credentials: []types.Credential{
|
Credentials: []types.Credential{
|
||||||
{
|
{
|
||||||
Username: "foo",
|
Username: "foo",
|
||||||
@@ -151,7 +151,7 @@ func TestGet(t *testing.T) {
|
|||||||
args: args{
|
args: args{
|
||||||
imageName: fmt.Sprintf("%s/library/alpine:3.10", serverAddr),
|
imageName: fmt.Sprintf("%s/library/alpine:3.10", serverAddr),
|
||||||
config: fmt.Sprintf(`{"auths": {"%s": {"auth": %q}}}`, serverAddr, encode("foo", "bar")),
|
config: fmt.Sprintf(`{"auths": {"%s": {"auth": %q}}}`, serverAddr, encode("foo", "bar")),
|
||||||
option: types.RemoteOptions{
|
option: types.RegistryOptions{
|
||||||
Insecure: true,
|
Insecure: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -35,19 +35,19 @@ type Server struct {
|
|||||||
dbRepository string
|
dbRepository string
|
||||||
|
|
||||||
// For OCI registries
|
// For OCI registries
|
||||||
types.RemoteOptions
|
types.RegistryOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer returns an instance of Server
|
// NewServer returns an instance of Server
|
||||||
func NewServer(appVersion, addr, cacheDir, token, tokenHeader, dbRepository string, opt types.RemoteOptions) Server {
|
func NewServer(appVersion, addr, cacheDir, token, tokenHeader, dbRepository string, opt types.RegistryOptions) Server {
|
||||||
return Server{
|
return Server{
|
||||||
appVersion: appVersion,
|
appVersion: appVersion,
|
||||||
addr: addr,
|
addr: addr,
|
||||||
cacheDir: cacheDir,
|
cacheDir: cacheDir,
|
||||||
token: token,
|
token: token,
|
||||||
tokenHeader: tokenHeader,
|
tokenHeader: tokenHeader,
|
||||||
dbRepository: dbRepository,
|
dbRepository: dbRepository,
|
||||||
RemoteOptions: opt,
|
RegistryOptions: opt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ func (s Server) ListenAndServe(serverCache cache.Cache, skipDBUpdate bool) error
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
for {
|
for {
|
||||||
time.Sleep(updateInterval)
|
time.Sleep(updateInterval)
|
||||||
if err := worker.update(ctx, s.appVersion, s.cacheDir, skipDBUpdate, dbUpdateWg, requestWg, s.RemoteOptions); err != nil {
|
if err := worker.update(ctx, s.appVersion, s.cacheDir, skipDBUpdate, dbUpdateWg, requestWg, s.RegistryOptions); err != nil {
|
||||||
log.Logger.Errorf("%+v\n", err)
|
log.Logger.Errorf("%+v\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -126,7 +126,7 @@ func newDBWorker(dbClient dbFile.Operation) dbWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w dbWorker) update(ctx context.Context, appVersion, cacheDir string,
|
func (w dbWorker) update(ctx context.Context, appVersion, cacheDir string,
|
||||||
skipDBUpdate bool, dbUpdateWg, requestWg *sync.WaitGroup, opt types.RemoteOptions) error {
|
skipDBUpdate bool, dbUpdateWg, requestWg *sync.WaitGroup, opt types.RegistryOptions) error {
|
||||||
log.Logger.Debug("Check for DB update...")
|
log.Logger.Debug("Check for DB update...")
|
||||||
needsUpdate, err := w.dbClient.NeedsUpdate(appVersion, skipDBUpdate)
|
needsUpdate, err := w.dbClient.NeedsUpdate(appVersion, skipDBUpdate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -142,7 +142,7 @@ func (w dbWorker) update(ctx context.Context, appVersion, cacheDir string,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w dbWorker) hotUpdate(ctx context.Context, cacheDir string, dbUpdateWg, requestWg *sync.WaitGroup, opt types.RemoteOptions) error {
|
func (w dbWorker) hotUpdate(ctx context.Context, cacheDir string, dbUpdateWg, requestWg *sync.WaitGroup, opt types.RegistryOptions) error {
|
||||||
tmpDir, err := os.MkdirTemp("", "db")
|
tmpDir, err := os.MkdirTemp("", "db")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("failed to create a temp dir: %w", err)
|
return xerrors.Errorf("failed to create a temp dir: %w", err)
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ func Test_dbWorker_update(t *testing.T) {
|
|||||||
|
|
||||||
var dbUpdateWg, requestWg sync.WaitGroup
|
var dbUpdateWg, requestWg sync.WaitGroup
|
||||||
err := w.update(context.Background(), tt.args.appVersion, cacheDir,
|
err := w.update(context.Background(), tt.args.appVersion, cacheDir,
|
||||||
tt.needsUpdate.input.skip, &dbUpdateWg, &requestWg, ftypes.RemoteOptions{})
|
tt.needsUpdate.input.skip, &dbUpdateWg, &requestWg, ftypes.RegistryOptions{})
|
||||||
if tt.wantErr != "" {
|
if tt.wantErr != "" {
|
||||||
require.NotNil(t, err, tt.name)
|
require.NotNil(t, err, tt.name)
|
||||||
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
|
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
|
||||||
|
|||||||
Reference in New Issue
Block a user