-
Notifications
You must be signed in to change notification settings - Fork 113
fix: verify blob digests and sandbox Python backends #848
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
Changes from all commits
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 |
|---|---|---|
|
|
@@ -2,6 +2,8 @@ package store | |
|
|
||
| import ( | ||
| "context" | ||
| "crypto/sha256" | ||
| "encoding/hex" | ||
| "errors" | ||
| "fmt" | ||
| "io" | ||
|
|
@@ -268,6 +270,35 @@ func (s *LocalStore) WriteBlobWithResume(diffID oci.Hash, r io.Reader, digestStr | |
|
|
||
| f.Close() // Rename will fail on Windows if the file is still open. | ||
|
|
||
| // Verify the digest of the completed file before making it visible. This | ||
| // must be done after closing the file (to flush all writes) and before the | ||
| // rename so that a mismatch never results in a corrupt blob being stored. | ||
| // We hash the whole file rather than the streamed bytes so that resumed | ||
| // downloads (which append to an existing partial file) are verified | ||
| // correctly over their entire contents. | ||
| if diffID.Algorithm == "sha256" { | ||
| completedFile, openErr := os.Open(incompletePath) | ||
| if openErr != nil { | ||
| _ = os.Remove(incompletePath) | ||
| return fmt.Errorf("open completed blob file for verification: %w", openErr) | ||
| } | ||
|
|
||
| hasher := sha256.New() | ||
| if _, copyErr := io.Copy(hasher, completedFile); copyErr != nil { | ||
| completedFile.Close() | ||
| _ = os.Remove(incompletePath) | ||
| return fmt.Errorf("hash completed blob file: %w", copyErr) | ||
| } | ||
| completedFile.Close() | ||
|
|
||
| computed := hex.EncodeToString(hasher.Sum(nil)) | ||
| if computed != diffID.Hex { | ||
| _ = os.Remove(incompletePath) | ||
| return fmt.Errorf("blob digest mismatch for %q: expected sha256:%s, got sha256:%s", | ||
| diffID.String(), diffID.Hex, computed) | ||
| } | ||
| } | ||
|
Comment on lines
+279
to
+300
Contributor
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. The verification logic is hardcoded to if diffID.Algorithm == "sha256" || diffID.Algorithm == "sha512" {
completedFile, openErr := os.Open(incompletePath)
if openErr != nil {
_ = os.Remove(incompletePath)
return fmt.Errorf("open completed blob file for verification: %w", openErr)
}
var hasher hash.Hash
if diffID.Algorithm == "sha512" {
hasher = sha512.New()
} else {
hasher = sha256.New()
}
if _, copyErr := io.Copy(hasher, completedFile); copyErr != nil {
completedFile.Close()
_ = os.Remove(incompletePath)
return fmt.Errorf("hash completed blob file: %w", copyErr)
}
completedFile.Close()
computed := hex.EncodeToString(hasher.Sum(nil))
if computed != diffID.Hex {
_ = os.Remove(incompletePath)
return fmt.Errorf("blob digest mismatch for %q: expected %s:%s, got %s:%s",
diffID.String(), diffID.Algorithm, diffID.Hex, diffID.Algorithm, computed)
}
} |
||
|
|
||
| if renameFinalErr := os.Rename(incompletePath, path); renameFinalErr != nil { | ||
| return fmt.Errorf("rename blob file: %w", renameFinalErr) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -18,6 +18,7 @@ import ( | |||||
| "github.com/docker/model-runner/pkg/internal/dockerhub" | ||||||
| "github.com/docker/model-runner/pkg/internal/utils" | ||||||
| "github.com/docker/model-runner/pkg/logging" | ||||||
| "github.com/docker/model-runner/pkg/sandbox" | ||||||
| ) | ||||||
|
|
||||||
| const ( | ||||||
|
|
@@ -255,7 +256,7 @@ func (d *diffusers) Run(ctx context.Context, socket, model string, modelRef stri | |||||
| Socket: socket, | ||||||
| BinaryPath: d.pythonPath, | ||||||
| SandboxPath: "", | ||||||
| SandboxConfig: "", | ||||||
| SandboxConfig: sandbox.ConfigurationPython, | ||||||
|
Contributor
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. The
Suggested change
|
||||||
| Args: args, | ||||||
| Logger: d.log, | ||||||
| ServerLogWriter: logging.NewWriter(d.serverLog), | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -13,6 +13,7 @@ import ( | |||||||||||||
| "github.com/docker/model-runner/pkg/inference/models" | ||||||||||||||
| "github.com/docker/model-runner/pkg/inference/platform" | ||||||||||||||
| "github.com/docker/model-runner/pkg/logging" | ||||||||||||||
| "github.com/docker/model-runner/pkg/sandbox" | ||||||||||||||
|
Comment on lines
15
to
+16
Contributor
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. To correctly support sandboxing for custom Python paths on Darwin, we need to determine the directory of the Python binary to set the
Suggested change
|
||||||||||||||
| ) | ||||||||||||||
|
|
||||||||||||||
| const ( | ||||||||||||||
|
|
@@ -140,7 +141,7 @@ func (m *mlx) Run(ctx context.Context, socket, model string, modelRef string, mo | |||||||||||||
| Socket: socket, | ||||||||||||||
| BinaryPath: m.pythonPath, | ||||||||||||||
| SandboxPath: "", | ||||||||||||||
| SandboxConfig: "", | ||||||||||||||
| SandboxConfig: sandbox.ConfigurationPython, | ||||||||||||||
|
Contributor
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. The
Suggested change
|
||||||||||||||
| Args: args, | ||||||||||||||
| Logger: m.log, | ||||||||||||||
| ServerLogWriter: logging.NewWriter(m.serverLog), | ||||||||||||||
|
|
||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -20,6 +20,7 @@ import ( | |||||
| "github.com/docker/model-runner/pkg/internal/dockerhub" | ||||||
| "github.com/docker/model-runner/pkg/internal/utils" | ||||||
| "github.com/docker/model-runner/pkg/logging" | ||||||
| "github.com/docker/model-runner/pkg/sandbox" | ||||||
| ) | ||||||
|
|
||||||
| const ( | ||||||
|
|
@@ -216,8 +217,8 @@ func (v *vllmMetal) Run(ctx context.Context, socket, model string, modelRef stri | |||||
| BackendName: "vllm-metal", | ||||||
| Socket: socket, | ||||||
| BinaryPath: v.pythonPath, | ||||||
| SandboxPath: "", | ||||||
| SandboxConfig: "", | ||||||
| SandboxPath: v.installDir, | ||||||
|
Contributor
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. The
Suggested change
|
||||||
| SandboxConfig: sandbox.ConfigurationPython, | ||||||
| Args: args, | ||||||
| Logger: v.log, | ||||||
| ServerLogWriter: logging.NewWriter(v.serverLog), | ||||||
|
|
||||||
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.
The store allows both
sha256andsha512algorithms (see line 25), but the verification logic only handlessha256. To prevent a security bypass where a malicious registry could serve unverifiedsha512blobs, we should support verification for all allowed algorithms. This requires importingcrypto/sha512andhash.