From be97ef15a08d07c547afd1f25765b0abe5c3eb25 Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Wed, 29 Oct 2025 13:09:30 -0600 Subject: [PATCH] Manage selinux context of tftp directory - Fixes: #1997 Signed-off-by: Jonathon Anderson --- CHANGELOG.md | 1 + LICENSE_DEPENDENCIES.md | 6 +++ etc/warewulf.conf-el10 | 39 +++++++++++++++++ etc/warewulf.conf-suse | 40 +++++++++++++++++ go.mod | 1 + go.sum | 2 + internal/app/wwctl/configure/main.go | 10 +++-- internal/pkg/configure/tftp.go | 16 ++++++- internal/pkg/util/selinux.go | 34 +++++++++++++++ warewulf.spec.in | 65 +++++++++++++++------------- 10 files changed, 179 insertions(+), 35 deletions(-) create mode 100644 etc/warewulf.conf-el10 create mode 100644 etc/warewulf.conf-suse create mode 100644 internal/pkg/util/selinux.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 2efa1b1d..f221965d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Renamed debian.interfaces overlay to ifupdown - Change the DHCP server package used on openeuler 24.03 to dnsmasq - Added configurable Serial over LAN speed via IPMI `bit-rate` tag in `50-ipmi` template +- Manage SELinux context of TFTP directory. #1997 ### Fixed diff --git a/LICENSE_DEPENDENCIES.md b/LICENSE_DEPENDENCIES.md index c0e18979..003e446b 100644 --- a/LICENSE_DEPENDENCIES.md +++ b/LICENSE_DEPENDENCIES.md @@ -263,6 +263,12 @@ The dependencies and their licenses are as follows: **License URL:** +## github.com/opencontainers/selinux + +**License:** Apache-2.0 + +**License URL:** + ## github.com/opencontainers/umoci **License:** Apache-2.0 diff --git a/etc/warewulf.conf-el10 b/etc/warewulf.conf-el10 new file mode 100644 index 00000000..e842f0a5 --- /dev/null +++ b/etc/warewulf.conf-el10 @@ -0,0 +1,39 @@ +ipaddr: 10.0.0.1 +netmask: 255.255.252.0 +warewulf: + secure: false + update interval: 60 + autobuild overlays: true + host overlay: true + systemd name: warewulfd +dhcp: + enabled: true + range start: 10.0.1.1 + range end: 10.0.1.255 + systemd name: dnsmasq + template: default +tftp: + enabled: true + systemd name: dnsmasq + ipxe: + 00:09: ipxe-snponly-x86_64.efi + 00:00: undionly.kpxe + 00:0B: arm64-efi/snponly.efi + 00:07: ipxe-snponly-x86_64.efi +nfs: + enabled: true + export paths: + - path: /home + export options: rw,sync + - path: /opt + export options: ro,sync,no_root_squash + systemd name: nfs-server +image mounts: + - source: /etc/resolv.conf + dest: /etc/resolv.conf + readonly: true +ssh: + key types: + - ed25519 + - ecdsa + - rsa diff --git a/etc/warewulf.conf-suse b/etc/warewulf.conf-suse new file mode 100644 index 00000000..a2cfbbd0 --- /dev/null +++ b/etc/warewulf.conf-suse @@ -0,0 +1,40 @@ +ipaddr: 10.0.0.1 +netmask: 255.255.252.0 +warewulf: + secure: false + update interval: 60 + autobuild overlays: true + host overlay: true + systemd name: warewulfd +dhcp: + enabled: true + range start: 10.0.1.1 + range end: 10.0.1.255 + systemd name: dhcpd + template: default +tftp: + enabled: true + systemd name: tftp + ipxe: + 00:00: undionly.kpxe + 00:07: ipxe-x86_64.efi + 00:09: ipxe-x86_64.efi + 00:0B: snp-arm64.efi +nfs: + enabled: true + export paths: + - path: /home + export options: rw,sync + - path: /opt + export options: ro,sync,no_root_squash + systemd name: nfs-server +image mounts: + - source: /etc/resolv.conf + dest: /etc/resolv.conf + readonly: true +ssh: + key types: + - ed25519 + - ecdsa + - rsa + - dsa diff --git a/go.mod b/go.mod index 55b9642a..05a91842 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( github.com/manifoldco/promptui v0.9.0 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 github.com/opencontainers/image-spec v1.1.0 + github.com/opencontainers/selinux v1.11.1 github.com/opencontainers/umoci v0.4.7 github.com/pkg/errors v0.9.1 github.com/spf13/cobra v1.9.1 diff --git a/go.sum b/go.sum index 878b35c5..76941b43 100644 --- a/go.sum +++ b/go.sum @@ -296,6 +296,8 @@ github.com/opencontainers/runc v1.1.14/go.mod h1:E4C2z+7BxR7GHXp0hAY53mek+x49X1L github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk= github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jDMcgULaH8= +github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= github.com/opencontainers/umoci v0.4.7 h1:mbIbtMpZ3v9oMpKaLopnWoLykgmnixeLzq51EzAX5nQ= github.com/opencontainers/umoci v0.4.7/go.mod h1:lgJ4bnwJezsN1o/5d7t/xdRPvmf8TvBko5kKYJsYvgo= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= diff --git a/internal/app/wwctl/configure/main.go b/internal/app/wwctl/configure/main.go index 2d594729..366a7930 100644 --- a/internal/app/wwctl/configure/main.go +++ b/internal/app/wwctl/configure/main.go @@ -24,6 +24,12 @@ func CobraRunE(cmd *cobra.Command, args []string) error { if err != nil { return err } + + err = configure.TFTP() + if err != nil { + return err + } + err = configure.DHCP() if err != nil { return err @@ -39,10 +45,6 @@ func CobraRunE(cmd *cobra.Command, args []string) error { return err } - err = configure.TFTP() - if err != nil { - return err - } err = configure.Hostfile() if err != nil { return err diff --git a/internal/pkg/configure/tftp.go b/internal/pkg/configure/tftp.go index 41c8d556..44e88367 100644 --- a/internal/pkg/configure/tftp.go +++ b/internal/pkg/configure/tftp.go @@ -13,9 +13,22 @@ import ( func TFTP() (err error) { controller := warewulfconf.Get() - var tftpdir string = path.Join(controller.TFTP.TftpRoot, "warewulf") oldMask := unix.Umask(0) defer unix.Umask(oldMask) + + // Check if TftpRoot exists, create and restore context if needed + if _, err := os.Stat(controller.TFTP.TftpRoot); err != nil { + err = os.MkdirAll(controller.TFTP.TftpRoot, 0755) + if err != nil { + return err + } + if err := util.RestoreSELinuxContext(controller.TFTP.TftpRoot); err != nil { + wwlog.Warn("failed to restore SELinux context for %s: %s", controller.TFTP.TftpRoot, err) + } + } + + // Create tftpdir if needed + var tftpdir string = path.Join(controller.TFTP.TftpRoot, "warewulf") err = os.MkdirAll(tftpdir, 0755) if err != nil { return @@ -43,6 +56,7 @@ func TFTP() (err error) { } } } + if !controller.TFTP.Enabled() { wwlog.Warn("Warewulf does not auto start TFTP services due to disable by warewulf.conf") return nil diff --git a/internal/pkg/util/selinux.go b/internal/pkg/util/selinux.go new file mode 100644 index 00000000..93513414 --- /dev/null +++ b/internal/pkg/util/selinux.go @@ -0,0 +1,34 @@ +package util + +import ( + "fmt" + "os/exec" + "strings" + + "github.com/opencontainers/selinux/go-selinux" + "github.com/warewulf/warewulf/internal/pkg/wwlog" +) + +// RestoreSELinuxContext restores the SELinux context for a path and all its children +// based on the system's SELinux policy, equivalent to running restorecon -R +func RestoreSELinuxContext(rootPath string) error { + if !selinux.GetEnabled() { + wwlog.Debug("SELinux not enabled, skipping context restoration") + return nil + } + + wwlog.Info("Restoring SELinux contexts for: %s", rootPath) + + cmd := exec.Command("restorecon", "-vR", rootPath) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("restorecon failed: %w: %s", err, string(output)) + } + + for _, line := range strings.Split(strings.TrimSpace(string(output)), "\n") { + if line != "" { + wwlog.Debug("restorecon output: %s", line) + } + } + return nil +} diff --git a/warewulf.spec.in b/warewulf.spec.in index 85afdd03..415b1044 100644 --- a/warewulf.spec.in +++ b/warewulf.spec.in @@ -1,11 +1,25 @@ %global debug_package %{nil} -%if 0%{?suse_version} +# feature macros +%if 0%{?rhel} >= 10 || 0%{?openEuler} +%global use_dnsmasq 1 +%else +%global use_dnsmasq 0 +%endif + +%if 0%{?suse_version} || 0%{?sle_version} +%global is_suse 1 +%else +%global is_suse 0 +%endif + +# Set tftpdir based on distribution +%if 0%{?is_suse} %global tftpdir /srv/tftpboot %else -# Assume Fedora-based OS if not SUSE-based %global tftpdir /var/lib/tftpboot %endif + %global srvdir %{_sharedstatedir} %global wwgroup warewulf @@ -35,27 +49,21 @@ Conflicts: warewulf-vnfs Conflicts: warewulf-provision Conflicts: warewulf-ipmi -%if 0%{?suse_version} || 0%{?sle_version} +%if 0%{?is_suse} BuildRequires: distribution-release BuildRequires: systemd-rpm-macros BuildRequires: go >= 1.22 BuildRequires: firewall-macros BuildRequires: firewalld -BuildRequires: tftp -BuildRequires: yq -Requires: tftp Requires: nfs-kernel-server Requires: firewalld Requires: ipxe-bootimgs %else -# Assume Red Hat/Fedora build +# Assume Red Hat/Fedora BuildRequires: system-release BuildRequires: systemd BuildRequires: golang >= 1.22 BuildRequires: firewalld-filesystem -%if ! (0%{?rhel} >= 10) -Requires: tftp-server -%endif Requires: nfs-utils %if 0%{?rhel} < 8 Requires: ipxe-bootimgs @@ -65,13 +73,18 @@ Requires: ipxe-bootimgs-aarch64 %endif %endif -%if 0%{?rhel} >= 10 || 0%{?openEuler} +# dhcp/tftp requirements +%if 0%{?is_suse} +BuildRequires: tftp +Requires: tftp +%elif 0%{?use_dnsmasq} Requires: dnsmasq %else -%if 0%{?rhel} >= 8 || 0%{?suse_version} || 0%{?fedora} +# Assume Red Hat/Fedora +Requires: tftp-server +%if 0%{?rhel} >= 8 || 0%{?fedora} Requires: dhcp-server %else -# rhel < 8 and others Requires: dhcp %endif %endif @@ -126,21 +139,12 @@ make install \ DESTDIR=%{buildroot} %if 0%{?rhel} >= 10 || 0%{?openEuler} -sed -i ' - s/systemd name: dhcpd/systemd name: dnsmasq/ - s/systemd name: tftp/systemd name: dnsmasq/ - /- dsa/d' \ - -i %{buildroot}%{_sysconfdir}/warewulf/warewulf.conf +cp -f etc/warewulf.conf-el10 %{buildroot}%{_sysconfdir}/warewulf/warewulf.conf +%elif 0%{?is_suse} +cp -f etc/warewulf.conf-suse %{buildroot}%{_sysconfdir}/warewulf/warewulf.conf %endif -%if 0%{?suse_version} || 0%{?sle_version} -yq e ' - .tftp.ipxe."00:00" = "undionly.kpxe" | - .tftp.ipxe."00:07" = "ipxe-x86_64.efi" | - .tftp.ipxe."00:09" = "ipxe-x86_64.efi" | - .tftp.ipxe."00:0B" = "snp-arm64.efi" ' \ - -i %{buildroot}%{_sysconfdir}/warewulf/warewulf.conf -%else +%if ! 0%{?is_suse} make install-sos \ DESTDIR=%{buildroot} %endif @@ -229,8 +233,7 @@ Summary: dracut module for loading a Warewulf image BuildArch: noarch Requires: dracut -%if 0%{?suse_version} -%else +%if ! 0%{?is_suse} Requires: dracut-network %endif Requires: curl @@ -249,8 +252,7 @@ initramfs that can fetch and boot a Warewulf node image from a Warewulf server. %{_prefix}/lib/dracut/modules.d/90wwinit -%if 0%{?suse_version} || 0%{?sle_version} -%else +%if ! 0%{?is_suse} %package sos Summary: sos plugin for Warewulf BuildArch: noarch @@ -270,6 +272,9 @@ about Warewulf in an sos report. %changelog +* Tue Oct 28 2025 Jonathon Anderson +- Use static variants for warewulf.conf-suse and warewulf.conf-el10 + * Thu Sep 4 2025 Jonathon Anderson - Update golang BuildRequires to 1.22 - Exclude overlay files from brp-mangle-shebangs