mirror of https://github.com/k3s-io/k3s
Add rootless support
parent
6f01e13fc4
commit
046a817818
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
"github.com/containerd/containerd/namespaces"
|
"github.com/containerd/containerd/namespaces"
|
||||||
"github.com/natefinch/lumberjack"
|
"github.com/natefinch/lumberjack"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/system"
|
||||||
util2 "github.com/rancher/k3s/pkg/agent/util"
|
util2 "github.com/rancher/k3s/pkg/agent/util"
|
||||||
"github.com/rancher/k3s/pkg/daemons/config"
|
"github.com/rancher/k3s/pkg/daemons/config"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
@ -31,6 +32,11 @@ const (
|
||||||
[plugins.cri]
|
[plugins.cri]
|
||||||
stream_server_address = "%NODE%"
|
stream_server_address = "%NODE%"
|
||||||
stream_server_port = "10010"
|
stream_server_port = "10010"
|
||||||
|
`
|
||||||
|
configUserNSToml = `
|
||||||
|
disable_cgroup = true
|
||||||
|
disable_apparmor = true
|
||||||
|
restrict_oom_score_adj = true
|
||||||
`
|
`
|
||||||
configCNIToml = `
|
configCNIToml = `
|
||||||
[plugins.cri.cni]
|
[plugins.cri.cni]
|
||||||
|
@ -49,6 +55,9 @@ func Run(ctx context.Context, cfg *config.Node) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
template := configToml
|
template := configToml
|
||||||
|
if system.RunningInUserNS() {
|
||||||
|
template += configUserNSToml
|
||||||
|
}
|
||||||
if !cfg.NoFlannel {
|
if !cfg.NoFlannel {
|
||||||
template += configCNIToml
|
template += configCNIToml
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/rancher/k3s/pkg/agent/tunnel"
|
"github.com/rancher/k3s/pkg/agent/tunnel"
|
||||||
"github.com/rancher/k3s/pkg/cli/cmds"
|
"github.com/rancher/k3s/pkg/cli/cmds"
|
||||||
"github.com/rancher/k3s/pkg/daemons/agent"
|
"github.com/rancher/k3s/pkg/daemons/agent"
|
||||||
|
"github.com/rancher/k3s/pkg/rootless"
|
||||||
"github.com/rancher/norman/pkg/clientaccess"
|
"github.com/rancher/norman/pkg/clientaccess"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
@ -69,6 +70,12 @@ func Run(ctx context.Context, cfg cmds.Agent) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.Rootless {
|
||||||
|
if err := rootless.Rootless(cfg.DataDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cfg.DataDir = filepath.Join(cfg.DataDir, "agent")
|
cfg.DataDir = filepath.Join(cfg.DataDir, "agent")
|
||||||
|
|
||||||
if cfg.ClusterSecret != "" {
|
if cfg.ClusterSecret != "" {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
|
|
||||||
"github.com/rancher/k3s/pkg/agent"
|
"github.com/rancher/k3s/pkg/agent"
|
||||||
"github.com/rancher/k3s/pkg/cli/cmds"
|
"github.com/rancher/k3s/pkg/cli/cmds"
|
||||||
"github.com/rancher/norman/pkg/resolvehome"
|
"github.com/rancher/k3s/pkg/datadir"
|
||||||
"github.com/rancher/norman/signal"
|
"github.com/rancher/norman/signal"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
@ -57,7 +57,7 @@ func Run(ctx *cli.Context) error {
|
||||||
|
|
||||||
logrus.Infof("Starting k3s agent %s", ctx.App.Version)
|
logrus.Infof("Starting k3s agent %s", ctx.App.Version)
|
||||||
|
|
||||||
dataDir, err := resolvehome.Resolve(cmds.AgentConfig.DataDir)
|
dataDir, err := datadir.LocalHome(cmds.AgentConfig.DataDir, cmds.AgentConfig.Rootless)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ type Agent struct {
|
||||||
ContainerRuntimeEndpoint string
|
ContainerRuntimeEndpoint string
|
||||||
NoFlannel bool
|
NoFlannel bool
|
||||||
Debug bool
|
Debug bool
|
||||||
|
Rootless bool
|
||||||
AgentShared
|
AgentShared
|
||||||
ExtraKubeletArgs cli.StringSlice
|
ExtraKubeletArgs cli.StringSlice
|
||||||
ExtraKubeProxyArgs cli.StringSlice
|
ExtraKubeProxyArgs cli.StringSlice
|
||||||
|
@ -113,6 +114,11 @@ func NewAgentCommand(action func(ctx *cli.Context) error) cli.Command {
|
||||||
Destination: &AgentConfig.ClusterSecret,
|
Destination: &AgentConfig.ClusterSecret,
|
||||||
EnvVar: "K3S_CLUSTER_SECRET",
|
EnvVar: "K3S_CLUSTER_SECRET",
|
||||||
},
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "rootless",
|
||||||
|
Usage: "(experimental) Run rootless",
|
||||||
|
Destination: &AgentConfig.Rootless,
|
||||||
|
},
|
||||||
DockerFlag,
|
DockerFlag,
|
||||||
FlannelFlag,
|
FlannelFlag,
|
||||||
NodeNameFlag,
|
NodeNameFlag,
|
||||||
|
|
|
@ -21,6 +21,7 @@ type Server struct {
|
||||||
ExtraAPIArgs cli.StringSlice
|
ExtraAPIArgs cli.StringSlice
|
||||||
ExtraSchedulerArgs cli.StringSlice
|
ExtraSchedulerArgs cli.StringSlice
|
||||||
ExtraControllerArgs cli.StringSlice
|
ExtraControllerArgs cli.StringSlice
|
||||||
|
Rootless bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var ServerConfig Server
|
var ServerConfig Server
|
||||||
|
@ -124,6 +125,11 @@ func NewServerCommand(action func(*cli.Context) error) cli.Command {
|
||||||
Usage: "Customized flag for kube-controller-manager process",
|
Usage: "Customized flag for kube-controller-manager process",
|
||||||
Value: &ServerConfig.ExtraControllerArgs,
|
Value: &ServerConfig.ExtraControllerArgs,
|
||||||
},
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "rootless",
|
||||||
|
Usage: "(experimental) Run rootless",
|
||||||
|
Destination: &ServerConfig.Rootless,
|
||||||
|
},
|
||||||
NodeIPFlag,
|
NodeIPFlag,
|
||||||
NodeNameFlag,
|
NodeNameFlag,
|
||||||
DockerFlag,
|
DockerFlag,
|
||||||
|
|
|
@ -15,12 +15,14 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/rancher/k3s/pkg/agent"
|
"github.com/rancher/k3s/pkg/agent"
|
||||||
"github.com/rancher/k3s/pkg/cli/cmds"
|
"github.com/rancher/k3s/pkg/cli/cmds"
|
||||||
|
"github.com/rancher/k3s/pkg/datadir"
|
||||||
|
"github.com/rancher/k3s/pkg/rootless"
|
||||||
"github.com/rancher/k3s/pkg/server"
|
"github.com/rancher/k3s/pkg/server"
|
||||||
"github.com/rancher/norman/signal"
|
"github.com/rancher/norman/signal"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
"k8s.io/apimachinery/pkg/util/net"
|
"k8s.io/apimachinery/pkg/util/net"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume/csi"
|
||||||
|
|
||||||
_ "github.com/mattn/go-sqlite3" // ensure we have sqlite
|
_ "github.com/mattn/go-sqlite3" // ensure we have sqlite
|
||||||
)
|
)
|
||||||
|
@ -67,18 +69,30 @@ func run(app *cli.Context, cfg *cmds.Server) error {
|
||||||
|
|
||||||
setupLogging(app)
|
setupLogging(app)
|
||||||
|
|
||||||
if !cfg.DisableAgent && os.Getuid() != 0 {
|
if !cfg.DisableAgent && os.Getuid() != 0 && !cfg.Rootless {
|
||||||
return fmt.Errorf("must run as root unless --disable-agent is specified")
|
return fmt.Errorf("must run as root unless --disable-agent is specified")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.Rootless {
|
||||||
|
dataDir, err := datadir.LocalHome(cfg.DataDir, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cfg.DataDir = dataDir
|
||||||
|
if err := rootless.Rootless(dataDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If running agent in server, set this so that CSI initializes properly
|
// If running agent in server, set this so that CSI initializes properly
|
||||||
volume.WaitForValidHost = !cfg.DisableAgent
|
csi.WaitForValidHostName = !cfg.DisableAgent
|
||||||
|
|
||||||
serverConfig := server.Config{}
|
serverConfig := server.Config{}
|
||||||
serverConfig.ControlConfig.ClusterSecret = cfg.ClusterSecret
|
serverConfig.ControlConfig.ClusterSecret = cfg.ClusterSecret
|
||||||
serverConfig.ControlConfig.DataDir = cfg.DataDir
|
serverConfig.ControlConfig.DataDir = cfg.DataDir
|
||||||
serverConfig.ControlConfig.KubeConfigOutput = cfg.KubeConfigOutput
|
serverConfig.ControlConfig.KubeConfigOutput = cfg.KubeConfigOutput
|
||||||
serverConfig.ControlConfig.KubeConfigMode = cfg.KubeConfigMode
|
serverConfig.ControlConfig.KubeConfigMode = cfg.KubeConfigMode
|
||||||
|
serverConfig.Rootless = cfg.Rootless
|
||||||
serverConfig.TLSConfig.HTTPSPort = cfg.HTTPSPort
|
serverConfig.TLSConfig.HTTPSPort = cfg.HTTPSPort
|
||||||
serverConfig.TLSConfig.HTTPPort = cfg.HTTPPort
|
serverConfig.TLSConfig.HTTPPort = cfg.HTTPPort
|
||||||
serverConfig.TLSConfig.KnownIPs = knownIPs(cfg.KnownIPs)
|
serverConfig.TLSConfig.KnownIPs = knownIPs(cfg.KnownIPs)
|
||||||
|
|
|
@ -9,12 +9,14 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/opencontainers/runc/libcontainer/system"
|
||||||
"github.com/rancher/k3s/pkg/daemons/config"
|
"github.com/rancher/k3s/pkg/daemons/config"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"k8s.io/apimachinery/pkg/util/net"
|
"k8s.io/apimachinery/pkg/util/net"
|
||||||
"k8s.io/component-base/logs"
|
"k8s.io/component-base/logs"
|
||||||
app2 "k8s.io/kubernetes/cmd/kube-proxy/app"
|
app2 "k8s.io/kubernetes/cmd/kube-proxy/app"
|
||||||
"k8s.io/kubernetes/cmd/kubelet/app"
|
"k8s.io/kubernetes/cmd/kubelet/app"
|
||||||
|
|
||||||
_ "k8s.io/kubernetes/pkg/client/metrics/prometheus" // for client metric registration
|
_ "k8s.io/kubernetes/pkg/client/metrics/prometheus" // for client metric registration
|
||||||
_ "k8s.io/kubernetes/pkg/version/prometheus" // for version metric registration
|
_ "k8s.io/kubernetes/pkg/version/prometheus" // for version metric registration
|
||||||
)
|
)
|
||||||
|
@ -107,8 +109,11 @@ func kubelet(cfg *config.Agent) {
|
||||||
argsMap["runtime-cgroups"] = root
|
argsMap["runtime-cgroups"] = root
|
||||||
argsMap["kubelet-cgroups"] = root
|
argsMap["kubelet-cgroups"] = root
|
||||||
}
|
}
|
||||||
args := config.GetArgsList(argsMap, cfg.ExtraKubeletArgs)
|
if system.RunningInUserNS() {
|
||||||
|
argsMap["feature-gates"] = "DevicePlugins=false"
|
||||||
|
}
|
||||||
|
|
||||||
|
args := config.GetArgsList(argsMap, cfg.ExtraKubeletArgs)
|
||||||
command.SetArgs(args)
|
command.SetArgs(args)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
|
|
@ -15,8 +15,12 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func Resolve(dataDir string) (string, error) {
|
func Resolve(dataDir string) (string, error) {
|
||||||
|
return LocalHome(dataDir, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LocalHome(dataDir string, forceLocal bool) (string, error) {
|
||||||
if dataDir == "" {
|
if dataDir == "" {
|
||||||
if os.Getuid() == 0 {
|
if os.Getuid() == 0 && !forceLocal {
|
||||||
dataDir = DefaultDataDir
|
dataDir = DefaultDataDir
|
||||||
} else {
|
} else {
|
||||||
dataDir = DefaultHomeDataDir
|
dataDir = DefaultHomeDataDir
|
||||||
|
|
|
@ -17,7 +17,7 @@ import (
|
||||||
func Main() {
|
func Main() {
|
||||||
kubenv := os.Getenv("KUBECONFIG")
|
kubenv := os.Getenv("KUBECONFIG")
|
||||||
if kubenv == "" {
|
if kubenv == "" {
|
||||||
config, err := server.HomeKubeConfig(false)
|
config, err := server.HomeKubeConfig(false, false)
|
||||||
if _, serr := os.Stat(config); err == nil && serr == nil {
|
if _, serr := os.Stat(config); err == nil && serr == nil {
|
||||||
os.Setenv("KUBECONFIG", config)
|
os.Setenv("KUBECONFIG", config)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
package rootless
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setupMounts(stateDir string) error {
|
||||||
|
mountMap := [][]string{
|
||||||
|
{"/run", ""},
|
||||||
|
{"/var/run", ""},
|
||||||
|
{"/var/log", filepath.Join(stateDir, "logs")},
|
||||||
|
{"/var/lib/cni", filepath.Join(stateDir, "cni")},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range mountMap {
|
||||||
|
if err := setupMount(v[0], v[1]); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to setup mount %s => %s", v[0], v[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupMount(target, dir string) error {
|
||||||
|
toCreate := target
|
||||||
|
for {
|
||||||
|
if toCreate == "/" {
|
||||||
|
return fmt.Errorf("missing /%s on the root filesystem", strings.Split(target, "/")[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(toCreate, 0700); err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
toCreate = filepath.Base(toCreate)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debug("Mounting none ", toCreate, " tmpfs")
|
||||||
|
if err := unix.Mount("none", toCreate, "tmpfs", 0, ""); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to mount tmpfs to %s", toCreate)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(target, 0700); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to create directory %s")
|
||||||
|
}
|
||||||
|
|
||||||
|
if dir == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to create directory %s")
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debug("Mounting ", dir, target, " none bind")
|
||||||
|
return unix.Mount(dir, target, "none", unix.MS_BIND, "")
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
package rootless
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/rootless-containers/rootlesskit/pkg/child"
|
||||||
|
"github.com/rootless-containers/rootlesskit/pkg/copyup/tmpfssymlink"
|
||||||
|
"github.com/rootless-containers/rootlesskit/pkg/network/slirp4netns"
|
||||||
|
"github.com/rootless-containers/rootlesskit/pkg/parent"
|
||||||
|
"github.com/rootless-containers/rootlesskit/pkg/port/socat"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
pipeFD = "_K3S_ROOTLESS_FD"
|
||||||
|
childEnv = "_K3S_ROOTLESS_SOCK"
|
||||||
|
Sock = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
func Rootless(stateDir string) error {
|
||||||
|
defer func() {
|
||||||
|
os.Unsetenv(pipeFD)
|
||||||
|
os.Unsetenv(childEnv)
|
||||||
|
}()
|
||||||
|
|
||||||
|
hasFD := os.Getenv(pipeFD) != ""
|
||||||
|
hasChildEnv := os.Getenv(childEnv) != ""
|
||||||
|
|
||||||
|
if hasFD {
|
||||||
|
logrus.Debug("Running rootless child")
|
||||||
|
childOpt, err := createChildOpt()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := child.Child(*childOpt); err != nil {
|
||||||
|
logrus.Fatal("child died", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasChildEnv {
|
||||||
|
Sock = os.Getenv(childEnv)
|
||||||
|
logrus.Debug("Running rootless process")
|
||||||
|
return setupMounts(stateDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debug("Running rootless parent")
|
||||||
|
parentOpt, err := createParentOpt(filepath.Join(stateDir, "rootless"))
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Setenv(childEnv, filepath.Join(parentOpt.StateDir, parent.StateFileAPISock))
|
||||||
|
if err := parent.Parent(*parentOpt); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseCIDR(s string) (*net.IPNet, error) {
|
||||||
|
if s == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
ip, ipnet, err := net.ParseCIDR(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !ip.Equal(ipnet.IP) {
|
||||||
|
return nil, errors.Errorf("cidr must be like 10.0.2.0/24, not like 10.0.2.100/24")
|
||||||
|
}
|
||||||
|
return ipnet, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createParentOpt(stateDir string) (*parent.Opt, error) {
|
||||||
|
if err := os.MkdirAll(stateDir, 0755); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to mkdir %s", stateDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
stateDir, err := ioutil.TempDir("", "rootless")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
opt := &parent.Opt{
|
||||||
|
StateDir: stateDir,
|
||||||
|
}
|
||||||
|
|
||||||
|
mtu := 0
|
||||||
|
ipnet, err := parseCIDR("10.41.0.0/16")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
disableHostLoopback := true
|
||||||
|
binary := "slirp4netns"
|
||||||
|
if _, err := exec.LookPath(binary); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
opt.NetworkDriver = slirp4netns.NewParentDriver(binary, mtu, ipnet, disableHostLoopback, "")
|
||||||
|
opt.PortDriver, err = socat.NewParentDriver(&logrusDebugWriter{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
opt.PipeFDEnvKey = pipeFD
|
||||||
|
|
||||||
|
return opt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type logrusDebugWriter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *logrusDebugWriter) Write(p []byte) (int, error) {
|
||||||
|
s := strings.TrimSuffix(string(p), "\n")
|
||||||
|
logrus.Debug(s)
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createChildOpt() (*child.Opt, error) {
|
||||||
|
opt := &child.Opt{}
|
||||||
|
opt.TargetCmd = os.Args
|
||||||
|
opt.PipeFDEnvKey = pipeFD
|
||||||
|
opt.NetworkDriver = slirp4netns.NewChildDriver()
|
||||||
|
opt.CopyUpDirs = []string{"/etc", "/run"}
|
||||||
|
opt.CopyUpDriver = tmpfssymlink.NewChildDriver()
|
||||||
|
return opt, nil
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
package rootlessports
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rancher/k3s/pkg/rootless"
|
||||||
|
coreClients "github.com/rancher/k3s/types/apis/core/v1"
|
||||||
|
"github.com/rootless-containers/rootlesskit/pkg/api/client"
|
||||||
|
"github.com/rootless-containers/rootlesskit/pkg/port"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
all = "_all_"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Register(ctx context.Context, httpsPort int) error {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
rootlessClient client.Client
|
||||||
|
)
|
||||||
|
|
||||||
|
if rootless.Sock == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
coreClients := coreClients.ClientsFrom(ctx)
|
||||||
|
for i := 0; i < 30; i++ {
|
||||||
|
rootlessClient, err = client.New(rootless.Sock)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
logrus.Infof("waiting for rootless API socket %s: %v", rootless.Sock, err)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
h := &handler{
|
||||||
|
rootlessClient: rootlessClient,
|
||||||
|
serviceClient: coreClients.Service,
|
||||||
|
serviceCache: coreClients.Service.Cache(),
|
||||||
|
httpsPort: httpsPort,
|
||||||
|
ctx: ctx,
|
||||||
|
}
|
||||||
|
coreClients.Service.Interface().Controller().AddHandler(ctx, "rootlessports", h.serviceChanged)
|
||||||
|
coreClients.Service.Enqueue("", all)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type handler struct {
|
||||||
|
rootlessClient client.Client
|
||||||
|
serviceClient coreClients.ServiceClient
|
||||||
|
serviceCache coreClients.ServiceClientCache
|
||||||
|
httpsPort int
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) serviceChanged(key string, svc *v1.Service) (runtime.Object, error) {
|
||||||
|
if key != all {
|
||||||
|
h.serviceClient.Enqueue("", all)
|
||||||
|
return svc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ports, err := h.rootlessClient.PortManager().ListPorts(h.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return svc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
boundPorts := map[int]int{}
|
||||||
|
for _, port := range ports {
|
||||||
|
boundPorts[port.Spec.ParentPort] = port.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
toBindPort, err := h.toBindPorts()
|
||||||
|
if err != nil {
|
||||||
|
return svc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for bindPort, childBindPort := range toBindPort {
|
||||||
|
if _, ok := boundPorts[bindPort]; ok {
|
||||||
|
logrus.Debugf("Parent port %d to child already bound", bindPort)
|
||||||
|
delete(boundPorts, bindPort)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
status, err := h.rootlessClient.PortManager().AddPort(h.ctx, port.Spec{
|
||||||
|
Proto: "tcp",
|
||||||
|
ParentPort: bindPort,
|
||||||
|
ChildPort: childBindPort,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return svc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Infof("Bound parent port %s:%d to child namespace port %d", status.Spec.ParentIP,
|
||||||
|
status.Spec.ParentPort, status.Spec.ChildPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
for bindPort, id := range boundPorts {
|
||||||
|
if err := h.rootlessClient.PortManager().RemovePort(h.ctx, id); err != nil {
|
||||||
|
return svc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Infof("Removed parent port %d to child namespace", bindPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
return svc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) toBindPorts() (map[int]int, error) {
|
||||||
|
svcs, err := h.serviceCache.List("", labels.Everything())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
toBindPorts := map[int]int{
|
||||||
|
h.httpsPort: h.httpsPort,
|
||||||
|
}
|
||||||
|
for _, svc := range svcs {
|
||||||
|
for _, ingress := range svc.Status.LoadBalancer.Ingress {
|
||||||
|
if ingress.IP == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, port := range svc.Spec.Ports {
|
||||||
|
if port.Protocol != v1.ProtocolTCP {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if port.Port != 0 {
|
||||||
|
if port.Port <= 1024 {
|
||||||
|
toBindPorts[10000+int(port.Port)] = int(port.Port)
|
||||||
|
} else {
|
||||||
|
toBindPorts[int(port.Port)] = int(port.Port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return toBindPorts, nil
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"github.com/rancher/k3s/pkg/datadir"
|
"github.com/rancher/k3s/pkg/datadir"
|
||||||
"github.com/rancher/k3s/pkg/deploy"
|
"github.com/rancher/k3s/pkg/deploy"
|
||||||
"github.com/rancher/k3s/pkg/helm"
|
"github.com/rancher/k3s/pkg/helm"
|
||||||
|
"github.com/rancher/k3s/pkg/rootlessports"
|
||||||
"github.com/rancher/k3s/pkg/servicelb"
|
"github.com/rancher/k3s/pkg/servicelb"
|
||||||
"github.com/rancher/k3s/pkg/static"
|
"github.com/rancher/k3s/pkg/static"
|
||||||
"github.com/rancher/k3s/pkg/tls"
|
"github.com/rancher/k3s/pkg/tls"
|
||||||
|
@ -36,16 +37,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func resolveDataDir(dataDir string) (string, error) {
|
func resolveDataDir(dataDir string) (string, error) {
|
||||||
if dataDir == "" {
|
dataDir, err := datadir.Resolve(dataDir)
|
||||||
if os.Getuid() == 0 {
|
return filepath.Join(dataDir, "server"), err
|
||||||
dataDir = "/var/lib/rancher/k3s"
|
|
||||||
} else {
|
|
||||||
dataDir = "${HOME}/.rancher/k3s"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dataDir = filepath.Join(dataDir, "server")
|
|
||||||
return resolvehome.Resolve(dataDir)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func StartServer(ctx context.Context, config *Config) (string, error) {
|
func StartServer(ctx context.Context, config *Config) (string, error) {
|
||||||
|
@ -72,7 +65,7 @@ func StartServer(ctx context.Context, config *Config) (string, error) {
|
||||||
}
|
}
|
||||||
printTokens(certs, ip.String(), &config.TLSConfig, &config.ControlConfig)
|
printTokens(certs, ip.String(), &config.TLSConfig, &config.ControlConfig)
|
||||||
|
|
||||||
writeKubeConfig(certs, &config.TLSConfig, &config.ControlConfig)
|
writeKubeConfig(certs, &config.TLSConfig, config)
|
||||||
|
|
||||||
return certs, nil
|
return certs, nil
|
||||||
}
|
}
|
||||||
|
@ -121,7 +114,8 @@ func startNorman(ctx context.Context, config *Config) (string, error) {
|
||||||
MasterControllers: []norman.ControllerRegister{
|
MasterControllers: []norman.ControllerRegister{
|
||||||
helm.Register,
|
helm.Register,
|
||||||
func(ctx context.Context) error {
|
func(ctx context.Context) error {
|
||||||
return servicelb.Register(ctx, norman.GetServer(ctx).K8sClient, !config.DisableServiceLB)
|
return servicelb.Register(ctx, norman.GetServer(ctx).K8sClient, !config.DisableServiceLB,
|
||||||
|
config.Rootless)
|
||||||
},
|
},
|
||||||
func(ctx context.Context) error {
|
func(ctx context.Context) error {
|
||||||
dataDir := filepath.Join(controlConfig.DataDir, "static")
|
dataDir := filepath.Join(controlConfig.DataDir, "static")
|
||||||
|
@ -138,6 +132,12 @@ func startNorman(ctx context.Context, config *Config) (string, error) {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
func(ctx context.Context) error {
|
||||||
|
if !config.DisableServiceLB && config.Rootless {
|
||||||
|
return rootlessports.Register(ctx, config.TLSConfig.HTTPSPort)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,9 +156,9 @@ func startNorman(ctx context.Context, config *Config) (string, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HomeKubeConfig(write bool) (string, error) {
|
func HomeKubeConfig(write, rootless bool) (string, error) {
|
||||||
if write {
|
if write {
|
||||||
if os.Getuid() == 0 {
|
if os.Getuid() == 0 && !rootless {
|
||||||
return datadir.GlobalConfig, nil
|
return datadir.GlobalConfig, nil
|
||||||
}
|
}
|
||||||
return resolvehome.Resolve(datadir.HomeConfig)
|
return resolvehome.Resolve(datadir.HomeConfig)
|
||||||
|
@ -194,30 +194,30 @@ func printTokens(certs, advertiseIP string, tlsConfig *dynamiclistener.UserConfi
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeKubeConfig(certs string, tlsConfig *dynamiclistener.UserConfig, config *config.Control) {
|
func writeKubeConfig(certs string, tlsConfig *dynamiclistener.UserConfig, config *Config) {
|
||||||
clientToken := FormatToken(config.Runtime.ClientToken, certs)
|
clientToken := FormatToken(config.ControlConfig.Runtime.ClientToken, certs)
|
||||||
ip := tlsConfig.BindAddress
|
ip := tlsConfig.BindAddress
|
||||||
if ip == "" {
|
if ip == "" {
|
||||||
ip = "localhost"
|
ip = "localhost"
|
||||||
}
|
}
|
||||||
url := fmt.Sprintf("https://%s:%d", ip, tlsConfig.HTTPSPort)
|
url := fmt.Sprintf("https://%s:%d", ip, tlsConfig.HTTPSPort)
|
||||||
kubeConfig, err := HomeKubeConfig(true)
|
kubeConfig, err := HomeKubeConfig(true, config.Rootless)
|
||||||
def := true
|
def := true
|
||||||
if err != nil {
|
if err != nil {
|
||||||
kubeConfig = filepath.Join(config.DataDir, "kubeconfig-k3s.yaml")
|
kubeConfig = filepath.Join(config.ControlConfig.DataDir, "kubeconfig-k3s.yaml")
|
||||||
def = false
|
def = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.KubeConfigOutput != "" {
|
if config.ControlConfig.KubeConfigOutput != "" {
|
||||||
kubeConfig = config.KubeConfigOutput
|
kubeConfig = config.ControlConfig.KubeConfigOutput
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = clientaccess.AgentAccessInfoToKubeConfig(kubeConfig, url, clientToken); err != nil {
|
if err = clientaccess.AgentAccessInfoToKubeConfig(kubeConfig, url, clientToken); err != nil {
|
||||||
logrus.Errorf("Failed to generate kubeconfig: %v", err)
|
logrus.Errorf("Failed to generate kubeconfig: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.KubeConfigMode != "" {
|
if config.ControlConfig.KubeConfigMode != "" {
|
||||||
mode, err := strconv.ParseInt(config.KubeConfigMode, 8, 0)
|
mode, err := strconv.ParseInt(config.ControlConfig.KubeConfigMode, 8, 0)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
os.Chmod(kubeConfig, os.FileMode(mode))
|
os.Chmod(kubeConfig, os.FileMode(mode))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -10,4 +10,5 @@ type Config struct {
|
||||||
DisableServiceLB bool
|
DisableServiceLB bool
|
||||||
TLSConfig dynamiclistener.UserConfig
|
TLSConfig dynamiclistener.UserConfig
|
||||||
ControlConfig config.Control
|
ControlConfig config.Control
|
||||||
|
Rootless bool
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,11 +34,12 @@ var (
|
||||||
trueVal = true
|
trueVal = true
|
||||||
)
|
)
|
||||||
|
|
||||||
func Register(ctx context.Context, kubernetes kubernetes.Interface, enabled bool) error {
|
func Register(ctx context.Context, kubernetes kubernetes.Interface, enabled, rootless bool) error {
|
||||||
clients := coreclient.ClientsFrom(ctx)
|
clients := coreclient.ClientsFrom(ctx)
|
||||||
appClients := appclient.ClientsFrom(ctx)
|
appClients := appclient.ClientsFrom(ctx)
|
||||||
|
|
||||||
h := &handler{
|
h := &handler{
|
||||||
|
rootless: rootless,
|
||||||
enabled: enabled,
|
enabled: enabled,
|
||||||
nodeCache: clients.Node.Cache(),
|
nodeCache: clients.Node.Cache(),
|
||||||
podCache: clients.Pod.Cache(),
|
podCache: clients.Pod.Cache(),
|
||||||
|
@ -59,6 +60,7 @@ func Register(ctx context.Context, kubernetes kubernetes.Interface, enabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type handler struct {
|
type handler struct {
|
||||||
|
rootless bool
|
||||||
enabled bool
|
enabled bool
|
||||||
nodeCache coreclient.NodeClientCache
|
nodeCache coreclient.NodeClientCache
|
||||||
podCache coreclient.PodClientCache
|
podCache coreclient.PodClientCache
|
||||||
|
@ -189,6 +191,11 @@ func (h *handler) podIPs(pods []*core.Pod) ([]string, error) {
|
||||||
for k := range ips {
|
for k := range ips {
|
||||||
ipList = append(ipList, k)
|
ipList = append(ipList, k)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(ipList) > 0 && h.rootless {
|
||||||
|
return []string{"127.0.0.1"}, nil
|
||||||
|
}
|
||||||
|
|
||||||
return ipList, nil
|
return ipList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,4 +5,9 @@ mkdir -p $(dirname $0)/../bin
|
||||||
cd $(dirname $0)/../bin
|
cd $(dirname $0)/../bin
|
||||||
|
|
||||||
echo Running
|
echo Running
|
||||||
go run -tags "apparmor" ../main.go --debug server --disable-agent "$@"
|
ARGS="--disable-agent"
|
||||||
|
if echo -- "$@" | grep -q rootless; then
|
||||||
|
ARGS=""
|
||||||
|
PATH=$(pwd):$PATH
|
||||||
|
fi
|
||||||
|
go run -tags "apparmor" ../main.go server $ARGS "$@"
|
||||||
|
|
|
@ -4,7 +4,7 @@ source $(dirname $0)/version.sh
|
||||||
|
|
||||||
cd $(dirname $0)/..
|
cd $(dirname $0)/..
|
||||||
|
|
||||||
ROOT_VERSION=v0.0.1
|
ROOT_VERSION=v0.1.1
|
||||||
TRAEFIK_VERSION=1.64.0
|
TRAEFIK_VERSION=1.64.0
|
||||||
CHARTS_DIR=build/static/charts
|
CHARTS_DIR=build/static/charts
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue