1*2810ac1bSKiyoung Kim// Program gowns is a small program to explore and demonstrate using 2*2810ac1bSKiyoung Kim// Go to Wrap a child in a NameSpace under Linux. 3*2810ac1bSKiyoung Kim// 4*2810ac1bSKiyoung Kim// Note, this program is under active development and should not be 5*2810ac1bSKiyoung Kim// considered stable. That is, it is more a worked example and may 6*2810ac1bSKiyoung Kim// change command line arguments and behavior from release to release. 7*2810ac1bSKiyoung Kim// Should it become stable, I'll remove this comment. 8*2810ac1bSKiyoung Kimpackage main 9*2810ac1bSKiyoung Kim 10*2810ac1bSKiyoung Kimimport ( 11*2810ac1bSKiyoung Kim "errors" 12*2810ac1bSKiyoung Kim "flag" 13*2810ac1bSKiyoung Kim "fmt" 14*2810ac1bSKiyoung Kim "log" 15*2810ac1bSKiyoung Kim "os" 16*2810ac1bSKiyoung Kim "strings" 17*2810ac1bSKiyoung Kim "syscall" 18*2810ac1bSKiyoung Kim 19*2810ac1bSKiyoung Kim "kernel.org/pub/linux/libs/security/libcap/cap" 20*2810ac1bSKiyoung Kim) 21*2810ac1bSKiyoung Kim 22*2810ac1bSKiyoung Kim// nsDetail is how we summarize the type of namespace we want to 23*2810ac1bSKiyoung Kim// enter. 24*2810ac1bSKiyoung Kimtype nsDetail struct { 25*2810ac1bSKiyoung Kim // uid holds the uid for the base user in this namespace (defaults to getuid). 26*2810ac1bSKiyoung Kim uid int 27*2810ac1bSKiyoung Kim 28*2810ac1bSKiyoung Kim // uidMap holds the namespace mapping of uid values. 29*2810ac1bSKiyoung Kim uidMap []syscall.SysProcIDMap 30*2810ac1bSKiyoung Kim 31*2810ac1bSKiyoung Kim // gid holds the gid for the base user in this namespace (defaults to getgid). 32*2810ac1bSKiyoung Kim gid int 33*2810ac1bSKiyoung Kim 34*2810ac1bSKiyoung Kim // uidMap holds the namespace mapping of gid values. 35*2810ac1bSKiyoung Kim gidMap []syscall.SysProcIDMap 36*2810ac1bSKiyoung Kim} 37*2810ac1bSKiyoung Kim 38*2810ac1bSKiyoung Kimvar ( 39*2810ac1bSKiyoung Kim baseID = flag.Int("base", -1, "base id for uids and gids (-1 = invoker's uid)") 40*2810ac1bSKiyoung Kim uid = flag.Int("uid", -1, "uid of the hosting user") 41*2810ac1bSKiyoung Kim gid = flag.Int("gid", -1, "gid of the hosting user") 42*2810ac1bSKiyoung Kim iab = flag.String("iab", "", "IAB string for inheritable capabilities") 43*2810ac1bSKiyoung Kim mode = flag.String("mode", "", "force a libcap mode (capsh --modes for list)") 44*2810ac1bSKiyoung Kim 45*2810ac1bSKiyoung Kim ns = flag.Bool("ns", false, "enable user namespace features") 46*2810ac1bSKiyoung Kim uids = flag.String("uids", "", "comma separated UID ranges to map contiguously (req. CAP_SETUID)") 47*2810ac1bSKiyoung Kim gids = flag.String("gids", "", "comma separated GID ranges to map contiguously (req. CAP_SETGID)") 48*2810ac1bSKiyoung Kim 49*2810ac1bSKiyoung Kim shell = flag.String("shell", "/bin/bash", "shell to be launched") 50*2810ac1bSKiyoung Kim debug = flag.Bool("verbose", false, "more verbose output") 51*2810ac1bSKiyoung Kim) 52*2810ac1bSKiyoung Kim 53*2810ac1bSKiyoung Kim// r holds a base and count for a contiguous range. 54*2810ac1bSKiyoung Kimtype r struct { 55*2810ac1bSKiyoung Kim base, count int 56*2810ac1bSKiyoung Kim} 57*2810ac1bSKiyoung Kim 58*2810ac1bSKiyoung Kim// ranges unpacks numerical ranges. 59*2810ac1bSKiyoung Kimfunc ranges(s string) []r { 60*2810ac1bSKiyoung Kim if s == "" { 61*2810ac1bSKiyoung Kim return nil 62*2810ac1bSKiyoung Kim } 63*2810ac1bSKiyoung Kim var rs []r 64*2810ac1bSKiyoung Kim for _, n := range strings.Split(s, ",") { 65*2810ac1bSKiyoung Kim var base, upper int 66*2810ac1bSKiyoung Kim if _, err := fmt.Sscanf(n, "%d-%d", &base, &upper); err == nil { 67*2810ac1bSKiyoung Kim if upper < base { 68*2810ac1bSKiyoung Kim log.Fatalf("invalid range: [%d-%d]", base, upper) 69*2810ac1bSKiyoung Kim } 70*2810ac1bSKiyoung Kim rs = append(rs, r{ 71*2810ac1bSKiyoung Kim base: base, 72*2810ac1bSKiyoung Kim count: 1 + upper - base, 73*2810ac1bSKiyoung Kim }) 74*2810ac1bSKiyoung Kim } else if _, err := fmt.Sscanf(n, "%d", &base); err == nil { 75*2810ac1bSKiyoung Kim rs = append(rs, r{ 76*2810ac1bSKiyoung Kim base: base, 77*2810ac1bSKiyoung Kim count: 1, 78*2810ac1bSKiyoung Kim }) 79*2810ac1bSKiyoung Kim } else { 80*2810ac1bSKiyoung Kim log.Fatalf("unable to parse range [%s]", n) 81*2810ac1bSKiyoung Kim } 82*2810ac1bSKiyoung Kim } 83*2810ac1bSKiyoung Kim return rs 84*2810ac1bSKiyoung Kim} 85*2810ac1bSKiyoung Kim 86*2810ac1bSKiyoung Kim// restart launches the program again with the remaining arguments. 87*2810ac1bSKiyoung Kimfunc restart() { 88*2810ac1bSKiyoung Kim log.Fatalf("failed to restart: flags: %q %q", os.Args[0], flag.Args()[1:]) 89*2810ac1bSKiyoung Kim} 90*2810ac1bSKiyoung Kim 91*2810ac1bSKiyoung Kim// errUnableToSetup is how nsSetup fails. 92*2810ac1bSKiyoung Kimvar errUnableToSetup = errors.New("data was not in supported format") 93*2810ac1bSKiyoung Kim 94*2810ac1bSKiyoung Kim// nsSetup is the callback used to enter the namespace for the user 95*2810ac1bSKiyoung Kim// via callback in the cap.Launcher mechanism. 96*2810ac1bSKiyoung Kimfunc nsSetup(pa *syscall.ProcAttr, data interface{}) error { 97*2810ac1bSKiyoung Kim nsD, ok := data.(nsDetail) 98*2810ac1bSKiyoung Kim if !ok { 99*2810ac1bSKiyoung Kim return errUnableToSetup 100*2810ac1bSKiyoung Kim } 101*2810ac1bSKiyoung Kim 102*2810ac1bSKiyoung Kim if pa.Sys == nil { 103*2810ac1bSKiyoung Kim pa.Sys = &syscall.SysProcAttr{} 104*2810ac1bSKiyoung Kim } 105*2810ac1bSKiyoung Kim pa.Sys.Cloneflags |= syscall.CLONE_NEWUSER 106*2810ac1bSKiyoung Kim pa.Sys.UidMappings = nsD.uidMap 107*2810ac1bSKiyoung Kim pa.Sys.GidMappings = nsD.gidMap 108*2810ac1bSKiyoung Kim return nil 109*2810ac1bSKiyoung Kim} 110*2810ac1bSKiyoung Kim 111*2810ac1bSKiyoung Kimfunc parseRanges(detail *nsDetail, ids string, id int) []syscall.SysProcIDMap { 112*2810ac1bSKiyoung Kim base := *baseID 113*2810ac1bSKiyoung Kim if base < 0 { 114*2810ac1bSKiyoung Kim base = detail.uid 115*2810ac1bSKiyoung Kim } 116*2810ac1bSKiyoung Kim 117*2810ac1bSKiyoung Kim list := []syscall.SysProcIDMap{ 118*2810ac1bSKiyoung Kim syscall.SysProcIDMap{ 119*2810ac1bSKiyoung Kim ContainerID: base, 120*2810ac1bSKiyoung Kim HostID: id, 121*2810ac1bSKiyoung Kim Size: 1, 122*2810ac1bSKiyoung Kim }, 123*2810ac1bSKiyoung Kim } 124*2810ac1bSKiyoung Kim 125*2810ac1bSKiyoung Kim base++ 126*2810ac1bSKiyoung Kim for _, next := range ranges(ids) { 127*2810ac1bSKiyoung Kim list = append(list, 128*2810ac1bSKiyoung Kim syscall.SysProcIDMap{ 129*2810ac1bSKiyoung Kim ContainerID: base, 130*2810ac1bSKiyoung Kim HostID: next.base, 131*2810ac1bSKiyoung Kim Size: next.count, 132*2810ac1bSKiyoung Kim }) 133*2810ac1bSKiyoung Kim base += next.count 134*2810ac1bSKiyoung Kim } 135*2810ac1bSKiyoung Kim return list 136*2810ac1bSKiyoung Kim} 137*2810ac1bSKiyoung Kim 138*2810ac1bSKiyoung Kimfunc main() { 139*2810ac1bSKiyoung Kim flag.Parse() 140*2810ac1bSKiyoung Kim 141*2810ac1bSKiyoung Kim detail := nsDetail{ 142*2810ac1bSKiyoung Kim gid: syscall.Getgid(), 143*2810ac1bSKiyoung Kim } 144*2810ac1bSKiyoung Kim 145*2810ac1bSKiyoung Kim thisUID := syscall.Getuid() 146*2810ac1bSKiyoung Kim switch *uid { 147*2810ac1bSKiyoung Kim case -1: 148*2810ac1bSKiyoung Kim detail.uid = thisUID 149*2810ac1bSKiyoung Kim default: 150*2810ac1bSKiyoung Kim detail.uid = *uid 151*2810ac1bSKiyoung Kim } 152*2810ac1bSKiyoung Kim detail.uidMap = parseRanges(&detail, *uids, detail.uid) 153*2810ac1bSKiyoung Kim 154*2810ac1bSKiyoung Kim thisGID := syscall.Getgid() 155*2810ac1bSKiyoung Kim switch *gid { 156*2810ac1bSKiyoung Kim case -1: 157*2810ac1bSKiyoung Kim detail.gid = thisGID 158*2810ac1bSKiyoung Kim default: 159*2810ac1bSKiyoung Kim detail.gid = *gid 160*2810ac1bSKiyoung Kim } 161*2810ac1bSKiyoung Kim detail.gidMap = parseRanges(&detail, *gids, detail.gid) 162*2810ac1bSKiyoung Kim 163*2810ac1bSKiyoung Kim unparsed := flag.Args() 164*2810ac1bSKiyoung Kim 165*2810ac1bSKiyoung Kim arg0 := *shell 166*2810ac1bSKiyoung Kim skip := 0 167*2810ac1bSKiyoung Kim var w *cap.Launcher 168*2810ac1bSKiyoung Kim if len(unparsed) > 0 { 169*2810ac1bSKiyoung Kim switch unparsed[0] { 170*2810ac1bSKiyoung Kim case "==": 171*2810ac1bSKiyoung Kim arg0 = os.Args[0] 172*2810ac1bSKiyoung Kim skip++ 173*2810ac1bSKiyoung Kim } 174*2810ac1bSKiyoung Kim } 175*2810ac1bSKiyoung Kim 176*2810ac1bSKiyoung Kim w = cap.NewLauncher(arg0, append([]string{arg0}, unparsed[skip:]...), nil) 177*2810ac1bSKiyoung Kim if *ns { 178*2810ac1bSKiyoung Kim // Include the namespace setup callback with the launcher. 179*2810ac1bSKiyoung Kim w.Callback(nsSetup) 180*2810ac1bSKiyoung Kim } 181*2810ac1bSKiyoung Kim 182*2810ac1bSKiyoung Kim if thisUID != detail.uid { 183*2810ac1bSKiyoung Kim w.SetUID(detail.uid) 184*2810ac1bSKiyoung Kim } 185*2810ac1bSKiyoung Kim 186*2810ac1bSKiyoung Kim if thisGID != detail.gid { 187*2810ac1bSKiyoung Kim w.SetGroups(detail.gid, nil) 188*2810ac1bSKiyoung Kim } 189*2810ac1bSKiyoung Kim 190*2810ac1bSKiyoung Kim if *iab != "" { 191*2810ac1bSKiyoung Kim ins, err := cap.IABFromText(*iab) 192*2810ac1bSKiyoung Kim if err != nil { 193*2810ac1bSKiyoung Kim log.Fatalf("--iab=%q parsing issue: %v", err) 194*2810ac1bSKiyoung Kim } 195*2810ac1bSKiyoung Kim w.SetIAB(ins) 196*2810ac1bSKiyoung Kim } 197*2810ac1bSKiyoung Kim 198*2810ac1bSKiyoung Kim if *mode != "" { 199*2810ac1bSKiyoung Kim for m := cap.Mode(1); ; m++ { 200*2810ac1bSKiyoung Kim if s := m.String(); s == "UNKNOWN" { 201*2810ac1bSKiyoung Kim log.Fatalf("mode %q is unknown", *mode) 202*2810ac1bSKiyoung Kim } else if s == *mode { 203*2810ac1bSKiyoung Kim w.SetMode(m) 204*2810ac1bSKiyoung Kim break 205*2810ac1bSKiyoung Kim } 206*2810ac1bSKiyoung Kim } 207*2810ac1bSKiyoung Kim } 208*2810ac1bSKiyoung Kim 209*2810ac1bSKiyoung Kim // The launcher can enable more functionality if involked with 210*2810ac1bSKiyoung Kim // effective capabilities. 211*2810ac1bSKiyoung Kim have := cap.GetProc() 212*2810ac1bSKiyoung Kim for _, c := range []cap.Value{cap.SETUID, cap.SETGID} { 213*2810ac1bSKiyoung Kim if canDo, err := have.GetFlag(cap.Permitted, c); err != nil { 214*2810ac1bSKiyoung Kim log.Fatalf("failed to explore process capabilities, %q for %q", have, c) 215*2810ac1bSKiyoung Kim } else if canDo { 216*2810ac1bSKiyoung Kim if err := have.SetFlag(cap.Effective, true, c); err != nil { 217*2810ac1bSKiyoung Kim log.Fatalf("failed to raise effective capability: \"%v e+%v\"", have, c) 218*2810ac1bSKiyoung Kim } 219*2810ac1bSKiyoung Kim } 220*2810ac1bSKiyoung Kim } 221*2810ac1bSKiyoung Kim if err := have.SetProc(); err != nil { 222*2810ac1bSKiyoung Kim log.Fatalf("privilege assertion %q failed: %v", have, err) 223*2810ac1bSKiyoung Kim } 224*2810ac1bSKiyoung Kim 225*2810ac1bSKiyoung Kim if *debug { 226*2810ac1bSKiyoung Kim if *ns { 227*2810ac1bSKiyoung Kim fmt.Println("launching namespace") 228*2810ac1bSKiyoung Kim } else { 229*2810ac1bSKiyoung Kim fmt.Println("launching without namespace") 230*2810ac1bSKiyoung Kim } 231*2810ac1bSKiyoung Kim } 232*2810ac1bSKiyoung Kim 233*2810ac1bSKiyoung Kim pid, err := w.Launch(detail) 234*2810ac1bSKiyoung Kim if err != nil { 235*2810ac1bSKiyoung Kim log.Fatalf("launch failed: %v", err) 236*2810ac1bSKiyoung Kim } 237*2810ac1bSKiyoung Kim if err := cap.NewSet().SetProc(); err != nil { 238*2810ac1bSKiyoung Kim log.Fatalf("gowns could not drop privilege: %v", err) 239*2810ac1bSKiyoung Kim } 240*2810ac1bSKiyoung Kim 241*2810ac1bSKiyoung Kim p, err := os.FindProcess(pid) 242*2810ac1bSKiyoung Kim if err != nil { 243*2810ac1bSKiyoung Kim log.Fatalf("cannot find process: %v", err) 244*2810ac1bSKiyoung Kim } 245*2810ac1bSKiyoung Kim state, err := p.Wait() 246*2810ac1bSKiyoung Kim if err != nil { 247*2810ac1bSKiyoung Kim log.Fatalf("waiting failed: %v", err) 248*2810ac1bSKiyoung Kim } 249*2810ac1bSKiyoung Kim 250*2810ac1bSKiyoung Kim if *debug { 251*2810ac1bSKiyoung Kim fmt.Println("process exited:", state) 252*2810ac1bSKiyoung Kim } 253*2810ac1bSKiyoung Kim} 254