mirror of
https://github.com/pgsty/minio.git
synced 2026-11-12 02:25:34 +03:00
Prepare SILO 20261112 release candidate
Harden IAM/bucket/import/replication policy validation, enforce tag and version authorization across read/copy/list/archive endpoints, bind SigV4 credential-scope dates and streaming trailer limits, validate HTTP SSE-KMS contexts as strict UTF-8 JSON, add release-commit and coordinated dependency gates, and pin silo-pkg v3.16.1, Console v2.7.1, mcli 20261112, Go 1.27.4, and Helm chart 7.0.5. Signed-off-by: Feng Ruohang <rh@vonng.com>
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
// Copyright (c) 2026 PGSTY
|
||||
//
|
||||
// This file is part of MinIO Object Storage stack
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
"github.com/pgsty/silo-pkg/v3/policy"
|
||||
)
|
||||
|
||||
type strictIAMStatementWire struct {
|
||||
SID policy.ID `json:"Sid,omitempty"`
|
||||
Effect policy.Effect `json:"Effect"`
|
||||
Actions json.RawMessage `json:"Action,omitempty"`
|
||||
NotActions json.RawMessage `json:"NotAction,omitempty"`
|
||||
Resources json.RawMessage `json:"Resource,omitempty"`
|
||||
NotResources json.RawMessage `json:"NotResource,omitempty"`
|
||||
Conditions json.RawMessage `json:"Condition,omitempty"`
|
||||
}
|
||||
|
||||
type strictIAMPolicyWire struct {
|
||||
ID policy.ID `json:"ID,omitempty"`
|
||||
Version string `json:"Version"`
|
||||
Statements []strictIAMStatementWire `json:"Statement"`
|
||||
}
|
||||
|
||||
type strictPrincipalWire struct {
|
||||
AWS json.RawMessage `json:"AWS"`
|
||||
}
|
||||
|
||||
type strictBucketStatementWire struct {
|
||||
SID policy.ID `json:"Sid,omitempty"`
|
||||
Effect policy.Effect `json:"Effect"`
|
||||
Principal json.RawMessage `json:"Principal,omitempty"`
|
||||
Actions json.RawMessage `json:"Action,omitempty"`
|
||||
NotActions json.RawMessage `json:"NotAction,omitempty"`
|
||||
Resources json.RawMessage `json:"Resource,omitempty"`
|
||||
NotResources json.RawMessage `json:"NotResource,omitempty"`
|
||||
Conditions json.RawMessage `json:"Condition,omitempty"`
|
||||
}
|
||||
|
||||
type strictBucketPolicyWire struct {
|
||||
ID policy.ID `json:"ID,omitempty"`
|
||||
Version string `json:"Version"`
|
||||
Statements []strictBucketStatementWire `json:"Statement"`
|
||||
}
|
||||
|
||||
func validateStrictIAMPolicyWire(data []byte) ([]byte, error) {
|
||||
trimmed := bytes.TrimSpace(data)
|
||||
if len(trimmed) == 0 || trimmed[0] != '{' {
|
||||
return nil, policy.Errorf("policy must be a JSON object")
|
||||
}
|
||||
var wire strictIAMPolicyWire
|
||||
dec := json.NewDecoder(bytes.NewReader(trimmed))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&wire); err != nil {
|
||||
return nil, policy.Errorf("%w", err)
|
||||
}
|
||||
var extra any
|
||||
if err := dec.Decode(&extra); err != io.EOF {
|
||||
if err == nil {
|
||||
return nil, policy.Errorf("unexpected trailing JSON data in policy")
|
||||
}
|
||||
return nil, policy.Errorf("%w", err)
|
||||
}
|
||||
return trimmed, nil
|
||||
}
|
||||
|
||||
func parsePolicyStrictBytes(data []byte) (*policy.Policy, error) {
|
||||
trimmed, err := validateStrictIAMPolicyWire(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p, err := policy.ParseConfigStrict(bytes.NewReader(trimmed))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if p.IsEmpty() {
|
||||
return nil, policy.Errorf("empty policies are not allowed")
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func parseSessionPolicyStrictBytes(data []byte) (*policy.Policy, error) {
|
||||
trimmed, err := validateStrictIAMPolicyWire(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return policy.ParseConfigStrict(bytes.NewReader(trimmed))
|
||||
}
|
||||
|
||||
// Snapshot imports retain supported historical policy semantics while rejecting
|
||||
// unknown JSON fields that could silently discard restrictions.
|
||||
func parsePolicySnapshotBytes(data []byte) (*policy.Policy, error) {
|
||||
trimmed, err := validateStrictIAMPolicyWire(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return policy.ParseConfig(bytes.NewReader(trimmed))
|
||||
}
|
||||
|
||||
func validateStrictPrincipalWire(raw json.RawMessage) error {
|
||||
trimmed := bytes.TrimSpace(raw)
|
||||
if len(trimmed) == 0 || trimmed[0] != '{' {
|
||||
return nil
|
||||
}
|
||||
var pw strictPrincipalWire
|
||||
dec := json.NewDecoder(bytes.NewReader(trimmed))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&pw); err != nil {
|
||||
return policy.Errorf("%w", err)
|
||||
}
|
||||
var extra any
|
||||
if err := dec.Decode(&extra); err != io.EOF {
|
||||
if err == nil {
|
||||
return policy.Errorf("unexpected trailing JSON data in principal")
|
||||
}
|
||||
return policy.Errorf("%w", err)
|
||||
}
|
||||
if len(bytes.TrimSpace(pw.AWS)) == 0 {
|
||||
return policy.Errorf("invalid principal '%s'", string(raw))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseBucketPolicyStrictBytes(data []byte, bucket string) (*policy.BucketPolicy, error) {
|
||||
trimmed := bytes.TrimSpace(data)
|
||||
if len(trimmed) == 0 || trimmed[0] != '{' {
|
||||
return nil, policy.Errorf("bucket policy must be a JSON object")
|
||||
}
|
||||
var wire strictBucketPolicyWire
|
||||
dec := json.NewDecoder(bytes.NewReader(trimmed))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&wire); err != nil {
|
||||
return nil, policy.Errorf("%w", err)
|
||||
}
|
||||
var extra any
|
||||
if err := dec.Decode(&extra); err != io.EOF {
|
||||
if err == nil {
|
||||
return nil, policy.Errorf("unexpected trailing JSON data in bucket policy")
|
||||
}
|
||||
return nil, policy.Errorf("%w", err)
|
||||
}
|
||||
for _, st := range wire.Statements {
|
||||
if err := validateStrictPrincipalWire(st.Principal); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return policy.ParseBucketPolicyConfig(bytes.NewReader(trimmed), bucket)
|
||||
}
|
||||
Reference in New Issue
Block a user