diff --git a/internal/pkg/warewulfd/overlay.go b/internal/pkg/warewulfd/overlay.go index 5f45bb08..74998518 100644 --- a/internal/pkg/warewulfd/overlay.go +++ b/internal/pkg/warewulfd/overlay.go @@ -17,8 +17,9 @@ import ( "github.com/warewulf/warewulf/internal/pkg/wwlog" ) -// HandleOverlay handles system and runtime overlay requests -func HandleOverlay(w http.ResponseWriter, req *http.Request) { +// HandleOverlayList handles requests for an explicit comma-separated list of +// named overlays via the ?overlay= query parameter. +func HandleOverlayList(w http.ResponseWriter, req *http.Request) { ctx, err := initHandleRequest(w, req) if err != nil { return // response already written @@ -30,19 +31,96 @@ func HandleOverlay(w http.ResponseWriter, req *http.Request) { return } - var context string - var request_overlays []string + request_overlays := strings.Split(ctx.rinfo.overlay, ",") + stageFile, err := getOverlayFile( + ctx.remoteNode, + "", + request_overlays, + ctx.conf.Warewulf.AutobuildOverlays()) - if len(ctx.rinfo.overlay) > 0 { - request_overlays = strings.Split(ctx.rinfo.overlay, ",") - } else { - context = ctx.rinfo.stage + if err != nil { + if errors.Is(err, overlay.ErrDoesNotExist) { + w.WriteHeader(http.StatusNotFound) + wwlog.ErrorExc(err, "") + return + } + w.WriteHeader(http.StatusInternalServerError) + wwlog.ErrorExc(err, "") + return + } + + sendResponse(w, req, stageFile, nil, ctx) +} + +// HandleSystemOverlay handles system overlay requests. +// If an explicit ?overlay= list is present, delegates to HandleOverlayList. +func HandleSystemOverlay(w http.ResponseWriter, req *http.Request) { + if len(req.URL.Query()["overlay"]) > 0 { + HandleOverlayList(w, req) + return + } + + ctx, err := initHandleRequest(w, req) + if err != nil { + return // response already written + } + + if !ctx.remoteNode.Valid() { + wwlog.Error("%s (unknown/unconfigured node)", ctx.rinfo.hwaddr) + sendResponse(w, req, "", nil, ctx) + return } stageFile, err := getOverlayFile( ctx.remoteNode, - context, - request_overlays, + "system", + nil, + ctx.conf.Warewulf.AutobuildOverlays()) + + if err != nil { + if errors.Is(err, overlay.ErrDoesNotExist) { + w.WriteHeader(http.StatusNotFound) + wwlog.ErrorExc(err, "") + return + } + w.WriteHeader(http.StatusInternalServerError) + wwlog.ErrorExc(err, "") + return + } + + sendResponse(w, req, stageFile, nil, ctx) +} + +// HandleRuntimeOverlay handles runtime overlay requests. +// If TLS is enabled, returns 403 Forbidden for plain-HTTP requests. +// If an explicit ?overlay= list is present, delegates to HandleOverlayList. +func HandleRuntimeOverlay(w http.ResponseWriter, req *http.Request) { + if config.Get().Warewulf.EnableTLS() && req.TLS == nil { + wwlog.Denied("runtime overlay requested over insecure connection") + w.WriteHeader(http.StatusForbidden) + return + } + + if len(req.URL.Query()["overlay"]) > 0 { + HandleOverlayList(w, req) + return + } + + ctx, err := initHandleRequest(w, req) + if err != nil { + return // response already written + } + + if !ctx.remoteNode.Valid() { + wwlog.Error("%s (unknown/unconfigured node)", ctx.rinfo.hwaddr) + sendResponse(w, req, "", nil, ctx) + return + } + + stageFile, err := getOverlayFile( + ctx.remoteNode, + "runtime", + nil, ctx.conf.Warewulf.AutobuildOverlays()) if err != nil { diff --git a/internal/pkg/warewulfd/provision.go b/internal/pkg/warewulfd/provision.go index b07603c9..cebdfb43 100644 --- a/internal/pkg/warewulfd/provision.go +++ b/internal/pkg/warewulfd/provision.go @@ -47,8 +47,10 @@ func HandleProvision(w http.ResponseWriter, req *http.Request) { handler = HandleKernel case "image": handler = HandleImage - case "system", "runtime": - handler = HandleOverlay + case "system": + handler = HandleSystemOverlay + case "runtime": + handler = HandleRuntimeOverlay case "efiboot": handler = HandleEfiBoot case "shim": diff --git a/internal/pkg/warewulfd/server/server.go b/internal/pkg/warewulfd/server/server.go index 26aa5ca1..87dd9429 100644 --- a/internal/pkg/warewulfd/server/server.go +++ b/internal/pkg/warewulfd/server/server.go @@ -37,7 +37,7 @@ func (h *slashFix) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.mux.ServeHTTP(w, r) } -func configureRootHandler(apiHandler http.Handler, includeRuntime bool) *slashFix { +func configureRootHandler(apiHandler http.Handler) *slashFix { var wwHandler http.ServeMux wwHandler.HandleFunc("/provision/", warewulfd.HandleProvision) wwHandler.HandleFunc("/ipxe/", warewulfd.HandleIpxe) @@ -45,10 +45,8 @@ func configureRootHandler(apiHandler http.Handler, includeRuntime bool) *slashFi wwHandler.HandleFunc("/kernel/", warewulfd.HandleKernel) wwHandler.HandleFunc("/image/", warewulfd.HandleImage) wwHandler.HandleFunc("/container/", warewulfd.HandleImage) - wwHandler.HandleFunc("/overlay-system/", warewulfd.HandleOverlay) - if includeRuntime { - wwHandler.HandleFunc("/overlay-runtime/", warewulfd.HandleOverlay) - } + wwHandler.HandleFunc("/overlay-system/", warewulfd.HandleSystemOverlay) + wwHandler.HandleFunc("/overlay-runtime/", warewulfd.HandleRuntimeOverlay) wwHandler.HandleFunc("/overlay-file/", warewulfd.HandleOverlayFile) wwHandler.HandleFunc("/status", warewulfd.HandleStatus) @@ -87,7 +85,7 @@ func RunServer() error { apiHandler = api.Handler(auth, conf.API.AllowedIPNets()) } - httpHandler := configureRootHandler(apiHandler, !conf.Warewulf.EnableTLS()) + httpHandler := configureRootHandler(apiHandler) errChan := make(chan error, 2) @@ -98,7 +96,7 @@ func RunServer() error { if !util.IsFile(key) || !util.IsFile(crt) { return fmt.Errorf("TLS enabled but keys not found in %s, run 'wwctl configure tls --create' to generate keys", path.Join(conf.Paths.Sysconfdir, "warewulf", "tls")) } - httpsHandler := configureRootHandler(apiHandler, true) + httpsHandler := configureRootHandler(apiHandler) go func() { wwlog.Info("Starting HTTPS service on port %d", conf.Warewulf.SecurePort) if err := http.ListenAndServeTLS(":"+strconv.Itoa(conf.Warewulf.SecurePort), crt, key, httpsHandler); err != nil {