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