diff --git a/CHANGELOG.md b/CHANGELOG.md index dabf8b57..2888a56a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -133,6 +133,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 the host running warewulf. If node has container it will get these binaries from the container image. +- Added support for booting nodes with grub. Enable this behavior using + warewulf.grubboot: true in warewulf.conf. For unknown nodes, grub.efi + and shim.efi are extracted from the Warewulf host. If the booted node + has a container these binaries are extracted from the container image. ## [4.4.0] 2023-01-18 ### Added diff --git a/internal/pkg/api/profile/profile.go b/internal/pkg/api/profile/profile.go index 349c57a7..50e60afc 100644 --- a/internal/pkg/api/profile/profile.go +++ b/internal/pkg/api/profile/profile.go @@ -24,13 +24,6 @@ func ProfileSet(set *wwapiv1.ProfileSetParameter) (err error) { if err != nil { return errors.Wrap(err, "Could not open database") } - - var pConf node.NodeConf - err = yaml.Unmarshal([]byte(set.NodeConfYaml), &pConf) - if err != nil { - wwlog.Error(fmt.Sprintf("%v", err.Error())) - return - } dbError := apinode.DbSave(&nodeDB) return dbError } diff --git a/internal/pkg/container/grub.go b/internal/pkg/container/grub.go deleted file mode 100644 index ac934581..00000000 --- a/internal/pkg/container/grub.go +++ /dev/null @@ -1,58 +0,0 @@ -package container - -import ( - "os" - "path" - "path/filepath" - - "github.com/hpcng/warewulf/internal/pkg/wwlog" -) - -func grubDirs() []string { - return []string{ - `/usr/share/grub2/x86_64-efi`, - `/usr/share/efi/x86_64/`, - `/boot/efi/EFI/*/`, - } -} -func grubNames() []string { - return []string{ - `grub-tpm.efi`, - `grub.efi`, - `grubx64.efi`, - `grubia32.efi`, - `grubaa64.efi`, - `grubarm.efi`, - } -} - -/* -find a grub.efi in the used container -*/ -func GrubFind(container string) string { - container_path := RootFsDir(container) - wwlog.Debug("Finding grub under paths: %s", container_path) - if container_path == "" { - return "" - } - return GrubFindPath(container_path) -} - -func GrubFindPath(grub_path string) string { - for _, grubdir := range grubDirs() { - wwlog.Debug("Checking grub directory: %s", grubdir) - for _, grubname := range grubNames() { - wwlog.Debug("Checking for grub name: %s", grubname) - grubPaths, _ := filepath.Glob(path.Join(grub_path, grubdir, grubname)) - for _, grubpath := range grubPaths { - wwlog.Debug("Checking for grub path: %s", grubpath) - // Only succeeds if grubpath exists and, if a - // symlink, links to a path that also exists - if _, err := os.Stat(grubpath); err == nil { - return grubpath - } - } - } - } - return "" -} diff --git a/internal/pkg/container/shim.go b/internal/pkg/container/shim.go deleted file mode 100644 index 8d14f2ac..00000000 --- a/internal/pkg/container/shim.go +++ /dev/null @@ -1,59 +0,0 @@ -package container - -import ( - "os" - "path" - "path/filepath" - - "github.com/hpcng/warewulf/internal/pkg/wwlog" -) - -func shimDirs() []string { - return []string{ - `/usr/share/efi/x86_64/`, - `/usr/lib64/efi`, - `/boot/efi/EFI/*/`, - } -} -func shimNames() []string { - return []string{ - `shim.efi`, - `shim-sles.efi`, - `shimx64.efi`, - `shim-susesigned.efi`, - } -} - -/* -find the path of the shim binary in container -*/ -func ShimFind(container string) string { - container_path := RootFsDir(container) - wwlog.Debug("Finding shim under path: %s", container_path) - if container_path == "" { - return "" - } - return ShimFindPath(container_path) -} - -/* -find the path of the shim binary in container -*/ -func ShimFindPath(shimpath string) string { - for _, shimdir := range shimDirs() { - wwlog.Debug("Checking shim directory: %s", shimdir) - for _, shimname := range shimNames() { - wwlog.Debug("Checking for shim name: %s", shimname) - shimPaths, _ := filepath.Glob(path.Join(shimpath, shimdir, shimname)) - for _, shimPath := range shimPaths { - wwlog.Debug("Checking for shim path: %s", shimPath) - // Only succeeds if shimPath exists and, if a - // symlink, links to a path that also exists - if _, err := os.Stat(shimPath); err == nil { - return shimPath - } - } - } - } - return "" -} diff --git a/internal/pkg/container/shimgrub.go b/internal/pkg/container/shimgrub.go new file mode 100644 index 00000000..a590690f --- /dev/null +++ b/internal/pkg/container/shimgrub.go @@ -0,0 +1,94 @@ +package container + +import ( + "os" + "path" + "path/filepath" + + "github.com/hpcng/warewulf/internal/pkg/wwlog" +) + +func shimDirs() []string { + return []string{ + `/usr/share/efi/x86_64/`, + `/usr/lib64/efi/`, + `/boot/efi/EFI/*/`, + } +} +func shimNames() []string { + return []string{ + `shim.efi`, + `shim-sles.efi`, + `shimx64.efi`, + `shim-susesigned.efi`, + } +} + +func grubDirs() []string { + return []string{ + `/usr/lib64/efi/`, + `/usr/share/grub2/*-efi/`, + `/usr/share/efi/*/`, + `/boot/efi/EFI/*/`, + } +} +func grubNames() []string { + return []string{ + `grub-tpm.efi`, + `grub.efi`, + `grubx64.efi`, + `grubia32.efi`, + `grubaa64.efi`, + `grubarm.efi`, + } +} + +/* +find the path of the shim binary in container +*/ +func ShimFind(container string) string { + var container_path string + if container != "" { + container_path = RootFsDir(container) + } else { + container_path = "/" + } + wwlog.Debug("Finding grub under paths: %s", container_path) + return BootLoaderFindPath(container_path, shimNames, shimDirs) +} + +/* +find a grub.efi in the used container +*/ +func GrubFind(container string) string { + var container_path string + if container != "" { + container_path = RootFsDir(container) + } else { + container_path = "/" + } + wwlog.Debug("Finding grub under paths: %s", container_path) + return BootLoaderFindPath(container_path, grubNames, grubDirs) +} + +/* +find the path of the shim binary in container +*/ +func BootLoaderFindPath(cpath string, names func() []string, paths func() []string) string { + for _, bdir := range paths() { + wwlog.Debug("Checking shim directory: %s", bdir) + for _, bname := range names() { + wwlog.Debug("Checking for bootloader name: %s", bname) + shimPaths, _ := filepath.Glob(path.Join(cpath, bdir, bname)) + for _, shimPath := range shimPaths { + wwlog.Debug("Checking for bootloader path: %s", shimPath) + // Only succeeds if shimPath exists and, if a + // symlink, links to a path that also exists + if _, err := os.Stat(shimPath); err == nil { + return shimPath + } + } + } + } + return "" +} diff --git a/internal/pkg/overlay/datastructure.go b/internal/pkg/overlay/datastructure.go index 04bddc52..0adc2f39 100644 --- a/internal/pkg/overlay/datastructure.go +++ b/internal/pkg/overlay/datastructure.go @@ -34,7 +34,6 @@ type TemplateStruct struct { Tftp warewulfconf.TFTPConf Paths warewulfconf.BuildConfig AllNodes []node.NodeInfo - ProfileMap map[string]*node.NodeInfo node.NodeConf // backward compatiblity Container string @@ -56,10 +55,6 @@ func InitStruct(nodeInfo *node.NodeInfo) TemplateStruct { if err != nil { wwlog.Warn("couldn't get all nodes: %s", err) } - tstruct.ProfileMap, err = nodeDB.MapAllProfiles() - if err != nil { - wwlog.Warn("couldn't get all profiles: %s", err) - } // init some convenience vars tstruct.Id = nodeInfo.Id.Get() tstruct.Hostname = nodeInfo.Id.Get() diff --git a/internal/pkg/warewulfd/copyshim.go b/internal/pkg/warewulfd/copyshim.go index 002ed336..44ea45f4 100644 --- a/internal/pkg/warewulfd/copyshim.go +++ b/internal/pkg/warewulfd/copyshim.go @@ -20,7 +20,7 @@ to the tftp directory func CopyShimGrub() (err error) { conf := warewulfconf.Get() wwlog.Debug("copy shim and grub binaries from host") - shimPath := container.ShimFindPath("/") + shimPath := container.ShimFind("") if shimPath == "" { return fmt.Errorf("no shim found on the host os") } @@ -29,7 +29,7 @@ func CopyShimGrub() (err error) { return err } _ = os.Chmod(path.Join(conf.Paths.Tftpdir, "warewulf", "shim.efi"), 0o755) - grubPath := container.GrubFindPath("/") + grubPath := container.GrubFind("") if grubPath == "" { return fmt.Errorf("no grub found on host os") } diff --git a/internal/pkg/warewulfd/warewulfd.go b/internal/pkg/warewulfd/warewulfd.go index 0848f4da..c9376634 100644 --- a/internal/pkg/warewulfd/warewulfd.go +++ b/internal/pkg/warewulfd/warewulfd.go @@ -16,8 +16,10 @@ import ( // TODO: https://github.com/danderson/netboot/blob/master/pixiecore/dhcp.go // TODO: https://github.com/pin/tftp /* -wrapper type for the server mux as him requests http://efiboot//grub.efi which is filtered out by http to `301 Moved Permanently` which -which shim.fi can't handle. So filter out `//` before they hit http +wrapper type for the server mux as shim requests http://efiboot//grub.efi +which is filtered out by http to `301 Moved Permanently` what +shim.efi can't handle. So filter out `//` before they hit go/http. +Makes go/http more to behave like apache */ type slashFix struct { mux http.Handler diff --git a/overlays/host/etc/dhcp/dhcpd.conf.ww b/overlays/host/etc/dhcp/dhcpd.conf.ww index 1054d401..03fe54cb 100644 --- a/overlays/host/etc/dhcp/dhcpd.conf.ww +++ b/overlays/host/etc/dhcp/dhcpd.conf.ww @@ -27,7 +27,13 @@ option architecture-type code 93 = unsigned integer 16; {{- if $.Warewulf.GrubBoot }} if substring (option vendor-class-identifier, 0, 9) = "PXEClient" { next-server {{ $.Ipaddr }}; - filename "warewulf/shim.efi"; + if substring (option vendor-class-identifier, 15, 5) = "00000" { + # pure BIOS clients will get iPXE configuration + filename "http://{{$.Ipaddr}}:{{$.Warewulf.Port}}/ipxe/${mac:hexhyp}"; + } else { + # EFI clients will get shim and grub instead + filename "warewulf/shim.efi"; + } } elsif substring (option vendor-class-identifier, 0, 10) = "HTTPClient" { filename "http://{{$.Ipaddr}}:{{$.Warewulf.Port}}/efiboot/shim.efi"; } diff --git a/userdocs/contents/grub.rst b/userdocs/contents/boot-management.rst similarity index 97% rename from userdocs/contents/grub.rst rename to userdocs/contents/boot-management.rst index 7bd3e0db..4ba87aa5 100644 --- a/userdocs/contents/grub.rst +++ b/userdocs/contents/boot-management.rst @@ -1,13 +1,13 @@ -=========== -Use of Grub -=========== +=============== +Boot Management +=============== Instead of the iPXE starter a combination of `shim and GRUB `_ can be used with the advantage that secure boot can be used. That means that only the signed kernel of a distribution can be booted. This can be a huge security benefit for some scenarios. -In order to enable the grub boot method it has to ne enabled in `warewulf.conf`. +In order to enable the grub boot method it has to be enabled in `warewulf.conf`. Nodes which are not known to warewulf will then booted with the shim/grub from the host on which warewulf is installed. diff --git a/userdocs/contents/security.rst b/userdocs/contents/security.rst index c016a1b4..b0bb42a8 100644 --- a/userdocs/contents/security.rst +++ b/userdocs/contents/security.rst @@ -55,7 +55,7 @@ when a user lands on a compute node, there is generally nothing stopping them from spoofing a provision request and downloading the provisioned raw materials for inspection. -In Warewulf there are ways to secure the provisioning process: +In Warewulf there are ways multiple to secure the provisioning process: #. The provisioning connections and transfers are not secure due to not being able to manage a secure root of trust through a PXE diff --git a/userdocs/index.rst b/userdocs/index.rst index ab2f65c3..9d7a4b8c 100644 --- a/userdocs/index.rst +++ b/userdocs/index.rst @@ -18,7 +18,7 @@ Welcome to the Warewulf User Guide! Warewulf Initialization Container Management Kernel Management - Boot Management + Boot Management Node Configuration Node Profiles Warewulf Overlays diff --git a/warewulf.spec.in b/warewulf.spec.in index 318703fb..e71a71a4 100644 --- a/warewulf.spec.in +++ b/warewulf.spec.in @@ -139,6 +139,7 @@ yq e ' %config(noreplace) %{_sysconfdir}/warewulf/wwapi*.conf %config(noreplace) %{_sysconfdir}/warewulf/examples %config(noreplace) %{_sysconfdir}/warewulf/ipxe +%config(noreplace) %{_sysconfdir}/warewulf/grub %config(noreplace) %attr(0640,-,-) %{_sysconfdir}/warewulf/nodes.conf %{_sysconfdir}/bash_completion.d/wwctl