v9.4.0
This commit is contained in:
@@ -28,6 +28,8 @@ type Config struct {
|
||||
OIDCIssuer, OIDCClientID, OIDCClientSecret, OIDCRedirectURL, OIDCAdminGroup, OIDCOperatorGroup string
|
||||
AgentToken, HostRoot string
|
||||
AllowHostUserManagement, AllowHostPermissionManagement bool
|
||||
HostSecurityEnabled, AllowHostSecurityChanges, AllowHostPackageManagement bool
|
||||
HostSecurityPID int
|
||||
CheckConcurrency, RetentionDays, AuditRetentionDays int
|
||||
HTTPTimeout time.Duration
|
||||
}
|
||||
@@ -61,6 +63,22 @@ func Load() (Config, error) {
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
hostSecurityEnabled, err := envBoolStrict("HOST_SECURITY_ENABLED", false)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
allowHostSecurityChanges, err := envBoolStrict("ALLOW_HOST_SECURITY_CHANGES", false)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
allowHostPackageManagement, err := envBoolStrict("ALLOW_HOST_PACKAGE_MANAGEMENT", false)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
hostSecurityPID, err := envIntStrict("HOST_SECURITY_HOST_PID", 1)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
c := Config{
|
||||
Mode: Mode(env("APP_MODE", "standalone")),
|
||||
ListenAddr: env("LISTEN_ADDR", ":8080"),
|
||||
@@ -79,6 +97,10 @@ func Load() (Config, error) {
|
||||
HostRoot: cleanOptionalPath(os.Getenv("HOST_ROOT")),
|
||||
AllowHostUserManagement: allowHostUserManagement,
|
||||
AllowHostPermissionManagement: allowHostPermissionManagement,
|
||||
HostSecurityEnabled: hostSecurityEnabled,
|
||||
AllowHostSecurityChanges: allowHostSecurityChanges,
|
||||
AllowHostPackageManagement: allowHostPackageManagement,
|
||||
HostSecurityPID: hostSecurityPID,
|
||||
CheckConcurrency: checkConcurrency,
|
||||
RetentionDays: retentionDays,
|
||||
AuditRetentionDays: auditRetentionDays,
|
||||
@@ -120,6 +142,18 @@ func Load() (Config, error) {
|
||||
if c.AllowHostPermissionManagement && c.HostRoot == "" {
|
||||
return c, errors.New("ALLOW_HOST_PERMISSION_MANAGEMENT=true requires HOST_ROOT")
|
||||
}
|
||||
if c.HostSecurityEnabled && c.HostRoot == "" {
|
||||
return c, errors.New("HOST_SECURITY_ENABLED=true requires HOST_ROOT")
|
||||
}
|
||||
if c.AllowHostSecurityChanges && !c.HostSecurityEnabled {
|
||||
return c, errors.New("ALLOW_HOST_SECURITY_CHANGES=true requires HOST_SECURITY_ENABLED=true")
|
||||
}
|
||||
if c.AllowHostPackageManagement && !c.AllowHostSecurityChanges {
|
||||
return c, errors.New("ALLOW_HOST_PACKAGE_MANAGEMENT=true requires ALLOW_HOST_SECURITY_CHANGES=true")
|
||||
}
|
||||
if c.HostSecurityPID < 1 {
|
||||
return c, errors.New("HOST_SECURITY_HOST_PID must be greater than 0")
|
||||
}
|
||||
if c.Mode == ModeAgent {
|
||||
if len(c.AgentToken) < 24 {
|
||||
return c, errors.New("AGENT_TOKEN must be at least 24 characters in agent mode")
|
||||
|
||||
@@ -42,3 +42,24 @@ func TestHostPermissionManagementRequiresHostRoot(t *testing.T) {
|
||||
t.Fatal("expected host permission management without HOST_ROOT to fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostSecurityRequiresHostRoot(t *testing.T) {
|
||||
t.Setenv("AUTH_DISABLED", "true")
|
||||
t.Setenv("APP_SECRET", "01234567890123456789012345678901")
|
||||
t.Setenv("HOST_SECURITY_ENABLED", "true")
|
||||
t.Setenv("HOST_ROOT", "")
|
||||
if _, err := Load(); err == nil {
|
||||
t.Fatal("expected host security without HOST_ROOT to fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostSecurityPackageManagementRequiresChanges(t *testing.T) {
|
||||
t.Setenv("AUTH_DISABLED", "true")
|
||||
t.Setenv("APP_SECRET", "01234567890123456789012345678901")
|
||||
t.Setenv("HOST_ROOT", "/host")
|
||||
t.Setenv("HOST_SECURITY_ENABLED", "true")
|
||||
t.Setenv("ALLOW_HOST_PACKAGE_MANAGEMENT", "true")
|
||||
if _, err := Load(); err == nil {
|
||||
t.Fatal("expected package management without security changes opt-in to fail")
|
||||
}
|
||||
}
|
||||
|
||||
+370
-2
@@ -13,6 +13,7 @@ import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.send.nrw/sendnrw/dockwatch/internal/audit"
|
||||
"git.send.nrw/sendnrw/dockwatch/internal/auth"
|
||||
@@ -20,6 +21,7 @@ import (
|
||||
"git.send.nrw/sendnrw/dockwatch/internal/composeedit"
|
||||
"git.send.nrw/sendnrw/dockwatch/internal/config"
|
||||
"git.send.nrw/sendnrw/dockwatch/internal/gitops"
|
||||
"git.send.nrw/sendnrw/dockwatch/internal/hostsecurity"
|
||||
"git.send.nrw/sendnrw/dockwatch/internal/monitor"
|
||||
"git.send.nrw/sendnrw/dockwatch/internal/nodes"
|
||||
"git.send.nrw/sendnrw/dockwatch/internal/notify"
|
||||
@@ -37,10 +39,11 @@ type Server struct {
|
||||
audit *audit.Service
|
||||
notify *notify.Service
|
||||
git *gitops.Service
|
||||
security *hostsecurity.Service
|
||||
}
|
||||
|
||||
func New(c config.Config, a *auth.Service, ss *stacks.Service, n *nodes.Manager, m *monitor.Service, au *audit.Service, nt *notify.Service, gs *gitops.Service) http.Handler {
|
||||
s := &Server{cfg: c, auth: a, stacks: ss, nodes: n, monitors: m, audit: au, notify: nt, git: gs}
|
||||
func New(c config.Config, a *auth.Service, ss *stacks.Service, n *nodes.Manager, m *monitor.Service, au *audit.Service, nt *notify.Service, gs *gitops.Service, hs *hostsecurity.Service) http.Handler {
|
||||
s := &Server{cfg: c, auth: a, stacks: ss, nodes: n, monitors: m, audit: au, notify: nt, git: gs, security: hs}
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("GET /healthz", func(w http.ResponseWriter, r *http.Request) {
|
||||
jsonOut(w, 200, map[string]any{"ok": true, "mode": c.Mode, "build": buildinfo.Current()})
|
||||
@@ -129,6 +132,18 @@ func New(c config.Config, a *auth.Service, ss *stacks.Service, n *nodes.Manager,
|
||||
api.Handle("PUT /api/nodes/{id}", auth.RequireRole("admin", http.HandlerFunc(s.updateNode)))
|
||||
api.Handle("DELETE /api/nodes/{id}", auth.RequireRole("admin", http.HandlerFunc(s.deleteNode)))
|
||||
api.HandleFunc("GET /api/nodes/{id}/health", s.nodeHealth)
|
||||
api.Handle("GET /api/security/status", auth.RequireRole("admin", http.HandlerFunc(s.securityStatus)))
|
||||
api.Handle("GET /api/security/firewall", auth.RequireRole("admin", http.HandlerFunc(s.securityFirewall)))
|
||||
api.Handle("POST /api/security/firewall/preview", auth.RequireRole("admin", http.HandlerFunc(s.securityFirewallPreview)))
|
||||
api.Handle("POST /api/security/firewall/apply", auth.RequireRole("admin", http.HandlerFunc(s.securityFirewallApply)))
|
||||
api.Handle("POST /api/security/firewall/commit", auth.RequireRole("admin", http.HandlerFunc(s.securityFirewallCommit)))
|
||||
api.Handle("POST /api/security/firewall/rollback", auth.RequireRole("admin", http.HandlerFunc(s.securityFirewallRollback)))
|
||||
api.Handle("GET /api/security/fail2ban", auth.RequireRole("admin", http.HandlerFunc(s.securityFail2Ban)))
|
||||
api.Handle("PUT /api/security/fail2ban", auth.RequireRole("admin", http.HandlerFunc(s.securityApplyFail2Ban)))
|
||||
api.Handle("GET /api/security/auditd", auth.RequireRole("admin", http.HandlerFunc(s.securityAuditd)))
|
||||
api.Handle("PUT /api/security/auditd", auth.RequireRole("admin", http.HandlerFunc(s.securityApplyAuditd)))
|
||||
api.Handle("POST /api/security/components/{component}/install", auth.RequireRole("admin", http.HandlerFunc(s.securityInstall)))
|
||||
api.Handle("POST /api/security/components/{component}/actions/{action}", auth.RequireRole("admin", http.HandlerFunc(s.securityComponentAction)))
|
||||
mux.Handle("/api/", a.Middleware(mutationOriginGuard(s.auditMiddleware(api))))
|
||||
assets, _ := fs.Sub(web.FS, ".")
|
||||
f := http.FileServer(http.FS(assets))
|
||||
@@ -149,6 +164,18 @@ func (s *Server) agent(m *http.ServeMux) {
|
||||
a.HandleFunc("POST /agent/v1/docker/containers/{id}/bind-permissions/preview", s.localBindPermissionPreview)
|
||||
a.HandleFunc("POST /agent/v1/host/bind-permissions/repair", s.localRepairBindPermissions)
|
||||
a.HandleFunc("POST /agent/v1/host/users", s.localCreateHostUser)
|
||||
a.HandleFunc("GET /agent/v1/security/status", s.localSecurityStatus)
|
||||
a.HandleFunc("GET /agent/v1/security/firewall", s.localSecurityFirewall)
|
||||
a.HandleFunc("POST /agent/v1/security/firewall/preview", s.localSecurityFirewallPreview)
|
||||
a.HandleFunc("POST /agent/v1/security/firewall/apply", s.localSecurityFirewallApply)
|
||||
a.HandleFunc("POST /agent/v1/security/firewall/commit", s.localSecurityFirewallCommit)
|
||||
a.HandleFunc("POST /agent/v1/security/firewall/rollback", s.localSecurityFirewallRollback)
|
||||
a.HandleFunc("GET /agent/v1/security/fail2ban", s.localSecurityFail2Ban)
|
||||
a.HandleFunc("PUT /agent/v1/security/fail2ban", s.localSecurityApplyFail2Ban)
|
||||
a.HandleFunc("GET /agent/v1/security/auditd", s.localSecurityAuditd)
|
||||
a.HandleFunc("PUT /agent/v1/security/auditd", s.localSecurityApplyAuditd)
|
||||
a.HandleFunc("POST /agent/v1/security/components/{component}/install", s.localSecurityInstall)
|
||||
a.HandleFunc("POST /agent/v1/security/components/{component}/actions/{action}", s.localSecurityComponentAction)
|
||||
a.HandleFunc("GET /agent/v1/stacks", s.localList)
|
||||
a.HandleFunc("GET /agent/v1/stacks/{name}", s.localGet)
|
||||
a.HandleFunc("PUT /agent/v1/stacks/{name}", s.localSave)
|
||||
@@ -558,6 +585,19 @@ func (s *Server) relay(w http.ResponseWriter, r *http.Request, id int64, method,
|
||||
w.WriteHeader(status)
|
||||
_, _ = w.Write(b)
|
||||
}
|
||||
func (s *Server) relayWithTimeout(w http.ResponseWriter, r *http.Request, id int64, method, path string, body any, timeout time.Duration) {
|
||||
b, status, e := s.nodes.DoWithTimeout(r.Context(), id, method, path, body, timeout)
|
||||
if e != nil {
|
||||
if status == 0 {
|
||||
status = 502
|
||||
}
|
||||
http.Error(w, e.Error(), status)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(status)
|
||||
_, _ = w.Write(b)
|
||||
}
|
||||
func (s *Server) dockerInventory(w http.ResponseWriter, r *http.Request) {
|
||||
kind := r.PathValue("kind")
|
||||
if id := nodeID(r); id > 0 {
|
||||
@@ -1384,3 +1424,331 @@ func (s *Server) proxyTerminal(w http.ResponseWriter, r *http.Request, id int64)
|
||||
case <-done:
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) securityStatus(w http.ResponseWriter, r *http.Request) {
|
||||
if id := nodeID(r); id > 0 {
|
||||
s.relay(w, r, id, http.MethodGet, "/agent/v1/security/status", nil)
|
||||
return
|
||||
}
|
||||
s.localSecurityStatus(w, r)
|
||||
}
|
||||
|
||||
func (s *Server) localSecurityStatus(w http.ResponseWriter, r *http.Request) {
|
||||
if s.security == nil {
|
||||
http.Error(w, "host security service unavailable", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
jsonOut(w, http.StatusOK, s.security.Status(r.Context()))
|
||||
}
|
||||
|
||||
func (s *Server) securityFirewall(w http.ResponseWriter, r *http.Request) {
|
||||
if id := nodeID(r); id > 0 {
|
||||
s.relay(w, r, id, http.MethodGet, "/agent/v1/security/firewall", nil)
|
||||
return
|
||||
}
|
||||
s.localSecurityFirewall(w, r)
|
||||
}
|
||||
|
||||
func (s *Server) localSecurityFirewall(w http.ResponseWriter, r *http.Request) {
|
||||
if s.security == nil {
|
||||
http.Error(w, "host security service unavailable", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
jsonOut(w, http.StatusOK, s.security.FirewallPolicy())
|
||||
}
|
||||
|
||||
func (s *Server) securityFirewallPreview(w http.ResponseWriter, r *http.Request) {
|
||||
var in hostsecurity.FirewallPolicy
|
||||
if e := read(r, &in); e != nil {
|
||||
http.Error(w, e.Error(), 400)
|
||||
return
|
||||
}
|
||||
if id := nodeID(r); id > 0 {
|
||||
s.relay(w, r, id, http.MethodPost, "/agent/v1/security/firewall/preview", in)
|
||||
return
|
||||
}
|
||||
s.securityFirewallPreviewLocal(w, r, in)
|
||||
}
|
||||
func (s *Server) localSecurityFirewallPreview(w http.ResponseWriter, r *http.Request) {
|
||||
var in hostsecurity.FirewallPolicy
|
||||
if e := read(r, &in); e != nil {
|
||||
http.Error(w, e.Error(), 400)
|
||||
return
|
||||
}
|
||||
s.securityFirewallPreviewLocal(w, r, in)
|
||||
}
|
||||
func (s *Server) securityFirewallPreviewLocal(w http.ResponseWriter, r *http.Request, in hostsecurity.FirewallPolicy) {
|
||||
if s.security == nil {
|
||||
http.Error(w, "host security service unavailable", 503)
|
||||
return
|
||||
}
|
||||
v, e := s.security.PreviewFirewall(r.Context(), in)
|
||||
if e != nil {
|
||||
http.Error(w, e.Error(), 400)
|
||||
return
|
||||
}
|
||||
jsonOut(w, 200, v)
|
||||
}
|
||||
|
||||
func (s *Server) securityFirewallApply(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
Policy hostsecurity.FirewallPolicy `json:"policy"`
|
||||
RollbackSeconds int `json:"rollback_seconds"`
|
||||
}
|
||||
if e := read(r, &in); e != nil {
|
||||
http.Error(w, e.Error(), 400)
|
||||
return
|
||||
}
|
||||
if id := nodeID(r); id > 0 {
|
||||
s.relay(w, r, id, http.MethodPost, "/agent/v1/security/firewall/apply", in)
|
||||
return
|
||||
}
|
||||
s.securityFirewallApplyLocal(w, r, in.Policy, in.RollbackSeconds)
|
||||
}
|
||||
func (s *Server) localSecurityFirewallApply(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
Policy hostsecurity.FirewallPolicy `json:"policy"`
|
||||
RollbackSeconds int `json:"rollback_seconds"`
|
||||
}
|
||||
if e := read(r, &in); e != nil {
|
||||
http.Error(w, e.Error(), 400)
|
||||
return
|
||||
}
|
||||
s.securityFirewallApplyLocal(w, r, in.Policy, in.RollbackSeconds)
|
||||
}
|
||||
func (s *Server) securityFirewallApplyLocal(w http.ResponseWriter, r *http.Request, p hostsecurity.FirewallPolicy, seconds int) {
|
||||
if s.security == nil {
|
||||
http.Error(w, "host security service unavailable", 503)
|
||||
return
|
||||
}
|
||||
v, e := s.security.ApplyFirewall(r.Context(), p, seconds)
|
||||
if e != nil {
|
||||
http.Error(w, e.Error(), 400)
|
||||
return
|
||||
}
|
||||
jsonOut(w, 200, v)
|
||||
}
|
||||
|
||||
func (s *Server) securityFirewallCommit(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
ChangeID string `json:"change_id"`
|
||||
}
|
||||
if e := read(r, &in); e != nil {
|
||||
http.Error(w, e.Error(), 400)
|
||||
return
|
||||
}
|
||||
if id := nodeID(r); id > 0 {
|
||||
s.relay(w, r, id, http.MethodPost, "/agent/v1/security/firewall/commit", in)
|
||||
return
|
||||
}
|
||||
s.securityFirewallCommitLocal(w, r, in.ChangeID)
|
||||
}
|
||||
func (s *Server) localSecurityFirewallCommit(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
ChangeID string `json:"change_id"`
|
||||
}
|
||||
if e := read(r, &in); e != nil {
|
||||
http.Error(w, e.Error(), 400)
|
||||
return
|
||||
}
|
||||
s.securityFirewallCommitLocal(w, r, in.ChangeID)
|
||||
}
|
||||
func (s *Server) securityFirewallCommitLocal(w http.ResponseWriter, r *http.Request, id string) {
|
||||
if s.security == nil {
|
||||
http.Error(w, "host security service unavailable", 503)
|
||||
return
|
||||
}
|
||||
if e := s.security.CommitFirewall(id); e != nil {
|
||||
http.Error(w, e.Error(), 400)
|
||||
return
|
||||
}
|
||||
jsonOut(w, 200, map[string]any{"ok": true, "message": "Firewall change committed."})
|
||||
}
|
||||
|
||||
func (s *Server) securityFirewallRollback(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
ChangeID string `json:"change_id"`
|
||||
}
|
||||
if e := read(r, &in); e != nil {
|
||||
http.Error(w, e.Error(), 400)
|
||||
return
|
||||
}
|
||||
if id := nodeID(r); id > 0 {
|
||||
s.relay(w, r, id, http.MethodPost, "/agent/v1/security/firewall/rollback", in)
|
||||
return
|
||||
}
|
||||
s.securityFirewallRollbackLocal(w, r, in.ChangeID)
|
||||
}
|
||||
func (s *Server) localSecurityFirewallRollback(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
ChangeID string `json:"change_id"`
|
||||
}
|
||||
if e := read(r, &in); e != nil {
|
||||
http.Error(w, e.Error(), 400)
|
||||
return
|
||||
}
|
||||
s.securityFirewallRollbackLocal(w, r, in.ChangeID)
|
||||
}
|
||||
func (s *Server) securityFirewallRollbackLocal(w http.ResponseWriter, r *http.Request, id string) {
|
||||
if s.security == nil {
|
||||
http.Error(w, "host security service unavailable", 503)
|
||||
return
|
||||
}
|
||||
if e := s.security.RollbackFirewall(r.Context(), id); e != nil {
|
||||
http.Error(w, e.Error(), 400)
|
||||
return
|
||||
}
|
||||
jsonOut(w, 200, map[string]any{"ok": true, "message": "Firewall change rolled back."})
|
||||
}
|
||||
|
||||
func (s *Server) securityFail2Ban(w http.ResponseWriter, r *http.Request) {
|
||||
if id := nodeID(r); id > 0 {
|
||||
s.relay(w, r, id, http.MethodGet, "/agent/v1/security/fail2ban", nil)
|
||||
return
|
||||
}
|
||||
s.localSecurityFail2Ban(w, r)
|
||||
}
|
||||
func (s *Server) localSecurityFail2Ban(w http.ResponseWriter, r *http.Request) {
|
||||
if s.security == nil {
|
||||
http.Error(w, "host security service unavailable", 503)
|
||||
return
|
||||
}
|
||||
jsonOut(w, 200, s.security.Fail2BanPolicy())
|
||||
}
|
||||
func (s *Server) securityApplyFail2Ban(w http.ResponseWriter, r *http.Request) {
|
||||
var in hostsecurity.Fail2BanPolicy
|
||||
if e := read(r, &in); e != nil {
|
||||
http.Error(w, e.Error(), 400)
|
||||
return
|
||||
}
|
||||
if id := nodeID(r); id > 0 {
|
||||
s.relay(w, r, id, http.MethodPut, "/agent/v1/security/fail2ban", in)
|
||||
return
|
||||
}
|
||||
s.securityApplyFail2BanLocal(w, r, in)
|
||||
}
|
||||
func (s *Server) localSecurityApplyFail2Ban(w http.ResponseWriter, r *http.Request) {
|
||||
var in hostsecurity.Fail2BanPolicy
|
||||
if e := read(r, &in); e != nil {
|
||||
http.Error(w, e.Error(), 400)
|
||||
return
|
||||
}
|
||||
s.securityApplyFail2BanLocal(w, r, in)
|
||||
}
|
||||
func (s *Server) securityApplyFail2BanLocal(w http.ResponseWriter, r *http.Request, in hostsecurity.Fail2BanPolicy) {
|
||||
if s.security == nil {
|
||||
http.Error(w, "host security service unavailable", 503)
|
||||
return
|
||||
}
|
||||
v, e := s.security.ApplyFail2Ban(r.Context(), in)
|
||||
if e != nil {
|
||||
http.Error(w, e.Error(), 400)
|
||||
return
|
||||
}
|
||||
jsonOut(w, 200, v)
|
||||
}
|
||||
|
||||
func (s *Server) securityAuditd(w http.ResponseWriter, r *http.Request) {
|
||||
if id := nodeID(r); id > 0 {
|
||||
s.relay(w, r, id, http.MethodGet, "/agent/v1/security/auditd", nil)
|
||||
return
|
||||
}
|
||||
s.localSecurityAuditd(w, r)
|
||||
}
|
||||
func (s *Server) localSecurityAuditd(w http.ResponseWriter, r *http.Request) {
|
||||
if s.security == nil {
|
||||
http.Error(w, "host security service unavailable", 503)
|
||||
return
|
||||
}
|
||||
jsonOut(w, 200, s.security.AuditdPolicy())
|
||||
}
|
||||
func (s *Server) securityApplyAuditd(w http.ResponseWriter, r *http.Request) {
|
||||
var in hostsecurity.AuditdPolicy
|
||||
if e := read(r, &in); e != nil {
|
||||
http.Error(w, e.Error(), 400)
|
||||
return
|
||||
}
|
||||
if id := nodeID(r); id > 0 {
|
||||
s.relay(w, r, id, http.MethodPut, "/agent/v1/security/auditd", in)
|
||||
return
|
||||
}
|
||||
s.securityApplyAuditdLocal(w, r, in)
|
||||
}
|
||||
func (s *Server) localSecurityApplyAuditd(w http.ResponseWriter, r *http.Request) {
|
||||
var in hostsecurity.AuditdPolicy
|
||||
if e := read(r, &in); e != nil {
|
||||
http.Error(w, e.Error(), 400)
|
||||
return
|
||||
}
|
||||
s.securityApplyAuditdLocal(w, r, in)
|
||||
}
|
||||
func (s *Server) securityApplyAuditdLocal(w http.ResponseWriter, r *http.Request, in hostsecurity.AuditdPolicy) {
|
||||
if s.security == nil {
|
||||
http.Error(w, "host security service unavailable", 503)
|
||||
return
|
||||
}
|
||||
v, e := s.security.ApplyAuditd(r.Context(), in)
|
||||
if e != nil {
|
||||
http.Error(w, e.Error(), 400)
|
||||
return
|
||||
}
|
||||
jsonOut(w, 200, v)
|
||||
}
|
||||
|
||||
func (s *Server) securityInstall(w http.ResponseWriter, r *http.Request) {
|
||||
var in hostsecurity.InstallInput
|
||||
if e := read(r, &in); e != nil {
|
||||
http.Error(w, e.Error(), 400)
|
||||
return
|
||||
}
|
||||
component := r.PathValue("component")
|
||||
if id := nodeID(r); id > 0 {
|
||||
s.relayWithTimeout(w, r, id, http.MethodPost, "/agent/v1/security/components/"+url.PathEscape(component)+"/install", in, 10*time.Minute)
|
||||
return
|
||||
}
|
||||
s.securityInstallLocal(w, r, component, in)
|
||||
}
|
||||
func (s *Server) localSecurityInstall(w http.ResponseWriter, r *http.Request) {
|
||||
var in hostsecurity.InstallInput
|
||||
if e := read(r, &in); e != nil {
|
||||
http.Error(w, e.Error(), 400)
|
||||
return
|
||||
}
|
||||
s.securityInstallLocal(w, r, r.PathValue("component"), in)
|
||||
}
|
||||
func (s *Server) securityInstallLocal(w http.ResponseWriter, r *http.Request, component string, in hostsecurity.InstallInput) {
|
||||
if s.security == nil {
|
||||
http.Error(w, "host security service unavailable", 503)
|
||||
return
|
||||
}
|
||||
v, e := s.security.Install(r.Context(), component, in)
|
||||
if e != nil {
|
||||
http.Error(w, e.Error(), 400)
|
||||
return
|
||||
}
|
||||
jsonOut(w, 200, v)
|
||||
}
|
||||
|
||||
func (s *Server) securityComponentAction(w http.ResponseWriter, r *http.Request) {
|
||||
component, action := r.PathValue("component"), r.PathValue("action")
|
||||
if id := nodeID(r); id > 0 {
|
||||
s.relay(w, r, id, http.MethodPost, "/agent/v1/security/components/"+url.PathEscape(component)+"/actions/"+url.PathEscape(action), map[string]any{})
|
||||
return
|
||||
}
|
||||
s.securityComponentActionLocal(w, r, component, action)
|
||||
}
|
||||
func (s *Server) localSecurityComponentAction(w http.ResponseWriter, r *http.Request) {
|
||||
s.securityComponentActionLocal(w, r, r.PathValue("component"), r.PathValue("action"))
|
||||
}
|
||||
func (s *Server) securityComponentActionLocal(w http.ResponseWriter, r *http.Request, component, action string) {
|
||||
if s.security == nil {
|
||||
http.Error(w, "host security service unavailable", 503)
|
||||
return
|
||||
}
|
||||
v, e := s.security.ComponentAction(r.Context(), component, action)
|
||||
if e != nil {
|
||||
http.Error(w, e.Error(), 400)
|
||||
return
|
||||
}
|
||||
jsonOut(w, 200, v)
|
||||
}
|
||||
|
||||
@@ -14,5 +14,5 @@ func TestRouterPatternsDoNotConflict(t *testing.T) {
|
||||
t.Fatalf("ServeMux route conflict: %v", r)
|
||||
}
|
||||
}()
|
||||
_ = New(config.Config{Mode: config.ModeStandalone}, &auth.Service{}, nil, nil, nil, (*audit.Service)(nil), nil, nil)
|
||||
_ = New(config.Config{Mode: config.ModeStandalone}, &auth.Service{}, nil, nil, nil, (*audit.Service)(nil), nil, nil, nil)
|
||||
}
|
||||
|
||||
+10
-1
@@ -155,6 +155,13 @@ func (m *Manager) get(ctx context.Context, id int64) (storedNode, error) {
|
||||
return n, nil
|
||||
}
|
||||
func (m *Manager) Do(ctx context.Context, id int64, method, path string, body any) ([]byte, int, error) {
|
||||
return m.DoWithTimeout(ctx, id, method, path, body, m.client.Timeout)
|
||||
}
|
||||
|
||||
// DoWithTimeout performs an authenticated agent request with an operation-specific
|
||||
// timeout. Long-running host package operations use this rather than weakening
|
||||
// the normal 30-second control-plane timeout for every request.
|
||||
func (m *Manager) DoWithTimeout(ctx context.Context, id int64, method, path string, body any, timeout time.Duration) ([]byte, int, error) {
|
||||
n, err := m.get(ctx, id)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
@@ -178,7 +185,9 @@ func (m *Manager) Do(ctx context.Context, id int64, method, path string, body an
|
||||
if body != nil {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
resp, err := m.client.Do(req)
|
||||
client := *m.client
|
||||
client.Timeout = timeout
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user