Go-based network automation with YANG models, gRPC, Ansible, Terraform, and Kubernetes integration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
94 lines
2.6 KiB
Go
94 lines
2.6 KiB
Go
package shellstream
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
)
|
|
|
|
// AccordPolicy defines the local node's policy for granting capabilities
|
|
// to incoming Shellstream sessions, derived from Guildhouse accords.
|
|
type AccordPolicy struct {
|
|
TrustTiers map[string]TierPolicy `json:"trust_tiers"`
|
|
}
|
|
|
|
// TierPolicy maps a Guildhouse trust tier to allowed Kedge modes and operations.
|
|
type TierPolicy struct {
|
|
Mode []string `json:"mode"` // ["overlay"], ["overlay", "underlay"]
|
|
Operations []string `json:"operations"` // ["read"], ["read", "mutate", "admin"]
|
|
Subnets []string `json:"subnets"` // ["172.16.0.0/24", "10.0.1.0/24"]
|
|
}
|
|
|
|
// EvaluateCapability evaluates a capability request against the local accord policy.
|
|
// Returns the attenuated grant (requested capabilities intersected with policy).
|
|
func EvaluateCapability(req CapabilityRequest, policyPath string) (*CapabilityGrant, error) {
|
|
policy, err := loadAccordPolicy(policyPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load accord policy: %w", err)
|
|
}
|
|
|
|
// TODO: Determine the remote peer's trust tier from its SAT token.
|
|
// For Phase 1, assume "journeyman" tier.
|
|
tier := "journeyman"
|
|
|
|
tierPolicy, ok := policy.TrustTiers[tier]
|
|
if !ok {
|
|
return nil, fmt.Errorf("unknown trust tier: %s", tier)
|
|
}
|
|
|
|
// Attenuate: grant the intersection of requested and allowed.
|
|
grantedMode := attenuateMode(req.Mode, tierPolicy.Mode)
|
|
if grantedMode == "" {
|
|
return nil, fmt.Errorf("requested mode %s not allowed for tier %s", req.Mode, tier)
|
|
}
|
|
|
|
grantedOps := attenuateOperations(req.Operations, tierPolicy.Operations)
|
|
if len(grantedOps) == 0 {
|
|
return nil, fmt.Errorf("no operations allowed for tier %s", tier)
|
|
}
|
|
|
|
return &CapabilityGrant{
|
|
Mode: grantedMode,
|
|
Operations: grantedOps,
|
|
}, nil
|
|
}
|
|
|
|
func loadAccordPolicy(path string) (*AccordPolicy, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var policy AccordPolicy
|
|
if err := json.Unmarshal(data, &policy); err != nil {
|
|
return nil, err
|
|
}
|
|
return &policy, nil
|
|
}
|
|
|
|
func attenuateMode(requested string, allowed []string) string {
|
|
for _, m := range allowed {
|
|
if m == requested || m == "both" {
|
|
return requested
|
|
}
|
|
}
|
|
// If "both" was requested but only one mode allowed, grant the allowed one.
|
|
if requested == "both" && len(allowed) > 0 {
|
|
return allowed[0]
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func attenuateOperations(requested, allowed []string) []string {
|
|
allowedSet := make(map[string]bool)
|
|
for _, op := range allowed {
|
|
allowedSet[op] = true
|
|
}
|
|
|
|
var granted []string
|
|
for _, op := range requested {
|
|
if allowedSet[op] {
|
|
granted = append(granted, op)
|
|
}
|
|
}
|
|
return granted
|
|
}
|