package host import ( "context" "crypto/sha256" "encoding/hex" "encoding/json" "errors" "github.com/carlosframework/platform/internal/activator" "github.com/carlosframework/platform/internal/provision" "github.com/carlosframework/platform/internal/registry" "os" "strings" ) func rootRecordRoute(rec *provision.Record) (registry.Route, error) { if rec.Unit == "" { return registry.Route{}, os.ErrPermission } rt := registry.Route{Host: rec.Host, Account: rec.Account, App: rec.App, Kind: rec.KindOrDefault(), Unit: rec.Unit, Addr: rec.Addr, Addrs: strings.Join(rec.Addrs, ","), Channel: rec.Channel, Region: rec.Region, Pool: rec.Pool} if rec.DerivesPaths() { unit := activator.UnitName(rec.Host) if rec.Unit != "" && rec.Unit != unit { return registry.Route{}, os.ErrPermission } rt.Unit, rt.Socket, rt.Backing = unit, provision.DefaultSocket(rec.Host), registry.BackingUnit } if (rt.Unit != "" && !registry.ValidUnitName(rt.Unit)) || (rt.Kind != "instance" && rt.Kind != "service") { return registry.Route{}, os.ErrPermission } if h := rec.HealthOrDefault(); h != nil { body, err := json.Marshal(h.WithDefaults()) if err != nil { return registry.Route{}, err } rt.Health = string(body) } if rt.Socket == "" && len(rec.AddrsOrDefault()) == 0 { return registry.Route{}, os.ErrPermission } return rt, nil } func sameProofCandidate(candidate, r registry.Route, declaredUnit string) bool { return candidate.Account == r.Account && candidate.App == r.App && candidate.Host == r.Host && (candidate.Unit == declaredUnit || candidate.Unit == r.Unit) && candidate.Socket == r.Socket && candidate.Addr == r.Addr && candidate.Addrs == r.Addrs && candidate.Health == r.Health && candidate.Kind == r.Kind && candidate.Backing == r.Backing } func (s *proofBindingSource) needsRestart(ctx context.Context, candidate registry.Route) (bool, error) { evidence, err := s.record(ctx, candidate.Host) if err != nil { if errors.Is(err, os.ErrNotExist) && candidate.Unit == "" { return false, nil } return false, err } rec := evidence.record if evidence.local { _, err := s.forHost(ctx, candidate.Host) return err == nil, err } if rec.Unit == "" { return false, nil } if _, err := s.claimIdentity(ctx, rec); err != nil { return false, err } rt, err := rootRecordRoute(rec) if err != nil { return false, err } if !sameProofCandidate(candidate, rt, rec.Unit) { return false, os.ErrPermission } return rt.Unit != "", nil } func (s *proofBindingSource) claimIdentity(ctx context.Context, rec *provision.Record) (string, error) { if s.claim == nil && s.box == nil { return "", nil } if s.claim == nil || s.box == nil { return "", os.ErrPermission } box, err := s.box(ctx) if err != nil { return "", err } o, err := s.claim(ctx, rec.Host) if err != nil { return "", err } if o.Absent || o.ETag == "" || o.Host != rec.Host || o.Claim == nil || o.Claim.Host != rec.Host || o.Claim.Holder == nil { return "", os.ErrPermission } h := o.Claim.Holder if h.Key != provision.RecordKey(rec.Account, rec.App, rec.Host) || h.Account != rec.Account || h.App != rec.App || h.Incarnation == "" || h.Stamp == 0 || h.Session == 0 || box == "" || h.WriterBox != box || o.Claim.Op.Exec != box { return "", os.ErrPermission } for _, lost := range o.Claim.Lost { if lost == box { return "", os.ErrPermission } } b, err := json.Marshal(o.Claim) if err != nil { return "", err } sum := sha256.Sum256(b) return o.ETag + "@" + hex.EncodeToString(sum[:]) + "@", nil }