-
Couldn't load subscription status.
- Fork 238
Added a warning when using a non-Toolbx container #1707
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
8f169ee
ada55eb
d516223
e7bef55
dfaed70
c9215d4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,6 +24,7 @@ import ( | |
| "fmt" | ||
| "io" | ||
| "strconv" | ||
| "strings" | ||
| "time" | ||
|
|
||
| "github.com/HarryMichal/go-version" | ||
|
|
@@ -352,17 +353,84 @@ func IsToolboxImage(image string) (bool, error) { | |
| } | ||
|
|
||
| if info["Labels"] == nil { | ||
| return false, fmt.Errorf("%s is not a Toolbx image", image) | ||
| return false, nil | ||
| } | ||
|
|
||
| labels := info["Labels"].(map[string]interface{}) | ||
| if labels["com.github.containers.toolbox"] != "true" && labels["com.github.debarshiray.toolbox"] != "true" { | ||
| return false, fmt.Errorf("%s is not a Toolbx image", image) | ||
| return false, nil | ||
| } | ||
|
|
||
| return true, nil | ||
| } | ||
|
|
||
| func IsLDPRELOADEnvSet(image string) (bool, error) { | ||
| info, err := InspectImage(image) | ||
| if err != nil { | ||
| return false, fmt.Errorf("failed to inspect image %s: %s", image, err) | ||
| } | ||
|
|
||
| if info["Config"] == nil { | ||
| return false, nil | ||
| } | ||
|
|
||
| config := info["Config"].(map[string]interface{}) | ||
| if config["Env"] == nil { | ||
| return false, nil | ||
| } | ||
|
|
||
| env := config["Env"] | ||
| switch envVars := env.(type) { | ||
| case []interface{}: | ||
| for _, envVar := range envVars { | ||
| if envVarStr, ok := envVar.(string); ok { | ||
| envVarStrTrimmed := strings.TrimSpace(envVarStr) | ||
| if strings.HasPrefix(envVarStrTrimmed, "LD_PRELOAD=") { | ||
| return true, nil | ||
| } | ||
| } | ||
| } | ||
| case []string: | ||
| for _, envVar := range envVars { | ||
| envVarTrimmed := strings.TrimSpace(envVar) | ||
| if strings.HasPrefix(envVarTrimmed, "LD_PRELOAD=") { | ||
| return true, nil | ||
| } | ||
| } | ||
| default: | ||
| return false, fmt.Errorf("unexpected type '%T' of environment variables in image %s", env, image) | ||
| } | ||
|
|
||
| return false, nil | ||
| } | ||
|
|
||
| func HasImageEntrypoint(image string) (bool, error) { | ||
| info, err := InspectImage(image) | ||
| if err != nil { | ||
| return false, fmt.Errorf("failed to inspect image %s: %s", image, err) | ||
| } | ||
|
|
||
| if info["Config"] == nil { | ||
| return false, nil | ||
| } | ||
|
|
||
| config := info["Config"].(map[string]interface{}) | ||
| if config["Entrypoint"] == nil { | ||
| return false, nil | ||
| } | ||
|
|
||
| entrypoint := config["Entrypoint"] | ||
|
|
||
| switch ep := entrypoint.(type) { | ||
| case []interface{}: | ||
| return len(ep) > 0, nil | ||
| case []string: | ||
| return len(ep) > 0, nil | ||
| default: | ||
| return false, fmt.Errorf("unexpected type '%T' of entrypoint of image %s", entrypoint, image) | ||
| } | ||
| } | ||
|
|
||
| func Logs(container string, since time.Time, stderr io.Writer) error { | ||
| ctx := context.Background() | ||
| err := LogsContext(ctx, container, false, since, stderr) | ||
|
|
@@ -506,3 +574,38 @@ func SystemMigrate(ociRuntimeRequired string) error { | |
|
|
||
| return nil | ||
| } | ||
|
|
||
| func DoesImageFulfillRequirements(image string) (bool, string, error) { | ||
| var warnings []string | ||
|
|
||
| isToolboxImage, err := IsToolboxImage(image) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are invoking For example, here are some past commits where we optimized
The We can reduce the number of |
||
| if err != nil { | ||
| return false, "", fmt.Errorf("failed to verify image compatibility: %w", err) | ||
| } | ||
| if !isToolboxImage { | ||
| warnings = append(warnings, fmt.Sprintf("Warning: Image '%s' does not contain either of the labels 'com.github.containers.toolbox=true' and 'com.github.debarshiray.toolbox=true'", image)) | ||
| } | ||
|
|
||
| isLDPRELOADEnvSet, err := IsLDPRELOADEnvSet(image) | ||
| if err != nil { | ||
| return false, "", fmt.Errorf("failed to validate LD_PRELOAD variable settings: %w", err) | ||
| } | ||
| if isLDPRELOADEnvSet { | ||
| warnings = append(warnings, fmt.Sprintf("Warning: Image '%s' has environment variable LD_PRELOAD set, which may cause container vulnerability (Container Escape)", image)) | ||
| } | ||
|
|
||
| hasEntrypoint, err := HasImageEntrypoint(image) | ||
| if err != nil { | ||
| return false, "", fmt.Errorf("failed to check image entrypoint: %w", err) | ||
| } | ||
| if hasEntrypoint { | ||
| warnings = append(warnings, fmt.Sprintf("Warning: Image '%s' has an entrypoint defined", image)) | ||
| } | ||
|
|
||
| if len(warnings) > 0 { | ||
| warningMessage := strings.Join(warnings, "\n") | ||
| return false, warningMessage, nil | ||
| } | ||
|
|
||
| return true, "", nil | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Decoding the JSON in this way can be fragile if the structure of the JSON changes, and over time Podman has sometimes done that. :(
See the comments in src/pkg/podman/container.go, and the
UnmarshalJSON()method of the Image object.Fortunately, Go can decode JSON on a best effort basis, which can cover-up many of the superficial changes and needs a lot less code. Here's an introductory article about how it works. Sometimes, Go can't automatically handle the changes and we need to handle it ourselves, like in the above comments.
To take advantage of this, and to avoid invoking
podman inspectrepeatedly, we can decode the JSON into anImageobject that implements the json.Unmarshaler interface. It's implemented by thecontainerInspect,containerPSandImageobjects above. The interface will limit all manual interventions during decoding in one place.Unfortunately, the JSON used by Podman to represent images differs between
podman imagesandpodman inspect --type image. TheImageobject only handles the former, but not the latter.We have the same problem for containers. That's why we have a
Containerinterface to hide the details, and it's implemented by the privatecontainerInspectandcontainerPSobjects that actually decode the JSON.So, our first step would be to have a commit that introduces an
Imageinterface, and renames the existingImageobject to make it private and makes it implement the interface. I don't know what would be the best name for the object, becauseimageImageslooks awkward, but it's also a private type so it won't be seen in too many places.Then, we have to add a second implementation of the
Imageinterface that decodes the JSON frompodman inspect --type image.Here are some commits that did the same thing for containers that you can use for help:
cmd, pkg/podman: Turn Container into an interfacecmd/run, pkg/podman: Make podman.InspectContainer() return a ContainerThen, the
IsToolboxImage()function can turn into theIsToolbx()method of theImageinterface:cmd, pkg/podman: Turn IsToolboxContainer() into Container.IsToolbx()