189 lines
8.7 KiB
Go
189 lines
8.7 KiB
Go
package infrastructureconformance
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"bytes"
|
||
|
|
"crypto/sha256"
|
||
|
|
"encoding/hex"
|
||
|
|
"encoding/json"
|
||
|
|
"errors"
|
||
|
|
"fmt"
|
||
|
|
"io"
|
||
|
|
"regexp"
|
||
|
|
"slices"
|
||
|
|
"strings"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
const (
|
||
|
|
CaptureSchema = "production-infrastructure-capture-v1"
|
||
|
|
ReportSchema = "production-infrastructure-conformance-report-v1"
|
||
|
|
)
|
||
|
|
|
||
|
|
var digestPattern = regexp.MustCompile(`^sha256:[0-9a-f]{64}$`)
|
||
|
|
|
||
|
|
var RequiredComponents = []string{"DOMAIN_TLS", "OIDC", "POSTGRES", "REDIS", "OBJECT_STORE", "NETWORK_ISOLATION", "MONITORING"}
|
||
|
|
|
||
|
|
var requiredControls = map[string][]string{
|
||
|
|
"DOMAIN_TLS": {"DNS_RESOLVED", "TLS_VALID", "CERTIFICATE_MONITORED"},
|
||
|
|
"OIDC": {"JWKS_AVAILABLE", "TLS_VALID", "TENANT_MAPPING_VERIFIED"},
|
||
|
|
"POSTGRES": {"PRIVATE_NETWORK", "TLS_VALID", "HIGH_AVAILABILITY", "BACKUP_CONFIGURED", "MONITORED"},
|
||
|
|
"REDIS": {"PRIVATE_NETWORK", "TLS_VALID", "HIGH_AVAILABILITY", "REBUILD_VERIFIED", "MONITORED"},
|
||
|
|
"OBJECT_STORE": {"PRIVATE_ACCESS", "TLS_VALID", "VERSIONING_OR_RETENTION", "MONITORED"},
|
||
|
|
"NETWORK_ISOLATION": {"INGRESS_RESTRICTED", "EGRESS_RESTRICTED", "NAMESPACE_ISOLATED"},
|
||
|
|
"MONITORING": {"METRICS_SCRAPED", "ALERT_RULES_LOADED", "EXTERNAL_RECEIVER_ROUTED"},
|
||
|
|
}
|
||
|
|
|
||
|
|
type Capture struct {
|
||
|
|
SchemaVersion string `json:"schema_version"`
|
||
|
|
EvidenceSource string `json:"evidence_source"`
|
||
|
|
CandidateVersion string `json:"candidate_version"`
|
||
|
|
BuildDigest string `json:"build_digest"`
|
||
|
|
Environment string `json:"environment"`
|
||
|
|
RegionRef string `json:"region_ref"`
|
||
|
|
ClusterRef string `json:"cluster_ref"`
|
||
|
|
ChangeTicketRef string `json:"change_ticket_ref"`
|
||
|
|
AttestedByRef string `json:"attested_by_ref"`
|
||
|
|
AttestedAt time.Time `json:"attested_at"`
|
||
|
|
CollectedAt time.Time `json:"collected_at"`
|
||
|
|
Components []ComponentEvidence `json:"components"`
|
||
|
|
}
|
||
|
|
|
||
|
|
type ComponentEvidence struct {
|
||
|
|
ComponentType string `json:"component_type"`
|
||
|
|
ResourceRef string `json:"resource_ref"`
|
||
|
|
Status string `json:"status"`
|
||
|
|
ObservedAt time.Time `json:"observed_at"`
|
||
|
|
VerifiedControls []string `json:"verified_controls"`
|
||
|
|
ProbeRef string `json:"probe_ref"`
|
||
|
|
ConfigurationRef string `json:"configuration_ref"`
|
||
|
|
AuditRef string `json:"audit_ref"`
|
||
|
|
}
|
||
|
|
|
||
|
|
type Check struct {
|
||
|
|
Code string `json:"code"`
|
||
|
|
Status string `json:"status"`
|
||
|
|
Detail string `json:"detail"`
|
||
|
|
}
|
||
|
|
|
||
|
|
type Report struct {
|
||
|
|
SchemaVersion string `json:"schema_version"`
|
||
|
|
Result string `json:"result"`
|
||
|
|
GeneratedAt time.Time `json:"generated_at"`
|
||
|
|
CandidateVersion string `json:"candidate_version"`
|
||
|
|
BuildDigest string `json:"build_digest"`
|
||
|
|
EvidenceScope string `json:"evidence_scope"`
|
||
|
|
ExternalValidationRequired bool `json:"external_validation_required"`
|
||
|
|
Environment string `json:"environment"`
|
||
|
|
RegionRef string `json:"region_ref"`
|
||
|
|
ClusterRef string `json:"cluster_ref"`
|
||
|
|
ComponentCount int `json:"component_count"`
|
||
|
|
VerifiedComponents []string `json:"verified_components"`
|
||
|
|
ControlCounts map[string]int `json:"control_counts"`
|
||
|
|
InputSHA256 map[string]string `json:"input_sha256"`
|
||
|
|
Checks []Check `json:"checks"`
|
||
|
|
Disclaimer string `json:"disclaimer"`
|
||
|
|
}
|
||
|
|
|
||
|
|
func ParseCapture(content []byte) (Capture, error) {
|
||
|
|
var capture Capture
|
||
|
|
if err := decodeStrict(content, &capture); err != nil {
|
||
|
|
return Capture{}, fmt.Errorf("decode production infrastructure capture: %w", err)
|
||
|
|
}
|
||
|
|
return capture, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func ParseReport(content []byte) (Report, error) {
|
||
|
|
var report Report
|
||
|
|
if err := decodeStrict(content, &report); err != nil {
|
||
|
|
return Report{}, fmt.Errorf("decode production infrastructure report: %w", err)
|
||
|
|
}
|
||
|
|
return report, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func Evaluate(capture Capture, candidateVersion, buildDigest string, generatedAt time.Time) Report {
|
||
|
|
candidateVersion = strings.TrimSpace(candidateVersion)
|
||
|
|
buildDigest = strings.ToLower(strings.TrimSpace(buildDigest))
|
||
|
|
report := Report{
|
||
|
|
SchemaVersion: ReportSchema, Result: "PASS", GeneratedAt: generatedAt.UTC(), CandidateVersion: candidateVersion, BuildDigest: buildDigest,
|
||
|
|
EvidenceScope: "LOCAL_INFRASTRUCTURE", ExternalValidationRequired: true, Environment: capture.Environment, RegionRef: capture.RegionRef, ClusterRef: capture.ClusterRef,
|
||
|
|
ComponentCount: len(capture.Components), VerifiedComponents: []string{}, ControlCounts: map[string]int{}, InputSHA256: map[string]string{}, Checks: []Check{},
|
||
|
|
Disclaimer: "基础设施一致性报告只证明所提供候选、资源引用、探针、配置、控制项和审计相互一致;本地组件不替代生产网络、托管服务和发布审批。",
|
||
|
|
}
|
||
|
|
if capture.EvidenceSource == "PRODUCTION" {
|
||
|
|
report.EvidenceScope = "PRODUCTION_INFRASTRUCTURE"
|
||
|
|
}
|
||
|
|
add := func(code string, ok bool, detail string) {
|
||
|
|
status := "PASS"
|
||
|
|
if !ok {
|
||
|
|
status, report.Result = "FAIL", "FAIL"
|
||
|
|
}
|
||
|
|
report.Checks = append(report.Checks, Check{Code: code, Status: status, Detail: detail})
|
||
|
|
}
|
||
|
|
add("CAPTURE_SCHEMA", capture.SchemaVersion == CaptureSchema, "基础设施证据必须使用"+CaptureSchema)
|
||
|
|
add("EVIDENCE_SOURCE", capture.EvidenceSource == "LOCAL" || capture.EvidenceSource == "PRODUCTION", "证据来源必须明确为LOCAL或PRODUCTION")
|
||
|
|
add("CANDIDATE_IDENTITY", candidateVersion != "" && digestPattern.MatchString(buildDigest), "候选版本和sha256构建摘要必须完整")
|
||
|
|
add("CAPTURE_CANDIDATE_BINDING", capture.CandidateVersion == candidateVersion && strings.ToLower(capture.BuildDigest) == buildDigest, "基础设施采集必须绑定同一候选版本和构建摘要")
|
||
|
|
real := capture.EvidenceSource == "PRODUCTION"
|
||
|
|
environmentOK := capture.AttestedByRef != "" && !capture.AttestedAt.IsZero() && !capture.CollectedAt.IsZero() && !capture.AttestedAt.After(capture.CollectedAt)
|
||
|
|
if real {
|
||
|
|
environmentOK = environmentOK && (capture.Environment == "STAGING" || capture.Environment == "PRODUCTION") && capture.RegionRef != "" && capture.ClusterRef != "" && capture.ChangeTicketRef != ""
|
||
|
|
} else {
|
||
|
|
environmentOK = environmentOK && capture.Environment == "LOCAL"
|
||
|
|
}
|
||
|
|
add("ENVIRONMENT_IDENTITY", environmentOK, "生产证据必须绑定环境、区域、集群、变更工单和复核人")
|
||
|
|
|
||
|
|
componentsOK, controlsOK, refsOK, timeOK := len(capture.Components) > 0, true, true, true
|
||
|
|
seen := map[string]bool{}
|
||
|
|
for _, component := range capture.Components {
|
||
|
|
if !slices.Contains(RequiredComponents, component.ComponentType) || seen[component.ComponentType] || component.ResourceRef == "" || component.Status != "VERIFIED" {
|
||
|
|
componentsOK = false
|
||
|
|
}
|
||
|
|
seen[component.ComponentType] = true
|
||
|
|
report.ControlCounts[component.ComponentType] = len(component.VerifiedControls)
|
||
|
|
timeOK = timeOK && !component.ObservedAt.IsZero() && !component.ObservedAt.After(capture.CollectedAt)
|
||
|
|
refsOK = refsOK && component.ProbeRef != "" && component.ConfigurationRef != "" && component.AuditRef != ""
|
||
|
|
if real {
|
||
|
|
for _, control := range requiredControls[component.ComponentType] {
|
||
|
|
controlsOK = controlsOK && slices.Contains(component.VerifiedControls, control)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for component := range seen {
|
||
|
|
report.VerifiedComponents = append(report.VerifiedComponents, component)
|
||
|
|
}
|
||
|
|
slices.Sort(report.VerifiedComponents)
|
||
|
|
if real {
|
||
|
|
for _, component := range RequiredComponents {
|
||
|
|
componentsOK = componentsOK && seen[component]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
add("COMPONENT_COVERAGE", componentsOK, "生产基础设施必须覆盖域名TLS、OIDC、PostgreSQL、Redis、对象存储、网络隔离和监控")
|
||
|
|
add("PRODUCTION_CONTROLS", controlsOK, "每类生产组件必须包含其TLS、私网、高可用、备份、隔离或监控控制项")
|
||
|
|
add("OBSERVATION_TIMELINE", timeOK, "所有组件观测时间必须有效且不晚于采集时间")
|
||
|
|
add("EVIDENCE_REFERENCES", refsOK, "每个组件必须保存探针、配置和审计引用")
|
||
|
|
if real && environmentOK && componentsOK && controlsOK && timeOK && refsOK && report.Result == "PASS" {
|
||
|
|
report.ExternalValidationRequired = false
|
||
|
|
}
|
||
|
|
return report
|
||
|
|
}
|
||
|
|
|
||
|
|
func RequiredControls(component string) []string { return slices.Clone(requiredControls[component]) }
|
||
|
|
|
||
|
|
func SHA256(content []byte) string {
|
||
|
|
digest := sha256.Sum256(content)
|
||
|
|
return hex.EncodeToString(digest[:])
|
||
|
|
}
|
||
|
|
|
||
|
|
func decodeStrict(content []byte, target any) error {
|
||
|
|
decoder := json.NewDecoder(bytes.NewReader(content))
|
||
|
|
decoder.DisallowUnknownFields()
|
||
|
|
if err := decoder.Decode(target); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
var trailing any
|
||
|
|
if err := decoder.Decode(&trailing); !errors.Is(err, io.EOF) {
|
||
|
|
return errors.New("trailing JSON data")
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|