Skip to content

Commit eebe20d

Browse files
feat: check sshd config before starting the install (#313)
now that we only allow installations with root the ssh installation method became a huge problem. we can only install if ssh access is allowed for the root user using a ssh key. while we work on moving ourselves out of the ssh based installation we need to at least provide the users with valid information on how to work around this issue. this pr does exactly that, upon finding an "invalid" ssh config this is what is printed on the terminal: ``` rmarasch@ec:~$ sudo ./embedded-cluster install PermitRootLogin config is set to 'no' in /etc/ssh/sshd_config This will prevent embedded-cluster from installing. You can temporarily enable root login by changing the PermitRootLogin config to 'without-password' and restarting the sshd service. Once the installation is finished you can restore the original configuration. FATA[0000] ssh root access is not allowed rmarasch@ec:~$ ```
1 parent 642775d commit eebe20d

File tree

5 files changed

+103
-0
lines changed

5 files changed

+103
-0
lines changed

.github/workflows/pull-request.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ jobs:
9191
- TestMultiNodeInstallation
9292
- TestMultiNodeReset
9393
- TestCommandsRequireSudo
94+
- TestInstallWithoutRootSSHAccess
9495
steps:
9596
- name: Checkout
9697
uses: actions/checkout@v4

.github/workflows/release-dev.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ jobs:
5959
- TestMultiNodeInstallation
6060
- TestMultiNodeReset
6161
- TestCommandsRequireSudo
62+
- TestInstallWithoutRootSSHAccess
6263
steps:
6364
- name: Checkout
6465
uses: actions/checkout@v4

cmd/embedded-cluster/install.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"bufio"
45
"bytes"
56
"context"
67
"fmt"
@@ -408,6 +409,38 @@ func runOutro(c *cli.Context) error {
408409
return addons.NewApplier(opts...).Outro(c.Context)
409410
}
410411

412+
// validateSSHDConfig checks if we can ssh into ourselves as root using ssh
413+
// keys. XXX this is a workaround while we don't implement a different method
414+
// of installation that does not require ssh access.
415+
func validateSSHDConfig() error {
416+
sshdcfg := "/etc/ssh/sshd_config"
417+
fp, err := os.Open(sshdcfg)
418+
if err != nil {
419+
return fmt.Errorf("unable to read sshd_config (%s): %w", sshdcfg, err)
420+
}
421+
defer fp.Close()
422+
scanner := bufio.NewScanner(fp)
423+
var invalid bool
424+
for scanner.Scan() {
425+
line := scanner.Text()
426+
if !strings.HasPrefix(line, "PermitRootLogin") {
427+
continue
428+
}
429+
invalid = strings.HasSuffix(line, "no")
430+
break
431+
}
432+
if !invalid {
433+
return nil
434+
}
435+
fmt.Printf("PermitRootLogin config is set to 'no' in %s\n", sshdcfg)
436+
fmt.Printf("This will prevent %s from installing.\n", defaults.BinaryName())
437+
fmt.Printf("You can temporarily enable root login by changing the\n")
438+
fmt.Printf("PermitRootLogin config to 'without-password' and restarting\n")
439+
fmt.Printf("the sshd service. Once the installation is finished you can\n")
440+
fmt.Printf("restore the original configuration.\n")
441+
return fmt.Errorf("ssh root access is not allowed")
442+
}
443+
411444
// installCommands executes the "install" command. This will ensure that a
412445
// k0sctl.yaml file exists and then run `k0sctl apply` to apply the cluster.
413446
// Once this is finished then a "kubeconfig" file is created.
@@ -455,6 +488,10 @@ var installCommand = &cli.Command{
455488
metrics.ReportApplyFinished(c, fmt.Errorf("wrong upgrade on decentralized install"))
456489
return fmt.Errorf("decentralized install detected")
457490
}
491+
if err := validateSSHDConfig(); err != nil {
492+
metrics.ReportApplyFinished(c, err)
493+
return err
494+
}
458495
logrus.Infof("Materializing binaries")
459496
if err := goods.Materialize(); err != nil {
460497
err := fmt.Errorf("unable to materialize binaries: %w", err)

e2e/install_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,3 +368,32 @@ func runPuppeteerAppStatusCheck(t *testing.T, node int, tc *cluster.Output) {
368368
t.Fatalf("cluster or app not ready: %s", stdout)
369369
}
370370
}
371+
372+
func TestInstallWithoutRootSSHAccess(t *testing.T) {
373+
t.Parallel()
374+
tc := cluster.NewTestCluster(&cluster.Input{
375+
T: t,
376+
Nodes: 1,
377+
Image: "ubuntu/jammy",
378+
SSHPublicKey: "../output/tmp/id_rsa.pub",
379+
SSHPrivateKey: "../output/tmp/id_rsa",
380+
EmbeddedClusterPath: "../output/bin/embedded-cluster",
381+
})
382+
defer tc.Destroy()
383+
t.Log("installing ssh on node 0")
384+
commands := [][]string{
385+
{"apt-get", "update", "-y"},
386+
{"apt-get", "install", "openssh-server", "-y"},
387+
}
388+
if err := RunCommandsOnNode(t, tc, 0, commands); err != nil {
389+
t.Fatalf("fail to install ssh on node %s: %v", tc.Nodes[0], err)
390+
}
391+
t.Log("testing installation without root access")
392+
line := []string{"single-node-install-without-root.sh"}
393+
if stdout, stderr, err := RunCommandOnNode(t, tc, 0, line); err != nil {
394+
t.Log("install stdout:", stdout)
395+
t.Log("install stderr:", stderr)
396+
t.Log("failed")
397+
t.Fatalf("fail to install embedded-cluster on node %s: %v", tc.Nodes[0], err)
398+
}
399+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/usr/bin/env bash
2+
set -euox pipefail
3+
4+
main() {
5+
echo "PermitRootLogin no" >> /etc/ssh/sshd_config
6+
if ! systemctl restart sshd; then
7+
echo "Failed to restart sshd"
8+
return 1
9+
fi
10+
if embedded-cluster install --no-prompt 2>&1 | tee /tmp/log ; then
11+
cat /tmp/log
12+
echo "This should never happen, able to install!"
13+
exit 1
14+
fi
15+
if ! grep -q "You can temporarily enable root login by changing" /tmp/log ; then
16+
cat /tmp/log
17+
echo "Failed to find expected error message"
18+
return 1
19+
fi
20+
sed -i '/^PermitRootLogin/c\PermitRootLogin without-password' /etc/ssh/sshd_config
21+
if ! systemctl restart sshd; then
22+
echo "Failed to restart sshd"
23+
return 1
24+
fi
25+
if ! embedded-cluster install --no-prompt 2>&1 | tee /tmp/log ; then
26+
cat /tmp/log
27+
echo "Unable to install with root login enabled without password"
28+
exit 1
29+
fi
30+
}
31+
32+
export EMBEDDED_CLUSTER_METRICS_BASEURL="https://staging.replicated.app"
33+
export KUBECONFIG=/root/.config/embedded-cluster/etc/kubeconfig
34+
export PATH=$PATH:/root/.config/embedded-cluster/bin
35+
main

0 commit comments

Comments
 (0)