go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/resources/discovery/docker_engine/resolver.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package docker_engine 5 6 import ( 7 "context" 8 "os" 9 10 "github.com/cockroachdb/errors" 11 "github.com/google/go-containerregistry/pkg/name" 12 "github.com/rs/zerolog/log" 13 "go.mondoo.com/cnquery/providers-sdk/v1/inventory" 14 "go.mondoo.com/cnquery/providers-sdk/v1/vault" 15 "go.mondoo.com/cnquery/providers/os/resources/discovery/container_registry" 16 "go.mondoo.com/cnquery/utils/stringx" 17 ) 18 19 const ( 20 DiscoveryContainerRunning = "container" 21 DiscoveryContainerImages = "container-images" 22 ) 23 24 type Resolver struct{} 25 26 func (r *Resolver) Name() string { 27 return "Docker Resolver" 28 } 29 30 func (r *Resolver) AvailableDiscoveryTargets() []string { 31 return []string{"auto", "all", DiscoveryContainerRunning, DiscoveryContainerImages} 32 } 33 34 // func (r *Resolver) Resolve(ctx context.Context, root *inventory.Asset, conf *inventory.Config, credsResolver vault.Resolver, sfn common.QuerySecretFn, userIdDetectors ...providers.PlatformIdDetector) ([]*inventory.Asset, error) { 35 func (r *Resolver) Resolve(ctx context.Context, root *inventory.Asset, conf *inventory.Config, credsResolver vault.Resolver) ([]*inventory.Asset, error) { 36 if conf == nil { 37 return nil, errors.New("no provider configuration found") 38 } 39 40 // check if we have a tar as input 41 // detect if the tar is a container image format -> container image 42 // or a container snapshot format -> container snapshot 43 if conf.Type == "tar" { 44 45 if conf.Options == nil || conf.Options["file"] == "" { 46 return nil, errors.New("could not find the tar file") 47 } 48 49 filename := conf.Options["file"] 50 51 // check if we are pointing to a local tar file 52 _, err := os.Stat(filename) 53 if err != nil { 54 return nil, errors.New("could not find the tar file: " + filename) 55 } 56 57 // Tar container can be an image or a snapshot 58 resolvedAsset := &inventory.Asset{ 59 Name: filename, 60 Connections: []*inventory.Config{conf}, 61 Platform: &inventory.Platform{ 62 Kind: "container-image", 63 Runtime: "docker-image", 64 }, 65 State: inventory.State_STATE_ONLINE, 66 } 67 68 // determine platform identifier 69 identifier, err := platformID(filename) 70 if err != nil { 71 return nil, err 72 } 73 74 resolvedAsset.PlatformIds = []string{identifier} 75 76 return []*inventory.Asset{resolvedAsset}, nil 77 } 78 79 ded, dockerEngErr := NewDockerEngineDiscovery() 80 // we do not fail here, since we pull the image from upstream if its is an image without the need for docker 81 82 if conf.Type == "docker-container" { 83 if dockerEngErr != nil { 84 return nil, errors.Wrap(dockerEngErr, "cannot connect to docker engine to fetch the container") 85 } 86 resolvedAsset, err := r.container(ctx, root, conf, ded) 87 if err != nil { 88 return nil, err 89 } 90 91 return []*inventory.Asset{resolvedAsset}, nil 92 } 93 94 if conf.Type == "docker-image" { 95 // NOTE, we ignore dockerEngErr here since we fallback to pulling the images directly 96 // resolvedAssets, err := r.images(ctx, root, conf, ded, credsResolver, sfn) 97 resolvedAssets, err := r.images(ctx, root, conf, ded, credsResolver) 98 if err != nil { 99 return nil, err 100 } 101 return resolvedAssets, nil 102 } 103 104 // check if we should do a discovery 105 if conf.Host == "" { 106 return DiscoverDockerEngineAssets(conf) 107 } 108 109 // if we are here, the user has not specified the direct target, we need to search for it 110 // could be an image id/name, container id/name or a short reference to an image in docker engine 111 // 1. check if we have a container id 112 // check if the container is running -> docker engine 113 // check if the container is stopped -> container snapshot 114 // 3. check if we have an image id -> container image 115 // 4. check if we have a descriptor for a registry -> container image 116 log.Debug().Str("docker", conf.Host).Msg("try to resolve the container or image source") 117 118 if dockerEngErr == nil { 119 containerAsset, err := r.container(ctx, root, conf, ded) 120 if err == nil { 121 return []*inventory.Asset{containerAsset}, nil 122 } 123 } 124 125 // containerImageAssets, err := r.images(ctx, root, conf, ded, credsResolver, sfn) 126 containerImageAssets, err := r.images(ctx, root, conf, ded, credsResolver) 127 if err == nil { 128 return containerImageAssets, nil 129 } 130 131 // if we reached here, we assume we have a name of an image or container from a registry 132 return nil, errors.Wrap(err, "could not find the container reference") 133 } 134 135 func (k *Resolver) container(ctx context.Context, root *inventory.Asset, conf *inventory.Config, ded *dockerEngineDiscovery) (*inventory.Asset, error) { 136 ci, err := ded.ContainerInfo(conf.Host) 137 if err != nil { 138 return nil, err 139 } 140 141 conf.Type = "docker-container" 142 143 // TODO: how do we know we're not connecting to docker over 144 // the network and LOCAL_OS is correct 145 relatedAssets := []*inventory.Asset{ 146 { 147 Connections: []*inventory.Config{ 148 { 149 Type: "local", 150 }, 151 }, 152 }, 153 } 154 155 return &inventory.Asset{ 156 Name: ci.Name, 157 Connections: []*inventory.Config{conf}, 158 PlatformIds: []string{ci.PlatformID}, 159 Platform: &inventory.Platform{ 160 Kind: "container", 161 Runtime: "docker-container", 162 }, 163 State: inventory.State_STATE_ONLINE, 164 Labels: ci.Labels, 165 RelatedAssets: relatedAssets, 166 }, nil 167 } 168 169 // func (k *Resolver) images(ctx context.Context, root *inventory.Asset, conf *inventory.Config, ded *dockerEngineDiscovery, credsResolver vault.Resolver, sfn common.QuerySecretFn) ([]*inventory.Asset, error) { 170 func (k *Resolver) images(ctx context.Context, root *inventory.Asset, conf *inventory.Config, ded *dockerEngineDiscovery, credsResolver vault.Resolver) ([]*inventory.Asset, error) { 171 // if we have a docker engine available, try to fetch it from there 172 if ded != nil { 173 ii, err := ded.ImageInfo(conf.Host) 174 if err == nil { 175 conf.Type = "docker-image" 176 return []*inventory.Asset{{ 177 Name: ii.Name, 178 Connections: []*inventory.Config{conf}, 179 PlatformIds: []string{ii.PlatformID}, 180 Platform: &inventory.Platform{ 181 Kind: "container-image", 182 Runtime: "docker-image", 183 }, 184 State: inventory.State_STATE_ONLINE, 185 Labels: ii.Labels, 186 }}, nil 187 } 188 189 } 190 191 // otherwise try to fetch the image from upstream 192 log.Debug().Msg("try to download the image from docker registry") 193 _, err := name.ParseReference(conf.Host, name.WeakValidation) 194 if err != nil { 195 return nil, err 196 } 197 198 // switch to container registry resolver since docker is not installed 199 rr := container_registry.Resolver{ 200 NoStrictValidation: true, 201 } 202 // return rr.Resolve(ctx, root, conf, credsResolver, sfn) 203 return rr.Resolve(ctx, root, conf, credsResolver) 204 } 205 206 func DiscoverDockerEngineAssets(conf *inventory.Config) ([]*inventory.Asset, error) { 207 log.Debug().Msg("start discovery for docker engine") 208 // we use generic `container` and `container-images` options to avoid the requirement for the user to know if 209 // the system is using docker or podman locally 210 assetList := []*inventory.Asset{} 211 212 if conf.Discover == nil { 213 return assetList, nil 214 } 215 216 // discover running container: container 217 if stringx.Contains(conf.Discover.Targets, "all") || stringx.Contains(conf.Discover.Targets, DiscoveryContainerRunning) { 218 ded, err := NewDockerEngineDiscovery() 219 if err != nil { 220 return nil, err 221 } 222 223 containerAssets, err := ded.ListContainer() 224 if err != nil { 225 return nil, err 226 } 227 228 log.Info().Int("container", len(containerAssets)).Msg("running container search completed") 229 assetList = append(assetList, containerAssets...) 230 } 231 232 // discover container images: container-images 233 if stringx.Contains(conf.Discover.Targets, "all") || stringx.Contains(conf.Discover.Targets, DiscoveryContainerImages) { 234 ded, err := NewDockerEngineDiscovery() 235 if err != nil { 236 return nil, err 237 } 238 239 containerImageAssets, err := ded.ListImages() 240 if err != nil { 241 return nil, err 242 } 243 log.Info().Int("images", len(containerImageAssets)).Msg("running container images search completed") 244 assetList = append(assetList, containerImageAssets...) 245 } 246 return assetList, nil 247 }