Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Warnings that k8s service may not work #657

Merged
merged 11 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/k8s/cmd/k8s/k8s_bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func newBootstrapCmd(env cmdutil.ExecutionEnvironment) *cobra.Command {
Use: "bootstrap",
Short: "Bootstrap a new Kubernetes cluster",
Long: "Generate certificates, configure service arguments and start the Kubernetes services.",
PreRun: chainPreRunHooks(hookRequireRoot(env), hookInitializeFormatter(env, &opts.outputFormat)),
PreRun: chainPreRunHooks(hookRequireRoot(env), hookInitializeFormatter(env, &opts.outputFormat), cmdutil.HookVerifyResources()),
Run: func(cmd *cobra.Command, args []string) {
if opts.interactive && opts.configFile != "" {
cmd.PrintErrln("Error: --interactive and --file flags cannot be set at the same time.")
Expand Down
5 changes: 3 additions & 2 deletions src/k8s/cmd/k8sd/k8sd.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ func addCommands(root *cobra.Command, group *cobra.Group, commands ...*cobra.Com

func NewRootCmd(env cmdutil.ExecutionEnvironment) *cobra.Command {
cmd := &cobra.Command{
Use: "k8sd",
Short: "Canonical Kubernetes orchestrator and clustering daemon",
Use: "k8sd",
Short: "Canonical Kubernetes orchestrator and clustering daemon",
PreRun: cmdutil.HookVerifyResources(),
Run: func(cmd *cobra.Command, args []string) {
// configure logging
log.Configure(log.Options{
Expand Down
118 changes: 118 additions & 0 deletions src/k8s/cmd/util/hooks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package cmdutil

import (
"errors"
"fmt"
"os"
"os/exec"
"strings"
"syscall"

"github.com/spf13/cobra"
)

const initialProcesEnvironmentVariables = "/proc/1/environ"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Process

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also add a short comment please


// paths to validate if root in the owner
maci3jka marked this conversation as resolved.
Show resolved Hide resolved
var pathsOwnershipCheck = []string{"/sys", "/proc", "/dev/kmsg"}

// HookVerifyResources checks ownership of dirs required for k8s to run.
// HookVerifyResources validates AppArmor configurations.
// If potential issue found pops up warning.
func HookVerifyResources() func(*cobra.Command, []string) {
maci3jka marked this conversation as resolved.
Show resolved Hide resolved
return func(cmd *cobra.Command, args []string) {
maci3jka marked this conversation as resolved.
Show resolved Hide resolved
var warnList []string

// check ownership of required dirs
for _, path := range pathsOwnershipCheck {
if msg, err := validateRootOwnership(path); err != nil {
cmd.PrintErrf(err.Error())
} else if len(msg) > 0 {
warnList = append(warnList, msg)
}
}

// check App Armor
if armor, err := checkAppArmor(); err != nil {
cmd.PrintErr(err.Error())
} else if len(armor) > 0 {
warnList = append(warnList, armor)
}

// check LXD
if lxd, err := validateLXD(); err != nil {
cmd.PrintErr(err.Error())
} else if len(warnList) > 0 && len(lxd) > 0 {
warnList = append(warnList, lxd)

}

// generate report
if len(warnList) > 0 {
cmd.PrintErrf("Warning: k8s may not run correctly due to reasons:\n%s",
strings.Join(warnList, ""))
}
}
}

// validateLXD checks if k8s runs in lxd container if so returns link to documentation
func validateLXD() (string, error) {
dat, err := os.ReadFile(initialProcesEnvironmentVariables)
if err != nil {
return "", err
}
env := string(dat)
if strings.Contains(env, "container=lxc") {
return "For running k8s inside LXD container refer to " +
"https://documentation.ubuntu.com/canonical-kubernetes/latest/snap/howto/install/lxd/.\n", nil
}
return "", nil
}

// validateRootOwnership checks if given path owner root and root group.
func validateRootOwnership(path string) (string, error) {

info, err := os.Stat(path)
if err != nil {
if os.IsNotExist(err) {
return fmt.Sprintf("%s do not exist.\n", path), nil
} else {
return "", err
}
}
var UID int
var GID int
if stat, ok := info.Sys().(*syscall.Stat_t); ok {
UID = int(stat.Uid)
GID = int(stat.Gid)
} else {
return "", errors.New(fmt.Sprintf("cannot access path %s", path))
}
var warnList string
if UID != 0 {
warnList += fmt.Sprintf("owner of %s is user with UID %d expected 0.\n", path, UID)
}
if GID != 0 {
warnList += fmt.Sprintf("owner of %s is group with GID %d expected 0.\n", path, GID)
}
return warnList, nil
}

// checkAppArmor checks AppArmor status.
func checkAppArmor() (string, error) {
cmd := exec.Command("journalctl", "-u", "apparmor")
maci3jka marked this conversation as resolved.
Show resolved Hide resolved
out, err := cmd.CombinedOutput()
if err != nil {
return "", err
}
output := string(out)
// AppArmor configured for container or service not present
if strings.Contains(output, "Not starting AppArmor in container") || strings.Contains(output, "-- No entries --") {
return "", nil
// cannot read status of AppArmor
} else if strings.Contains(output, "Users in groups 'adm', 'systemd-journal' can see all messages.") {
return "could not validate AppArmor status.\n", nil
}

return "AppArmor may block hosting of nested containers.\n", nil
}
Loading