run: ensure that stdio pipes are labeled correctly

Label stdio pipes to ensure that processes we run can read through
/dev/stdin and write through the /dev/stdout and /dev/stderr links.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
This commit is contained in:
Nalin Dahyabhai 2021-11-16 10:56:04 -05:00
parent e6f732096c
commit 9b6a02f3af
12 changed files with 143 additions and 9 deletions

2
go.mod
View File

@ -26,7 +26,7 @@ require (
github.com/opencontainers/runc v1.0.2
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
github.com/opencontainers/runtime-tools v0.9.0
github.com/opencontainers/selinux v1.9.1
github.com/opencontainers/selinux v1.10.0
github.com/openshift/imagebuilder v1.2.2-0.20210415181909-87f3e48c2656
github.com/pkg/errors v0.9.1
github.com/seccomp/libseccomp-golang v0.9.2-0.20200616122406-847368b35ebf

4
go.sum
View File

@ -672,8 +672,8 @@ github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3
github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
github.com/opencontainers/selinux v1.8.4/go.mod h1:HTvjPFoGMbpQsG886e3lQwnsRWtE4TC1OF3OUvG9FAo=
github.com/opencontainers/selinux v1.8.5/go.mod h1:HTvjPFoGMbpQsG886e3lQwnsRWtE4TC1OF3OUvG9FAo=
github.com/opencontainers/selinux v1.9.1 h1:b4VPEF3O5JLZgdTDBmGepaaIbAo0GqoF6EBRq5f/g3Y=
github.com/opencontainers/selinux v1.9.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
github.com/opencontainers/selinux v1.10.0 h1:rAiKF8hTcgLI3w0DHm6i0ylVVcOrlgR1kK99DRLDhyU=
github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
github.com/openshift/imagebuilder v1.2.2-0.20210415181909-87f3e48c2656 h1:WaxyNFpmIDu4i6so9r6LVFIbSaXqsj8oitMitt86ae4=
github.com/openshift/imagebuilder v1.2.2-0.20210415181909-87f3e48c2656/go.mod h1:9aJRczxCH0mvT6XQ+5STAQaPWz7OsWcU5/mRkt8IWeo=
github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913 h1:TnbXhKzrTOyuvWrjI8W6pcoI9XPbLHFXCdN2dtUw7Rw=

View File

@ -863,6 +863,9 @@ func runUsingRuntime(isolation define.Isolation, options RunOptions, configureNe
if stdioPipe, err = runMakeStdioPipe(int(uid), int(gid)); err != nil {
return 1, err
}
if err = runLabelStdioPipes(stdioPipe, spec.Process.SelinuxLabel, spec.Linux.MountLabel); err != nil {
return 1, err
}
errorFds = []int{stdioPipe[unix.Stdout][0], stdioPipe[unix.Stderr][0]}
closeBeforeReadingErrorFds = []int{stdioPipe[unix.Stdout][1], stdioPipe[unix.Stderr][1]}
// Set stdio to our pipes.

View File

@ -3,8 +3,12 @@
package buildah
import (
"fmt"
"github.com/opencontainers/runtime-tools/generate"
selinux "github.com/opencontainers/selinux/go-selinux"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
)
func selinuxGetEnabled() bool {
@ -17,3 +21,21 @@ func setupSelinux(g *generate.Generator, processLabel, mountLabel string) {
g.SetLinuxMountLabel(mountLabel)
}
}
func runLabelStdioPipes(stdioPipe [][]int, processLabel, mountLabel string) error {
if !selinuxGetEnabled() || processLabel == "" || mountLabel == "" {
// SELinux is completely disabled, or we're not doing anything at all with labeling
return nil
}
pipeContext, err := selinux.ComputeCreateContext(processLabel, mountLabel, "fifo_file")
if err != nil {
return errors.Wrapf(err, "computing file creation context for pipes")
}
for i := range stdioPipe {
pipeFdName := fmt.Sprintf("/proc/self/fd/%d", stdioPipe[i][0])
if err := label.Relabel(pipeFdName, pipeContext, false); err != nil {
return errors.Wrapf(err, "setting file label on %q", pipeFdName)
}
}
return nil
}

View File

@ -12,3 +12,7 @@ func selinuxGetEnabled() bool {
func setupSelinux(g *generate.Generator, processLabel, mountLabel string) {
}
func runLabelStdioPipes(stdioPipe [][]int, processLabel, mountLabel string) error {
return nil
}

View File

@ -7,6 +7,10 @@ load helpers
expect_output --substring "non-directory/Dockerfile: not a directory"
}
@test "bud stdio is usable pipes" {
run_buildah build ${TESTSDIR}/bud/stdio
}
@test "bud with --dns* flags" {
_prefetch alpine
run_buildah build --dns-search=example.com --dns=223.5.5.5 --dns-option=use-vc --signature-policy ${TESTSDIR}/policy.json -f ${TESTSDIR}/bud/dns/Dockerfile ${TESTSDIR}/bud/dns

View File

@ -0,0 +1,7 @@
FROM alpine
# Will stall if this is connected to a terminal, or fail if it's not readable
RUN cat /dev/stdin
# Will fail if it's not writable
RUN echo foo > /dev/stdout
# Will fail if it's not writable
RUN echo foo > /dev/stderr

View File

@ -61,16 +61,30 @@ func ClassIndex(class string) (int, error) {
return classIndex(class)
}
// SetFileLabel sets the SELinux label for this path or returns an error.
// SetFileLabel sets the SELinux label for this path, following symlinks,
// or returns an error.
func SetFileLabel(fpath string, label string) error {
return setFileLabel(fpath, label)
}
// FileLabel returns the SELinux label for this path or returns an error.
// LsetFileLabel sets the SELinux label for this path, not following symlinks,
// or returns an error.
func LsetFileLabel(fpath string, label string) error {
return lSetFileLabel(fpath, label)
}
// FileLabel returns the SELinux label for this path, following symlinks,
// or returns an error.
func FileLabel(fpath string) (string, error) {
return fileLabel(fpath)
}
// LfileLabel returns the SELinux label for this path, not following symlinks,
// or returns an error.
func LfileLabel(fpath string) (string, error) {
return lFileLabel(fpath)
}
// SetFSCreateLabel tells the kernel what label to use for all file system objects
// created by this task.
// Set the label to an empty string to return to the default label. Calls to SetFSCreateLabel

View File

@ -316,8 +316,9 @@ func classIndex(class string) (int, error) {
return index, nil
}
// setFileLabel sets the SELinux label for this path or returns an error.
func setFileLabel(fpath string, label string) error {
// lSetFileLabel sets the SELinux label for this path, not following symlinks,
// or returns an error.
func lSetFileLabel(fpath string, label string) error {
if fpath == "" {
return ErrEmptyPath
}
@ -334,12 +335,50 @@ func setFileLabel(fpath string, label string) error {
return nil
}
// fileLabel returns the SELinux label for this path or returns an error.
// setFileLabel sets the SELinux label for this path, following symlinks,
// or returns an error.
func setFileLabel(fpath string, label string) error {
if fpath == "" {
return ErrEmptyPath
}
for {
err := unix.Setxattr(fpath, xattrNameSelinux, []byte(label), 0)
if err == nil {
break
}
if err != unix.EINTR { //nolint:errorlint // unix errors are bare
return &os.PathError{Op: "setxattr", Path: fpath, Err: err}
}
}
return nil
}
// fileLabel returns the SELinux label for this path, following symlinks,
// or returns an error.
func fileLabel(fpath string) (string, error) {
if fpath == "" {
return "", ErrEmptyPath
}
label, err := getxattr(fpath, xattrNameSelinux)
if err != nil {
return "", &os.PathError{Op: "getxattr", Path: fpath, Err: err}
}
// Trim the NUL byte at the end of the byte buffer, if present.
if len(label) > 0 && label[len(label)-1] == '\x00' {
label = label[:len(label)-1]
}
return string(label), nil
}
// lFileLabel returns the SELinux label for this path, not following symlinks,
// or returns an error.
func lFileLabel(fpath string) (string, error) {
if fpath == "" {
return "", ErrEmptyPath
}
label, err := lgetxattr(fpath, xattrNameSelinux)
if err != nil {
return "", &os.PathError{Op: "lgetxattr", Path: fpath, Err: err}

View File

@ -17,10 +17,18 @@ func setFileLabel(fpath string, label string) error {
return nil
}
func lSetFileLabel(fpath string, label string) error {
return nil
}
func fileLabel(fpath string) (string, error) {
return "", nil
}
func lFileLabel(fpath string) (string, error) {
return "", nil
}
func setFSCreateLabel(label string) error {
return nil
}

View File

@ -36,3 +36,36 @@ func doLgetxattr(path, attr string, dest []byte) (int, error) {
}
}
}
// getxattr returns a []byte slice containing the value of
// an extended attribute attr set for path.
func getxattr(path, attr string) ([]byte, error) {
// Start with a 128 length byte array
dest := make([]byte, 128)
sz, errno := dogetxattr(path, attr, dest)
for errno == unix.ERANGE { //nolint:errorlint // unix errors are bare
// Buffer too small, use zero-sized buffer to get the actual size
sz, errno = dogetxattr(path, attr, []byte{})
if errno != nil {
return nil, errno
}
dest = make([]byte, sz)
sz, errno = dogetxattr(path, attr, dest)
}
if errno != nil {
return nil, errno
}
return dest[:sz], nil
}
// dogetxattr is a wrapper that retries on EINTR
func dogetxattr(path, attr string, dest []byte) (int, error) {
for {
sz, err := unix.Getxattr(path, attr, dest)
if err != unix.EINTR { //nolint:errorlint // unix errors are bare
return sz, err
}
}
}

2
vendor/modules.txt vendored
View File

@ -407,7 +407,7 @@ github.com/opencontainers/runtime-tools/generate
github.com/opencontainers/runtime-tools/generate/seccomp
github.com/opencontainers/runtime-tools/specerror
github.com/opencontainers/runtime-tools/validate
# github.com/opencontainers/selinux v1.9.1
# github.com/opencontainers/selinux v1.10.0
github.com/opencontainers/selinux/go-selinux
github.com/opencontainers/selinux/go-selinux/label
github.com/opencontainers/selinux/pkg/pwalk