5232fcb808
R-023: Zero-trust enforcement operationally wired. ACL enforcement (C-45 staged rollout): - acl.Check wired into all 5 daemon handlers (dispatch/jobs/nodes/tasks) - health endpoints exempt (liveness probes not gated) - ACL log-only mode default (config acl.enforce=false); enforce after bootstrap ACL verified - sshpush auth: ORCA_OIDC_TOKEN validated against JWKS before apply - txn apply: Authorize hook validates OIDC token before running pull - acl.json mode 0600 (was 0644) - flock on acl.json for concurrent grant/revoke - bootstrap ACL: init grants cluster-admin to orca-admins group + SVID Audit actor identity: - currentActor reads OIDC sub from credentials.json (was hardcoded "cli") - threaded through all audit.Record calls via context WebAuthn registration auth: - BeginRegistration/FinishRegistration require authenticated session - fail-closed 401 when no authFunc configured New files: internal/daemon/acl.go, internal/cli/authactor.go, internal/engine/actor.go, internal/identity/authtoken.go, internal/sshpush/auth.go, internal/txn/auth_test.go ---ci--- project: orca phase: 4 milestone: v0.13 status: complete requirements: covered: [153] ---/ci---
42 lines
1.1 KiB
Go
42 lines
1.1 KiB
Go
package daemon
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"time"
|
|
|
|
"git.cloudinit.dev/coreci/orca/internal/acl"
|
|
"git.cloudinit.dev/coreci/orca/internal/model"
|
|
"git.cloudinit.dev/coreci/orca/internal/store"
|
|
)
|
|
|
|
// handleNodesCollection handles /v1/nodes (GET only in v0.1).
|
|
// Node registration is CLI-only; the API is read-only for observability.
|
|
func (s *Server) handleNodesCollection(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
writeError(w, http.StatusMethodNotAllowed, "method not allowed")
|
|
return
|
|
}
|
|
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
// P04 ACL enforcement (C-44). Node list is a cluster-wide read.
|
|
ns := "_defaults"
|
|
if s.acl != nil {
|
|
if id, ok := s.acl.Check(r, ns, acl.PermRead); !ok {
|
|
deny(w, id, ns, acl.PermRead)
|
|
return
|
|
}
|
|
}
|
|
|
|
nodes, err := store.NewNodeRepo(s.db).List(ctx)
|
|
if err != nil {
|
|
writeError(w, http.StatusInternalServerError, "failed to list nodes")
|
|
return
|
|
}
|
|
if nodes == nil {
|
|
nodes = []*model.Node{}
|
|
}
|
|
writeJSON(w, http.StatusOK, map[string]any{"nodes": nodes, "count": len(nodes)})
|
|
}
|