@@ -15,10 +15,12 @@ import (
1515 "syscall"
1616 "time"
1717
18+ "github.com/containers/libpod/pkg/lookup"
1819 "github.com/containers/libpod/pkg/rootless"
1920 "github.com/containers/libpod/pkg/util"
2021 "github.com/coreos/go-systemd/activation"
2122 "github.com/cri-o/ocicni/pkg/ocicni"
23+ "github.com/docker/docker/daemon/caps"
2224 spec "github.com/opencontainers/runtime-spec/specs-go"
2325 "github.com/opencontainers/selinux/go-selinux"
2426 "github.com/opencontainers/selinux/go-selinux/label"
@@ -735,18 +737,23 @@ func (r *OCIRuntime) unpauseContainer(ctr *Container) error {
735737// TODO: Add --detach support
736738// TODO: Convert to use conmon
737739// TODO: add --pid-file and use that to generate exec session tracking
738- func (r * OCIRuntime ) execContainer (c * Container , cmd , capAdd , env []string , tty bool , cwd , user , sessionID string ) (* exec.Cmd , error ) {
740+ func (r * OCIRuntime ) execContainer (c * Container , cmd , env []string , tty bool , cwd , user , sessionID string , privileged bool ) (* exec.Cmd , string , error ) {
739741 if len (cmd ) == 0 {
740- return nil , errors .Wrapf (ErrInvalidArg , "must provide a command to execute" )
742+ return nil , "" , errors .Wrapf (ErrInvalidArg , "must provide a command to execute" )
741743 }
742744
743745 if sessionID == "" {
744- return nil , errors .Wrapf (ErrEmptyID , "must provide a session ID for exec" )
746+ return nil , "" , errors .Wrapf (ErrEmptyID , "must provide a session ID for exec" )
745747 }
746748
747749 runtimeDir , err := util .GetRootlessRuntimeDir ()
748750 if err != nil {
749- return nil , err
751+ return nil , "" , err
752+ }
753+
754+ processFile , err := prepareProcessExec (c , cmd , env , tty , cwd , user , sessionID , privileged )
755+ if err != nil {
756+ return nil , "" , err
750757 }
751758
752759 args := []string {}
@@ -756,34 +763,14 @@ func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty
756763
757764 args = append (args , "exec" )
758765
759- if cwd != "" {
760- args = append (args , "--cwd" , cwd )
761- }
766+ args = append (args , "--process" , processFile )
762767
763768 args = append (args , "--pid-file" , c .execPidPath (sessionID ))
764769
765- if tty {
766- args = append (args , "--tty" )
767- } else {
768- args = append (args , "--tty=false" )
769- }
770-
771- if user != "" {
772- args = append (args , "--user" , user )
773- }
774-
775770 if c .config .Spec .Process .NoNewPrivileges {
776771 args = append (args , "--no-new-privs" )
777772 }
778773
779- for _ , cap := range capAdd {
780- args = append (args , "--cap" , cap )
781- }
782-
783- for _ , envVar := range env {
784- args = append (args , "--env" , envVar )
785- }
786-
787774 // Append container ID and command
788775 args = append (args , c .ID ())
789776 args = append (args , cmd ... )
@@ -797,10 +784,10 @@ func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty
797784 execCmd .Env = append (execCmd .Env , fmt .Sprintf ("XDG_RUNTIME_DIR=%s" , runtimeDir ))
798785
799786 if err := execCmd .Start (); err != nil {
800- return nil , errors .Wrapf (err , "cannot start container %s" , c .ID ())
787+ return nil , "" , errors .Wrapf (err , "cannot start container %s" , c .ID ())
801788 }
802789
803- return execCmd , nil
790+ return execCmd , processFile , nil
804791}
805792
806793// execStopContainer stops all active exec sessions in a container
@@ -892,3 +879,110 @@ func (r *OCIRuntime) checkpointContainer(ctr *Container, options ContainerCheckp
892879 args = append (args , ctr .ID ())
893880 return utils .ExecCmdWithStdStreams (os .Stdin , os .Stdout , os .Stderr , nil , r .path , args ... )
894881}
882+
883+ // prepareProcessExec returns the path of the process.json used in runc exec -p.
884+ // Returns path to the created exec process file. This will need to be removed
885+ // by the caller when they're done, best effort.
886+ func prepareProcessExec (c * Container , cmd , env []string , tty bool , cwd , user , sessionID string , privileged bool ) (string , error ) {
887+ filename := filepath .Join (c .bundlePath (), fmt .Sprintf ("exec-process-%s" , sessionID ))
888+ f , err := os .OpenFile (filename , os .O_CREATE | os .O_WRONLY , 0600 )
889+ if err != nil {
890+ return "" , err
891+ }
892+ defer f .Close ()
893+
894+ pspec := c .config .Spec .Process
895+ pspec .SelinuxLabel = c .config .ProcessLabel
896+ pspec .Args = cmd
897+ // We need to default this to false else it will inherit terminal as true
898+ // from the container.
899+ pspec .Terminal = false
900+ if tty {
901+ pspec .Terminal = true
902+ }
903+ if len (env ) > 0 {
904+ pspec .Env = append (pspec .Env , env ... )
905+ }
906+
907+ if cwd != "" {
908+ pspec .Cwd = cwd
909+
910+ }
911+
912+ var addGroups []string
913+ var sgids []uint32
914+
915+ // if the user is empty, we should inherit the user that the container is currently running with
916+ if user == "" {
917+ user = c .config .User
918+ addGroups = c .config .Groups
919+ }
920+
921+ execUser , err := lookup .GetUserGroupInfo (c .state .Mountpoint , user , nil )
922+ if err != nil {
923+ return "" , err
924+ }
925+
926+ if len (addGroups ) > 0 {
927+ sgids , err = lookup .GetContainerGroups (addGroups , c .state .Mountpoint , nil )
928+ if err != nil {
929+ return "" , errors .Wrapf (err , "error looking up supplemental groups for container %s exec session %s" , c .ID (), sessionID )
930+ }
931+ }
932+
933+ // If user was set, look it up in the container to get a UID to use on
934+ // the host
935+ if user != "" || len (sgids ) > 0 {
936+ if user != "" {
937+ for _ , sgid := range execUser .Sgids {
938+ sgids = append (sgids , uint32 (sgid ))
939+ }
940+ }
941+ processUser := spec.User {
942+ UID : uint32 (execUser .Uid ),
943+ GID : uint32 (execUser .Gid ),
944+ AdditionalGids : sgids ,
945+ }
946+
947+ pspec .User = processUser
948+ }
949+
950+ allCaps := caps .GetAllCapabilities ()
951+ pspec .Capabilities .Effective = []string {}
952+ if privileged {
953+ pspec .Capabilities .Bounding = allCaps
954+ } else {
955+ pspec .Capabilities .Bounding = []string {}
956+ }
957+ pspec .Capabilities .Inheritable = pspec .Capabilities .Bounding
958+ if execUser .Uid == 0 {
959+ pspec .Capabilities .Effective = pspec .Capabilities .Bounding
960+ pspec .Capabilities .Permitted = pspec .Capabilities .Bounding
961+ pspec .Capabilities .Ambient = pspec .Capabilities .Bounding
962+ } else {
963+ pspec .Capabilities .Permitted = pspec .Capabilities .Effective
964+ pspec .Capabilities .Ambient = pspec .Capabilities .Effective
965+ }
966+
967+ hasHomeSet := false
968+ for _ , s := range pspec .Env {
969+ if strings .HasPrefix (s , "HOME=" ) {
970+ hasHomeSet = true
971+ break
972+ }
973+ }
974+ if ! hasHomeSet {
975+ pspec .Env = append (pspec .Env , fmt .Sprintf ("HOME=%s" , execUser .Home ))
976+ }
977+
978+ processJSON , err := json .Marshal (pspec )
979+ if err != nil {
980+ return "" , err
981+ }
982+
983+ if err := ioutil .WriteFile (filename , processJSON , 0644 ); err != nil {
984+ return "" , err
985+ }
986+
987+ return filename , nil
988+ }
0 commit comments