Skip to content

Commit

Permalink
chore: run apid and trustd services as non-root user
Browse files Browse the repository at this point in the history
For the `trustd`, this change is simple as it doesn't access any files
on the host filesystem.

For the `apid`, there are more things involved:

* `apid.sock` used for internal API calls should be createable by `apid`
* `runtime.sock` used for apid to COSI communication should be
accessible for `apid`
* `machined.sock` used for proxying calls to machined should be as well
made available to the `apid`.

Plus fixes default permissions for `tmpfs` mountpoints.

Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
  • Loading branch information
smira committed Aug 13, 2021
1 parent dadaa65 commit 33d1c3e
Show file tree
Hide file tree
Showing 8 changed files with 54 additions and 7 deletions.
3 changes: 2 additions & 1 deletion hack/release.toml
Expand Up @@ -93,6 +93,7 @@ Talos can be configued to use Kubernetes 1.21 or CAPI v0.4.x components can be u
* etcd PKI moved to `/system/secrets`
* kubelet bootstrap CSR auto-signing scoped to kubelet bootstrap tokens only
* enforce default seccomp profile on all system containers
* run system services apid and trustd as non-root users
"""

[notes.equinixmetal]
Expand All @@ -105,7 +106,7 @@ Talos automatically re-assigns IP using the Equinix Metal API when leadership ch
[notes.configuration]
title = "Machine Config Validation"
description = """\
Unknown keys in the machine config now make the config invalid,
Unknown keys in the machine config now make the config invalid,
so any attempt to apply/edit the configuration with the unknown keys will lead into an error.
"""

Expand Down
Expand Up @@ -120,12 +120,18 @@ func EnforceKSPPRequirements(seq runtime.Sequence, data interface{}) (runtime.Ta
// SetupSystemDirectory represents the SetupSystemDirectory task.
func SetupSystemDirectory(seq runtime.Sequence, data interface{}) (runtime.TaskExecutionFunc, string) {
return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) {
for _, p := range []string{constants.SystemEtcPath, constants.SystemRunPath, constants.SystemVarPath, constants.StateMountPoint} {
for _, p := range []string{constants.SystemEtcPath, constants.SystemVarPath, constants.StateMountPoint} {
if err = os.MkdirAll(p, 0o700); err != nil {
return err
}
}

for _, p := range []string{constants.SystemRunPath} {
if err = os.MkdirAll(p, 0o751); err != nil {
return err
}
}

return nil
}, "setupSystemDirectory"
}
Expand Down
16 changes: 16 additions & 0 deletions internal/app/machined/pkg/system/services/apid.go
Expand Up @@ -67,11 +67,21 @@ func (o *APID) PreFunc(ctx context.Context, r runtime.Runtime) error {
return err
}

// set the final leaf to be world-executable to make apid connect to the socket
if err := os.Chmod(filepath.Dir(constants.APIRuntimeSocketPath), 0o751); err != nil {
return err
}

listener, err := net.Listen("unix", constants.APIRuntimeSocketPath)
if err != nil {
return err
}

// chown the socket path to make it accessible to the apid
if err := os.Chown(constants.APIRuntimeSocketPath, constants.ApidUserID, constants.ApidUserID); err != nil {
return err
}

o.runtimeServer = grpc.NewServer()
v1alpha1.RegisterStateServer(o.runtimeServer, server.NewState(resources))

Expand Down Expand Up @@ -104,6 +114,11 @@ func (o *APID) Runner(r runtime.Runtime) (runner.Runner, error) {
return nil, err
}

// Make sure apid user owns socket directory.
if err := os.Chown(filepath.Dir(constants.APISocketPath), constants.ApidUserID, constants.ApidUserID); err != nil {
return nil, err
}

// Set the process arguments.
args := runner.Args{
ID: o.ID(r),
Expand Down Expand Up @@ -155,6 +170,7 @@ func (o *APID) Runner(r runtime.Runtime) (runner.Runner, error) {
oci.WithMounts(mounts),
oci.WithRootFSPath(filepath.Join(constants.SystemLibexecPath, o.ID(r))),
oci.WithRootFSReadonly(),
oci.WithUser(fmt.Sprintf("%d:%d", constants.ApidUserID, constants.ApidUserID)),
),
runner.WithOOMScoreAdj(-998),
),
Expand Down
17 changes: 17 additions & 0 deletions internal/app/machined/pkg/system/services/machined.go
Expand Up @@ -8,6 +8,8 @@ import (
"context"
"io"
"log"
"os"
"path/filepath"

v1alpha1server "github.com/talos-systems/talos/internal/app/machined/internal/server/v1alpha1"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
Expand Down Expand Up @@ -112,11 +114,26 @@ func (s *machinedService) Main(ctx context.Context, r runtime.Runtime, logWriter
factory.WithStreamInterceptor(authorizer.StreamInterceptor()),
)

// ensure socket dir exists
if err := os.MkdirAll(filepath.Dir(constants.MachineSocketPath), 0o750); err != nil {
return err
}

// set the final leaf to be world-executable to make apid connect to the socket
if err := os.Chmod(filepath.Dir(constants.MachineSocketPath), 0o751); err != nil {
return err
}

listener, err := factory.NewListener(factory.Network("unix"), factory.SocketPath(constants.MachineSocketPath))
if err != nil {
return err
}

// chown the socket path to make it accessible to the apid
if err := os.Chown(constants.MachineSocketPath, constants.ApidUserID, constants.ApidUserID); err != nil {
return err
}

defer server.Stop()

go func() {
Expand Down
1 change: 1 addition & 0 deletions internal/app/machined/pkg/system/services/trustd.go
Expand Up @@ -103,6 +103,7 @@ func (t *Trustd) Runner(r runtime.Runtime) (runner.Runner, error) {
oci.WithMounts(mounts),
oci.WithRootFSPath(filepath.Join(constants.SystemLibexecPath, t.ID(r))),
oci.WithRootFSReadonly(),
oci.WithUser(fmt.Sprintf("%d:%d", constants.TrustdUserID, constants.TrustdUserID)),
),
runner.WithOOMScoreAdj(-998),
),
Expand Down
4 changes: 2 additions & 2 deletions internal/app/machined/pkg/system/services/utils.go
Expand Up @@ -19,13 +19,13 @@ import (
func prepareRootfs(id string) error {
rootfsPath := filepath.Join(constants.SystemLibexecPath, id)

if err := os.MkdirAll(rootfsPath, 0o700); err != nil {
if err := os.MkdirAll(rootfsPath, 0o711); err != nil { // rwx--x--x, non-root programs should be able to follow path
return fmt.Errorf("failed to create rootfs %q: %w", rootfsPath, err)
}

executablePath := filepath.Join(rootfsPath, id)

if err := ioutil.WriteFile(executablePath, nil, 0o500); err != nil {
if err := ioutil.WriteFile(executablePath, nil, 0o555); err != nil { // r-xr-xr-x, non-root programs should be able to execute & read
return fmt.Errorf("failed to create empty executable %q: %w", executablePath, err)
}

Expand Down
6 changes: 3 additions & 3 deletions internal/pkg/mount/pseudo.go
Expand Up @@ -14,9 +14,9 @@ func PseudoMountPoints() (mountpoints *Points, err error) {
pseudo.Set("dev", NewMountPoint("devtmpfs", "/dev", "devtmpfs", unix.MS_NOSUID, "mode=0755"))
pseudo.Set("proc", NewMountPoint("proc", "/proc", "proc", unix.MS_NOSUID|unix.MS_NOEXEC|unix.MS_NODEV, ""))
pseudo.Set("sys", NewMountPoint("sysfs", "/sys", "sysfs", 0, ""))
pseudo.Set("run", NewMountPoint("tmpfs", "/run", "tmpfs", 0, ""))
pseudo.Set("system", NewMountPoint("tmpfs", "/system", "tmpfs", 0, ""))
pseudo.Set("tmp", NewMountPoint("tmpfs", "/tmp", "tmpfs", 0, ""))
pseudo.Set("run", NewMountPoint("tmpfs", "/run", "tmpfs", 0, "mode=755"))
pseudo.Set("system", NewMountPoint("tmpfs", "/system", "tmpfs", 0, "mode=755"))
pseudo.Set("tmp", NewMountPoint("tmpfs", "/tmp", "tmpfs", 0, "mode=755"))

return pseudo, nil
}
Expand Down
6 changes: 6 additions & 0 deletions pkg/machinery/constants/constants.go
Expand Up @@ -305,9 +305,15 @@ const (
// ApidPort is the port for the apid service.
ApidPort = 50000

// ApidUserID is the user ID for apid.
ApidUserID = 50

// TrustdPort is the port for the trustd service.
TrustdPort = 50001

// TrustdUserID is the user ID for trustd.
TrustdUserID = 51

// DefaultContainerdVersion is the default container runtime version.
DefaultContainerdVersion = "1.5.5"

Expand Down

0 comments on commit 33d1c3e

Please sign in to comment.