github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/libcontainer/dmz/dmz_linux.go (about)

     1  //go:build !runc_nodmz
     2  // +build !runc_nodmz
     3  
     4  package dmz
     5  
     6  import (
     7  	"bytes"
     8  	"debug/elf"
     9  	"embed"
    10  	"fmt"
    11  	"os"
    12  	"strconv"
    13  	"sync"
    14  
    15  	"github.com/sirupsen/logrus"
    16  )
    17  
    18  // Try to build the runc-dmz binary on "go generate". If it fails (or
    19  // libcontainer is being imported as a library), the embed.FS will not contain
    20  // the file, which will then cause us to fall back to a clone of
    21  // /proc/self/exe.
    22  //
    23  // There is an empty file called dummy-file.txt in libcontainer/dmz/binary in
    24  // order to work around the restriction that go:embed requires at least one
    25  // file to match the pattern.
    26  //
    27  //go:generate make -B binary/runc-dmz
    28  //go:embed binary
    29  var runcDmzFs embed.FS
    30  
    31  // A cached copy of the contents of runc-dmz.
    32  var (
    33  	runcDmzBinaryOnce    sync.Once
    34  	runcDmzBinaryIsValid bool
    35  	runcDmzBinary        []byte
    36  )
    37  
    38  // Binary returns a cloned copy (see CloneBinary) of a very minimal C program
    39  // that just does an execve() of its arguments. This is used in the final
    40  // execution step of the container execution as an intermediate process before
    41  // the container process is execve'd. This allows for protection against
    42  // CVE-2019-5736 without requiring a complete copy of the runc binary. Each
    43  // call to Binary will return a new copy.
    44  //
    45  // If the runc-dmz binary is not embedded into the runc binary, Binary will
    46  // return ErrNoDmzBinary as the error.
    47  func Binary(tmpDir string) (*os.File, error) {
    48  	// Only RUNC_DMZ=true enables runc_dmz.
    49  	runcDmz := os.Getenv("RUNC_DMZ")
    50  	if runcDmz == "" {
    51  		logrus.Debugf("RUNC_DMZ is not set -- switching back to classic /proc/self/exe cloning")
    52  		return nil, ErrNoDmzBinary
    53  	}
    54  	if dmzEnabled, err := strconv.ParseBool(runcDmz); err == nil && !dmzEnabled {
    55  		logrus.Debugf("RUNC_DMZ is false -- switching back to classic /proc/self/exe cloning")
    56  		return nil, ErrNoDmzBinary
    57  	} else if err != nil {
    58  		return nil, fmt.Errorf("parsing RUNC_DMZ: %w", err)
    59  	}
    60  
    61  	runcDmzBinaryOnce.Do(func() {
    62  		runcDmzBinary, _ = runcDmzFs.ReadFile("binary/runc-dmz")
    63  		// Verify that our embedded binary has a standard ELF header.
    64  		if !bytes.HasPrefix(runcDmzBinary, []byte(elf.ELFMAG)) {
    65  			if len(runcDmzBinary) != 0 {
    66  				logrus.Infof("misconfigured build: embedded runc-dmz binary is non-empty but is missing a proper ELF header")
    67  			}
    68  		} else {
    69  			runcDmzBinaryIsValid = true
    70  		}
    71  	})
    72  	if !runcDmzBinaryIsValid {
    73  		return nil, ErrNoDmzBinary
    74  	}
    75  	rdr := bytes.NewBuffer(runcDmzBinary)
    76  	return CloneBinary(rdr, int64(rdr.Len()), "runc-dmz", tmpDir)
    77  }