Skip to content

Commit 80fb2ff

Browse files
Resolve selinux installation issues (#2394)
* run restorecon after materialising files * don't care about selinux enforcing * set specific bin_t context on bins dir * path * Correct match group * add selinux preflight * message formatting * explicitly check for restorecon binary * Add selinux test This builds on the existing airgapped testing. due to some limitations in our alma images I had to change all script calls to absolute paths. * bump troubleshoot tag * remove path env from sudo prefix * move selinux setup to hostutils interface * mock * Revert "remove path env from sudo prefix" This reverts commit 9b932da.
1 parent 64bfa59 commit 80fb2ff

File tree

10 files changed

+262
-31
lines changed

10 files changed

+262
-31
lines changed

.github/workflows/ci.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,7 @@ jobs:
910910
test:
911911
- TestResetAndReinstallAirgap
912912
- TestSingleNodeAirgapUpgrade
913+
- TestSingleNodeAirgapUpgradeSelinux
913914
- TestSingleNodeAirgapUpgradeConfigValues
914915
- TestSingleNodeAirgapUpgradeCustomCIDR
915916
- TestMultiNodeAirgapUpgrade

.github/workflows/release-prod.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,7 @@ jobs:
569569
test:
570570
- TestResetAndReinstallAirgap
571571
- TestSingleNodeAirgapUpgrade
572+
- TestSingleNodeAirgapUpgradeSelinux
572573
- TestSingleNodeAirgapUpgradeConfigValues
573574
- TestSingleNodeAirgapUpgradeCustomCIDR
574575
- TestMultiNodeAirgapUpgrade

e2e/cluster/cmx/cluster.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,15 @@ func copyScriptsToNode(node Node) error {
205205
}
206206

207207
func getSSHEndpoint(nodeID string) (string, error) {
208-
output, err := exec.Command("replicated", "vm", "ssh-endpoint", nodeID).CombinedOutput()
208+
args := []string{
209+
"vm",
210+
"ssh-endpoint",
211+
nodeID,
212+
}
213+
if user := os.Getenv("CMX_SSH_USERNAME"); user != "" {
214+
args = append(args, "--username", user)
215+
}
216+
output, err := exec.Command("replicated", args...).CombinedOutput()
209217
if err != nil {
210218
return "", fmt.Errorf("%v: %s", err, string(output))
211219
}
@@ -335,7 +343,7 @@ func runCommandOnNode(node Node, line []string, envs ...map[string]string) (stri
335343
line = append([]string{fmt.Sprintf("%s=%s", k, v)}, line...)
336344
}
337345
}
338-
line = append([]string{"sudo"}, line...)
346+
line = append([]string{"sudo", "PATH=$PATH:/usr/local/bin"}, line...)
339347

340348
cmd := exec.Command("ssh", append(sshArgs(), node.sshEndpoint, strings.Join(line, " "))...)
341349
var stdout, stderr bytes.Buffer
@@ -363,7 +371,7 @@ func (c *Cluster) SetupPlaywrightAndRunTest(testName string, args ...string) (st
363371

364372
func (c *Cluster) SetupPlaywright(envs ...map[string]string) error {
365373
c.t.Logf("%s: bypassing kurl-proxy", time.Now().Format(time.RFC3339))
366-
_, stderr, err := c.RunCommandOnNode(0, []string{"bypass-kurl-proxy.sh"}, envs...)
374+
_, stderr, err := c.RunCommandOnNode(0, []string{"/usr/local/bin/bypass-kurl-proxy.sh"}, envs...)
367375
if err != nil {
368376
return fmt.Errorf("bypass kurl-proxy: %v: %s", err, string(stderr))
369377
}
@@ -401,7 +409,7 @@ func (c *Cluster) generateSupportBundle(envs ...map[string]string) {
401409
go func(i int, wg *sync.WaitGroup) {
402410
defer wg.Done()
403411
c.t.Logf("%s: generating host support bundle from node %d", time.Now().Format(time.RFC3339), i)
404-
if stdout, stderr, err := c.RunCommandOnNode(i, []string{"collect-support-bundle-host.sh"}, envs...); err != nil {
412+
if stdout, stderr, err := c.RunCommandOnNode(i, []string{"/usr/local/bin/collect-support-bundle-host.sh"}, envs...); err != nil {
405413
c.t.Logf("stdout: %s", stdout)
406414
c.t.Logf("stderr: %s", stderr)
407415
c.t.Logf("fail to generate support bundle from node %d: %v", i, err)
@@ -419,7 +427,7 @@ func (c *Cluster) generateSupportBundle(envs ...map[string]string) {
419427
}
420428

421429
c.t.Logf("%s: generating cluster support bundle from node %d", time.Now().Format(time.RFC3339), c.supportBundleNodeIndex)
422-
if stdout, stderr, err := c.RunCommandOnNode(c.supportBundleNodeIndex, []string{"collect-support-bundle-cluster.sh"}, envs...); err != nil {
430+
if stdout, stderr, err := c.RunCommandOnNode(c.supportBundleNodeIndex, []string{"/usr/local/bin/collect-support-bundle-cluster.sh"}, envs...); err != nil {
423431
c.t.Logf("stdout: %s", stdout)
424432
c.t.Logf("stderr: %s", stderr)
425433
c.t.Logf("fail to generate cluster support bundle from node %d: %v", c.supportBundleNodeIndex, err)

e2e/install_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,95 @@ func TestSingleNodeAirgapUpgrade(t *testing.T) {
694694
t.Logf("%s: test complete", time.Now().Format(time.RFC3339))
695695
}
696696

697+
func TestSingleNodeAirgapUpgradeSelinux(t *testing.T) {
698+
t.Parallel()
699+
700+
RequireEnvVars(t, []string{"SHORT_SHA"})
701+
702+
tc := cmx.NewCluster(&cmx.ClusterInput{
703+
T: t,
704+
Nodes: 1,
705+
Distribution: "almalinux",
706+
Version: "8",
707+
})
708+
defer tc.Cleanup()
709+
710+
t.Logf("%s: downloading airgap files on node 0", time.Now().Format(time.RFC3339))
711+
initialVersion := fmt.Sprintf("appver-%s-previous-k0s", os.Getenv("SHORT_SHA"))
712+
runInParallel(t,
713+
func(t *testing.T) error {
714+
return downloadAirgapBundleOnNode(t, tc, 0, initialVersion, AirgapInstallBundlePath, AirgapLicenseID)
715+
}, func(t *testing.T) error {
716+
return downloadAirgapBundleOnNode(t, tc, 0, fmt.Sprintf("appver-%s-upgrade", os.Getenv("SHORT_SHA")), AirgapUpgradeBundlePath, AirgapLicenseID)
717+
},
718+
)
719+
720+
t.Logf("%s: airgapping cluster", time.Now().Format(time.RFC3339))
721+
if err := tc.Airgap(); err != nil {
722+
t.Fatalf("failed to airgap cluster: %v", err)
723+
}
724+
725+
t.Logf("%s: creating /.autorelabel file for SELinux relabeling", time.Now().Format(time.RFC3339))
726+
if stdout, stderr, err := tc.RunCommandOnNode(0, []string{"touch", "/.autorelabel"}); err != nil {
727+
t.Fatalf("fail to create /.autorelabel file on node %s: %v: %s: %s", tc.Nodes[0], err, stdout, stderr)
728+
}
729+
730+
t.Logf("%s: rebooting VM for SELinux relabeling", time.Now().Format(time.RFC3339))
731+
if stdout, stderr, err := tc.RunCommandOnNode(0, []string{"reboot"}); err != nil {
732+
t.Fatalf("fail to reboot node %s: %v: %s: %s", tc.Nodes[0], err, stdout, stderr)
733+
}
734+
735+
t.Logf("%s: waiting for node to reboot", time.Now().Format(time.RFC3339))
736+
tc.WaitForReboot()
737+
738+
t.Logf("%s: setting selinux to Enforcing mode", time.Now().Format(time.RFC3339))
739+
if stdout, stderr, err := tc.RunCommandOnNode(0, []string{"setenforce 1"}); err != nil {
740+
t.Fatalf("fail to set selinux to Enforcing mode %s: %v: %s: %s", tc.Nodes[0], err, stdout, stderr)
741+
}
742+
743+
t.Logf("%s: preparing embedded cluster airgap files", time.Now().Format(time.RFC3339))
744+
line := []string{"/usr/local/bin/airgap-prepare.sh"}
745+
if stdout, stderr, err := tc.RunCommandOnNode(0, line); err != nil {
746+
t.Fatalf("fail to prepare airgap files on node %s: %v: %s: %s", tc.Nodes[0], err, stdout, stderr)
747+
}
748+
749+
installSingleNodeWithOptions(t, tc, installOptions{
750+
isAirgap: true,
751+
version: initialVersion,
752+
localArtifactMirrorPort: "50001", // choose an alternate lam port
753+
})
754+
755+
if stdout, stderr, err := tc.SetupPlaywrightAndRunTest("deploy-app"); err != nil {
756+
t.Fatalf("fail to run playwright test deploy-app: %v: %s: %s", err, stdout, stderr)
757+
}
758+
759+
t.Logf("%s: checking installation state after app deployment", time.Now().Format(time.RFC3339))
760+
line = []string{"/usr/local/bin/check-airgap-installation-state.sh", initialVersion, k8sVersionPrevious()}
761+
if _, _, err := tc.RunCommandOnNode(0, line); err != nil {
762+
t.Fatalf("fail to check installation state: %v", err)
763+
}
764+
765+
checkNodeJoinCommand(t, tc, 0)
766+
767+
t.Logf("%s: running airgap update", time.Now().Format(time.RFC3339))
768+
line = []string{"/usr/local/bin/airgap-update.sh"}
769+
if _, _, err := tc.RunCommandOnNode(0, line); err != nil {
770+
t.Fatalf("fail to run airgap update: %v", err)
771+
}
772+
773+
appUpgradeVersion := fmt.Sprintf("appver-%s-upgrade", os.Getenv("SHORT_SHA"))
774+
testArgs := []string{appUpgradeVersion}
775+
776+
t.Logf("%s: upgrading cluster", time.Now().Format(time.RFC3339))
777+
if stdout, stderr, err := tc.RunPlaywrightTest("deploy-upgrade", testArgs...); err != nil {
778+
t.Fatalf("fail to run playwright test deploy-upgrade: %v: %s: %s", err, stdout, stderr)
779+
}
780+
781+
checkPostUpgradeState(t, tc)
782+
783+
t.Logf("%s: test complete", time.Now().Format(time.RFC3339))
784+
}
785+
697786
func TestSingleNodeAirgapUpgradeCustomCIDR(t *testing.T) {
698787
t.Parallel()
699788

e2e/shared.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@ func installSingleNodeWithOptions(t *testing.T, tc cluster.Cluster, opts install
7676
line := []string{}
7777

7878
if opts.isAirgap {
79-
line = append(line, "single-node-airgap-install.sh")
79+
line = append(line, "/usr/local/bin/single-node-airgap-install.sh")
8080
} else {
81-
line = append(line, "single-node-install.sh")
81+
line = append(line, "/usr/local/bin/single-node-install.sh")
8282
// the cli/ui option is currently only applicable for online installs
8383
if opts.viaCLI {
8484
line = append(line, "cli")
@@ -136,7 +136,7 @@ func checkInstallationState(t *testing.T, tc cluster.Cluster) {
136136
}
137137

138138
func checkInstallationStateWithOptions(t *testing.T, tc cluster.Cluster, opts installationStateOptions) {
139-
line := []string{"check-installation-state.sh"}
139+
line := []string{"/usr/local/bin/check-installation-state.sh"}
140140
if opts.version != "" {
141141
line = append(line, opts.version)
142142
} else {
@@ -233,23 +233,23 @@ func joinWorkerNodeWithOptions(t *testing.T, tc cluster.Cluster, node int, opts
233233

234234
func waitForNodes(t *testing.T, tc cluster.Cluster, nodes int, envs map[string]string, args ...string) {
235235
t.Logf("%s: all nodes joined, waiting for them to be ready", time.Now().Format(time.RFC3339))
236-
stdout, stderr, err := tc.RunCommandOnNode(0, append([]string{"wait-for-ready-nodes.sh", fmt.Sprintf("%d", nodes)}, args...), envs)
236+
stdout, stderr, err := tc.RunCommandOnNode(0, append([]string{"/usr/local/bin/wait-for-ready-nodes.sh", fmt.Sprintf("%d", nodes)}, args...), envs)
237237
if err != nil {
238238
t.Fatalf("fail to wait for ready nodes: %v: %s: %s", err, stdout, stderr)
239239
}
240240
}
241241

242242
func checkWorkerProfile(t *testing.T, tc cluster.Cluster, node int) {
243243
t.Logf("checking worker profile on node %d", node)
244-
line := []string{"check-worker-profile.sh"}
244+
line := []string{"/usr/local/bin/check-worker-profile.sh"}
245245
if stdout, stderr, err := tc.RunCommandOnNode(node, line); err != nil {
246246
t.Fatalf("fail to check worker profile on node %d: %v: %s: %s", node, err, stdout, stderr)
247247
}
248248
}
249249

250250
func checkNodeJoinCommand(t *testing.T, tc cluster.Cluster, node int) {
251251
t.Logf("node join command generation on node %d", node)
252-
line := []string{"check-node-join-command.sh"}
252+
line := []string{"/usr/local/bin/check-node-join-command.sh"}
253253
if stdout, stderr, err := tc.RunCommandOnNode(node, line); err != nil {
254254
t.Fatalf("fail to check if node join command is generated successfully on node %d: %v: %s: %s", node, err, stdout, stderr)
255255
}
@@ -261,7 +261,7 @@ func downloadECRelease(t *testing.T, tc cluster.Cluster, node int) {
261261

262262
func downloadECReleaseWithOptions(t *testing.T, tc cluster.Cluster, node int, opts downloadECReleaseOptions) {
263263
t.Logf("%s: downloading embedded cluster release on node %d", time.Now().Format(time.RFC3339), node)
264-
line := []string{"vandoor-prepare.sh"}
264+
line := []string{"/usr/local/bin/vandoor-prepare.sh"}
265265

266266
if opts.version != "" {
267267
line = append(line, opts.version)
@@ -292,7 +292,7 @@ func resetInstallationWithOptions(t *testing.T, tc cluster.Cluster, node int, op
292292

293293
func resetInstallationWithError(t *testing.T, tc cluster.Cluster, node int, opts resetInstallationOptions) (string, string, error) {
294294
t.Logf("%s: resetting the installation on node %d", time.Now().Format(time.RFC3339), node)
295-
line := []string{"reset-installation.sh"}
295+
line := []string{"/usr/local/bin/reset-installation.sh"}
296296
if opts.force {
297297
line = append(line, "--force")
298298
}
@@ -304,7 +304,7 @@ func checkPostUpgradeState(t *testing.T, tc cluster.Cluster) {
304304
}
305305

306306
func checkPostUpgradeStateWithOptions(t *testing.T, tc cluster.Cluster, opts postUpgradeStateOptions) {
307-
line := []string{"check-postupgrade-state.sh"}
307+
line := []string{"/usr/local/bin/check-postupgrade-state.sh"}
308308

309309
if opts.k8sVersion != "" {
310310
line = append(line, opts.k8sVersion)

pkg-new/hostutils/initialize.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ func (h *HostUtils) ConfigureHost(ctx context.Context, rc runtimeconfig.RuntimeC
3939
}
4040
}
4141

42+
h.logger.Debugf("configuring selinux fcontext")
43+
if err := h.ConfigureSELinuxFcontext(rc); err != nil {
44+
h.logger.Debugf("unable to configure selinux fcontext: %v", err)
45+
}
46+
47+
h.logger.Debugf("restoring selinux context")
48+
if err := h.RestoreSELinuxContext(rc); err != nil {
49+
h.logger.Debugf("unable to restore selinux context: %v", err)
50+
}
51+
4252
h.logger.Debugf("configuring sysctl")
4353
if err := h.ConfigureSysctl(); err != nil {
4454
h.logger.Debugf("unable to configure sysctl: %v", err)

pkg-new/hostutils/interface.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ type HostUtilsInterface interface {
2828
CreateSystemdUnitFiles(ctx context.Context, logger logrus.FieldLogger, rc runtimeconfig.RuntimeConfig, isWorker bool) error
2929
WriteLocalArtifactMirrorDropInFile(rc runtimeconfig.RuntimeConfig) error
3030
AddInsecureRegistry(registry string) error
31+
ConfigureSELinuxFcontext(rc runtimeconfig.RuntimeConfig) error
32+
RestoreSELinuxContext(rc runtimeconfig.RuntimeConfig) error
3133
}
3234

3335
// Convenience functions
@@ -72,3 +74,11 @@ func WriteLocalArtifactMirrorDropInFile(rc runtimeconfig.RuntimeConfig) error {
7274
func AddInsecureRegistry(registry string) error {
7375
return h.AddInsecureRegistry(registry)
7476
}
77+
78+
func ConfigureSELinuxFcontext(rc runtimeconfig.RuntimeConfig) error {
79+
return h.ConfigureSELinuxFcontext(rc)
80+
}
81+
82+
func RestoreSELinuxContext(rc runtimeconfig.RuntimeConfig) error {
83+
return h.RestoreSELinuxContext(rc)
84+
}

pkg-new/hostutils/mock.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,18 @@ type MockHostUtils struct {
1515
mock.Mock
1616
}
1717

18+
// ConfigureSELinuxFcontext implements HostUtilsInterface.
19+
func (m *MockHostUtils) ConfigureSELinuxFcontext(rc runtimeconfig.RuntimeConfig) error {
20+
args := m.Called(rc)
21+
return args.Error(0)
22+
}
23+
24+
// RestoreSELinuxContext implements HostUtilsInterface.
25+
func (m *MockHostUtils) RestoreSELinuxContext(rc runtimeconfig.RuntimeConfig) error {
26+
args := m.Called(rc)
27+
return args.Error(0)
28+
}
29+
1830
// ConfigureHost mocks the ConfigureHost method
1931
func (m *MockHostUtils) ConfigureHost(ctx context.Context, rc runtimeconfig.RuntimeConfig, opts InitForInstallOptions) error {
2032
args := m.Called(ctx, rc, opts)

pkg-new/hostutils/selinux.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package hostutils
2+
3+
import (
4+
"os/exec"
5+
6+
"github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig"
7+
)
8+
9+
func (h *HostUtils) ConfigureSELinuxFcontext(rc runtimeconfig.RuntimeConfig) error {
10+
h.logger.Debugln("checking for semanage binary in $PATH")
11+
if _, err := exec.LookPath("semanage"); err != nil {
12+
h.logger.Debugln("semanage not found in $PATH")
13+
return nil
14+
}
15+
16+
h.logger.Debugf("setting selinux fcontext for embedded-cluster binary directory to bin_t")
17+
args := []string{
18+
"fcontext",
19+
"-a",
20+
"-s",
21+
"system_u",
22+
"-t",
23+
"bin_t",
24+
rc.EmbeddedClusterBinsSubDir() + "(/.*)?",
25+
}
26+
out, err := exec.Command("semanage", args...).CombinedOutput()
27+
if err != nil {
28+
h.logger.Debugf("unable to set contexts on binary directory: %v", err)
29+
h.logger.Debugln(string(out))
30+
}
31+
32+
return nil
33+
}
34+
35+
func (h *HostUtils) RestoreSELinuxContext(rc runtimeconfig.RuntimeConfig) error {
36+
h.logger.Debugln("checking for restorecon binary in $PATH")
37+
if _, err := exec.LookPath("restorecon"); err != nil {
38+
h.logger.Debugln("restorecon not found in $PATH")
39+
return nil
40+
}
41+
42+
h.logger.Debugf("relabeling embedded-cluster data directory with restorecon")
43+
out, err := exec.Command("restorecon", "-RvF", rc.EmbeddedClusterHomeDirectory()).CombinedOutput()
44+
if err != nil {
45+
h.logger.Debugf("unable to run restorecon: %v", err)
46+
h.logger.Debugln(string(out))
47+
}
48+
49+
return nil
50+
}

0 commit comments

Comments
 (0)