diff --git a/cmd/oci-runtime-tool/generate.go b/cmd/oci-runtime-tool/generate.go index 0936da76..f77dbe9b 100644 --- a/cmd/oci-runtime-tool/generate.go +++ b/cmd/oci-runtime-tool/generate.go @@ -5,6 +5,7 @@ import ( "bytes" "encoding/json" "fmt" + "net" "os" "runtime" "strconv" @@ -23,6 +24,30 @@ var generateFlags = []cli.Flag{ cli.StringFlag{Name: "domainname", Usage: "domainname value for the container"}, cli.StringSliceFlag{Name: "env", Usage: "add environment variable e.g. key=value"}, cli.StringSliceFlag{Name: "env-file", Usage: "read in a file of environment variables"}, + cli.StringSliceFlag{Name: "freebsd-device-add", Usage: "add a device which must be made available in the container"}, + cli.StringSliceFlag{Name: "freebsd-device-remove", Usage: "remove a device which must be made available in the container"}, + cli.BoolFlag{Name: "freebsd-jail-allow-chflags", Usage: "allow chflags(2) inside the jail"}, + cli.BoolFlag{Name: "freebsd-jail-allow-mlock", Usage: "allow using mlock(2) inside the jail"}, + cli.StringSliceFlag{Name: "freebsd-jail-allow-mount", Usage: "allow mounting filesystems inside the jail"}, + cli.BoolFlag{Name: "freebsd-jail-allow-quotas", Usage: "allow administering quotas inside the jail"}, + cli.BoolFlag{Name: "freebsd-jail-allow-raw-sockets", Usage: "allow using raw sockets inside the jail"}, + cli.BoolFlag{Name: "freebsd-jail-allow-reserved-ports", Usage: "allow using reserved IP ports inside the jail"}, + cli.BoolFlag{Name: "freebsd-jail-allow-set-hostname", Usage: "allow setting hostname inside the jail"}, + cli.BoolFlag{Name: "freebsd-jail-allow-socket-af", Usage: "allow using non-IP protocols inside the jail"}, + cli.BoolFlag{Name: "freebsd-jail-allow-suser", Usage: "allow super-user inside the jail"}, + cli.IntFlag{Name: "freebsd-jail-enforce-statfs", Usage: "specifies the visibility of mounts in the jail"}, + cli.StringFlag{Name: "freebsd-jail-host", Usage: "specifies the jail's host namespace"}, + cli.StringFlag{Name: "freebsd-jail-interface", Usage: "specifies the network interface to use for ip4 and ip6 addresses"}, + cli.StringFlag{Name: "freebsd-jail-ip4", Usage: "specifies the jail's ip4 namespace"}, + cli.StringSliceFlag{Name: "freebsd-jail-ip4-addr", Usage: "specifies the jail's ip4 addresses"}, + cli.StringFlag{Name: "freebsd-jail-ip6", Usage: "specifies the jail's ip6 namespace"}, + cli.StringSliceFlag{Name: "freebsd-jail-ip6-addr", Usage: "specifies the jail's ip6 addresses"}, + cli.StringFlag{Name: "freebsd-jail-parent", Usage: "set the name of a parent jail which can share namespaces with this container"}, + cli.StringFlag{Name: "freebsd-jail-sysvmsg", Usage: "specifies the jail's SYSV IPC message namespace"}, + cli.StringFlag{Name: "freebsd-jail-sysvsem", Usage: "specifies the jail's SYSV IPC semaphore namespace"}, + cli.StringFlag{Name: "freebsd-jail-sysvshm", Usage: "specifies the jail's SYSV IPC shared memory namespace"}, + cli.StringFlag{Name: "freebsd-jail-vnet", Usage: "specifies the jail's vnet namespace"}, + cli.StringSliceFlag{Name: "freebsd-jail-vnet-interface", Usage: "specifies interfaces to move into the jail's vnet"}, cli.StringSliceFlag{Name: "hooks-poststart-add", Usage: "set command to run in poststart hooks"}, cli.BoolFlag{Name: "hooks-poststart-remove-all", Usage: "remove all poststart hooks"}, cli.StringSliceFlag{Name: "hooks-poststop-add", Usage: "set command to run in poststop hooks"}, @@ -1017,6 +1042,134 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { g.SetWindowsServicing(context.Bool("windows-servicing")) } + if context.IsSet("freebsd-device-add") { + devices := context.StringSlice("freebsd-device-add") + for _, deviceArg := range devices { + dev, err := parseFreeBSDDevice(deviceArg) + if err != nil { + return err + } + g.AddFreeBSDDevice(dev) + } + } + + if context.IsSet("freebsd-device-remove") { + devices := context.StringSlice("freebsd-device-remove") + for _, device := range devices { + g.RemoveFreeBSDDevice(device) + } + } + if context.IsSet("freebsd-jail-parent") { + g.SetFreeBSDJailParent(context.String("freebsd-jail-parent")) + } + if context.IsSet("freebsd-jail-host") { + sharing, err := parseFreeBSDSharing(context.String("freebsd-jail-host"), false) + if err != nil { + return err + } + g.SetFreeBSDJailHost(sharing) + } + if context.IsSet("freebsd-jail-ip4") { + sharing, err := parseFreeBSDSharing(context.String("freebsd-jail-ip4"), true) + if err != nil { + return err + } + g.SetFreeBSDJailIP4(sharing) + } + if context.IsSet("freebsd-jail-ip4-addr") { + addrs := context.StringSlice("freebsd-jail-ip4-addr") + for _, addr := range addrs { + if !validFreeBSDIP4Addr(addr) { + return fmt.Errorf("invalid IPv4 address: %s", addr) + } + } + g.SetFreeBSDJailIP4Addr(addrs) + } + if context.IsSet("freebsd-jail-ip6") { + sharing, err := parseFreeBSDSharing(context.String("freebsd-jail-ip6"), true) + if err != nil { + return err + } + g.SetFreeBSDJailIP6(sharing) + } + if context.IsSet("freebsd-jail-ip6-addr") { + addrs := context.StringSlice("freebsd-jail-ip6-addr") + for _, addr := range addrs { + if !validFreeBSDIP6Addr(addr) { + return fmt.Errorf("invalid IPv6 address: %s", addr) + } + } + g.SetFreeBSDJailIP6Addr(addrs) + } + if context.IsSet("freebsd-jail-vnet") { + sharing, err := parseFreeBSDSharing(context.String("freebsd-jail-vnet"), false) + if err != nil { + return err + } + g.SetFreeBSDJailVnet(sharing) + } + if context.IsSet("freebsd-jail-interface") { + g.SetFreeBSDJailInterface(context.String("freebsd-jail-interface")) + } + if context.IsSet("freebsd-jail-vnet-interface") { + g.SetFreeBSDJailVnetInterfaces(context.StringSlice("freebsd-jail-vnet-interface")) + } + if context.IsSet("freebsd-jail-sysvmsg") { + sharing, err := parseFreeBSDSharing(context.String("freebsd-jail-sysvmsg"), true) + if err != nil { + return err + } + g.SetFreeBSDJailSysVMsg(sharing) + } + if context.IsSet("freebsd-jail-sysvsem") { + sharing, err := parseFreeBSDSharing(context.String("freebsd-jail-sysvsem"), true) + if err != nil { + return err + } + g.SetFreeBSDJailSysVSem(sharing) + } + if context.IsSet("freebsd-jail-sysvshm") { + sharing, err := parseFreeBSDSharing(context.String("freebsd-jail-sysvshm"), true) + if err != nil { + return err + } + g.SetFreeBSDJailSysVShm(sharing) + } + if context.IsSet("freebsd-jail-enforce-statfs") { + val := context.Int("freebsd-jail-enforce-statfs") + if val < 0 || val > 2 { + return fmt.Errorf("invalid enforce-statfs value: %d", val) + } + g.SetFreeBSDJailEnforceStatfs(val) + } + if context.IsSet("freebsd-jail-allow-set-hostname") { + g.SetFreeBSDJailAllowSetHostname(context.Bool("freebsd-jail-allow-set-hostname")) + } + if context.IsSet("freebsd-jail-allow-raw-sockets") { + g.SetFreeBSDJailAllowRawSockets(context.Bool("freebsd-jail-allow-raw-sockets")) + } + if context.IsSet("freebsd-jail-allow-reserved-ports") { + g.SetFreeBSDJailAllowReservedPorts(context.Bool("freebsd-jail-allow-reserved-ports")) + } + if context.IsSet("freebsd-jail-allow-chflags") { + g.SetFreeBSDJailAllowChflags(context.Bool("freebsd-jail-allow-chflags")) + } + if context.IsSet("freebsd-jail-allow-quotas") { + g.SetFreeBSDJailAllowQuotas(context.Bool("freebsd-jail-allow-quotas")) + } + if context.IsSet("freebsd-jail-allow-socket-af") { + g.SetFreeBSDJailAllowSocketAf(context.Bool("freebsd-jail-allow-socket-af")) + } + if context.IsSet("freebsd-jail-allow-mlock") { + g.SetFreeBSDJailAllowMlock(context.Bool("freebsd-jail-allow-mlock")) + } + if context.IsSet("freebsd-jail-allow-suser") { + g.SetFreeBSDJailAllowSuser(context.Bool("freebsd-jail-allow-suser")) + } + if context.IsSet("freebsd-jail-allow-mount") { + g.SetFreeBSDJailAllowMount(context.StringSlice("freebsd-jail-allow-mount")) + } + err := addSeccomp(context, g) return err } @@ -1565,3 +1718,54 @@ func parseThrottleDevice(throttleDevice string) (int64, int64, int64, error) { return int64(major), int64(minor), int64(rate), nil } + +// parseFreeBSDDevice takes the raw string passed with the --freebsd-device-add flag +func parseFreeBSDDevice(device string) (rspec.FreeBSDDevice, error) { + dev := rspec.FreeBSDDevice{} + argsParts := strings.Split(device, ":") + if len(argsParts) < 2 { + return dev, fmt.Errorf("Incomplete device arguments: %s", device) + } + if len(argsParts) > 2 { + return dev, fmt.Errorf("Too many device arguments: %s", device) + } + dev.Path = argsParts[0] + i, err := strconv.ParseInt(argsParts[1], 10, 32) + if err != nil { + return dev, err + } + mode := os.FileMode(i) + dev.Mode = &mode + + return dev, nil +} + +// parseFreeBSDSharing takes a string representing a namespace sharing mode +func parseFreeBSDSharing(sharing string, allowDisable bool) (rspec.FreeBSDSharing, error) { + if sharing == "inherit" { + return rspec.FreeBSDShareInherit, nil + } + if sharing == "new" { + return rspec.FreeBSDShareNew, nil + } + if allowDisable && sharing == "disable" { + return rspec.FreeBSDShareDisable, nil + } + return "", fmt.Errorf("Invalid sharing mode: %s", sharing) +} + +func validFreeBSDIP4Addr(addr string) bool { + ip := net.ParseIP(addr) + if ip == nil || ip.To4() == nil { + return false + } + return true +} + +func validFreeBSDIP6Addr(addr string) bool { + ip := net.ParseIP(addr) + if ip == nil || ip.To4() != nil { + return false + } + return true +} diff --git a/generate/config.go b/generate/config.go index 48f281d2..bb91e8b4 100644 --- a/generate/config.go +++ b/generate/config.go @@ -192,3 +192,24 @@ func (g *Generator) initConfigVM() { g.Config.VM = &rspec.VM{} } } + +func (g *Generator) initConfigFreeBSD() { + g.initConfig() + if g.Config.FreeBSD == nil { + g.Config.FreeBSD = &rspec.FreeBSD{} + } +} + +func (g *Generator) initConfigFreeBSDJail() { + g.initConfigFreeBSD() + if g.Config.FreeBSD.Jail == nil { + g.Config.FreeBSD.Jail = &rspec.FreeBSDJail{} + } +} + +func (g *Generator) initConfigFreeBSDJailAllow() { + g.initConfigFreeBSDJail() + if g.Config.FreeBSD.Jail.Allow == nil { + g.Config.FreeBSD.Jail.Allow = &rspec.FreeBSDJailAllow{} + } +} diff --git a/generate/generate.go b/generate/generate.go index 44c199e1..32808f39 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -260,6 +260,7 @@ func New(os string) (generator Generator, err error) { Options: []string{}, }, } + config.FreeBSD = &rspec.FreeBSD{} } envCache := map[string]int{} @@ -1879,3 +1880,146 @@ func (g *Generator) SetWindowsServicing(servicing bool) { g.initConfigWindows() g.Config.Windows.Servicing = servicing } + +// AddFreeBSDDevice - add a device into g.Config.FreeBSD.Devices +func (g *Generator) AddFreeBSDDevice(device rspec.FreeBSDDevice) { + g.initConfigFreeBSD() + + for i, dev := range g.Config.FreeBSD.Devices { + if dev.Path == device.Path { + g.Config.FreeBSD.Devices[i] = device + return + } + } + + g.Config.FreeBSD.Devices = append(g.Config.FreeBSD.Devices, device) +} + +// RemoveFreeBSDDevice remove a device from g.Config.Linux.Devices +func (g *Generator) RemoveFreeBSDDevice(path string) { + if g.Config == nil || g.Config.FreeBSD == nil || g.Config.FreeBSD.Devices == nil { + return + } + + for i, device := range g.Config.FreeBSD.Devices { + if device.Path == path { + g.Config.FreeBSD.Devices = append(g.Config.FreeBSD.Devices[:i], g.Config.FreeBSD.Devices[i+1:]...) + return + } + } +} + +func (g *Generator) SetFreeBSDJailParent(id string) error { + g.initConfigFreeBSDJail() + p := g.Config.FreeBSD.Jail.Parent + if p != "" && p != id { + return fmt.Errorf("Jail parent is already set to %s", p) + } + g.Config.FreeBSD.Jail.Parent = id + return nil +} + +func (g *Generator) SetFreeBSDJailHost(val rspec.FreeBSDSharing) { + g.initConfigFreeBSDJail() + g.Config.FreeBSD.Jail.Host = val +} + +func (g *Generator) SetFreeBSDJailIP4(val rspec.FreeBSDSharing) { + g.initConfigFreeBSDJail() + g.Config.FreeBSD.Jail.Ip4 = val +} + +func (g *Generator) SetFreeBSDJailIP4Addr(val []string) { + g.initConfigFreeBSDJail() + g.Config.FreeBSD.Jail.Ip4Addr = val +} + +func (g *Generator) SetFreeBSDJailIP6(val rspec.FreeBSDSharing) { + g.initConfigFreeBSDJail() + g.Config.FreeBSD.Jail.Ip6 = val +} + +func (g *Generator) SetFreeBSDJailIP6Addr(val []string) { + g.initConfigFreeBSDJail() + g.Config.FreeBSD.Jail.Ip6Addr = val +} + +func (g *Generator) SetFreeBSDJailVnet(val rspec.FreeBSDSharing) { + g.initConfigFreeBSDJail() + g.Config.FreeBSD.Jail.Vnet = val +} + +func (g *Generator) SetFreeBSDJailInterface(val string) { + g.initConfigFreeBSDJail() + g.Config.FreeBSD.Jail.Interface = val +} + +func (g *Generator) SetFreeBSDJailVnetInterfaces(val []string) { + g.initConfigFreeBSDJail() + g.Config.FreeBSD.Jail.VnetInterfaces = val +} + +func (g *Generator) SetFreeBSDJailSysVMsg(val rspec.FreeBSDSharing) { + g.initConfigFreeBSDJail() + g.Config.FreeBSD.Jail.SysVMsg = val +} + +func (g *Generator) SetFreeBSDJailSysVSem(val rspec.FreeBSDSharing) { + g.initConfigFreeBSDJail() + g.Config.FreeBSD.Jail.SysVSem = val +} + +func (g *Generator) SetFreeBSDJailSysVShm(val rspec.FreeBSDSharing) { + g.initConfigFreeBSDJail() + g.Config.FreeBSD.Jail.SysVShm = val +} + +func (g *Generator) SetFreeBSDJailEnforceStatfs(val int) { + g.initConfigFreeBSDJail() + g.Config.FreeBSD.Jail.EnforceStatfs = &val +} + +func (g *Generator) SetFreeBSDJailAllowSetHostname(val bool) { + g.initConfigFreeBSDJailAllow() + g.Config.FreeBSD.Jail.Allow.SetHostname = val +} + +func (g *Generator) SetFreeBSDJailAllowRawSockets(val bool) { + g.initConfigFreeBSDJailAllow() + g.Config.FreeBSD.Jail.Allow.RawSockets = val +} + +func (g *Generator) SetFreeBSDJailAllowChflags(val bool) { + g.initConfigFreeBSDJailAllow() + g.Config.FreeBSD.Jail.Allow.Chflags = val +} + +func (g *Generator) SetFreeBSDJailAllowQuotas(val bool) { + g.initConfigFreeBSDJailAllow() + g.Config.FreeBSD.Jail.Allow.Quotas = val +} + +func (g *Generator) SetFreeBSDJailAllowSocketAf(val bool) { + g.initConfigFreeBSDJailAllow() + g.Config.FreeBSD.Jail.Allow.SocketAf = val +} + +func (g *Generator) SetFreeBSDJailAllowMlock(val bool) { + g.initConfigFreeBSDJailAllow() + g.Config.FreeBSD.Jail.Allow.Mlock = val +} + +func (g *Generator) SetFreeBSDJailAllowReservedPorts(val bool) { + g.initConfigFreeBSDJailAllow() + g.Config.FreeBSD.Jail.Allow.ReservedPorts = val +} + +func (g *Generator) SetFreeBSDJailAllowSuser(val bool) { + g.initConfigFreeBSDJailAllow() + g.Config.FreeBSD.Jail.Allow.Suser = val +} + +func (g *Generator) SetFreeBSDJailAllowMount(val []string) { + g.initConfigFreeBSDJailAllow() + g.Config.FreeBSD.Jail.Allow.Mount = val +} diff --git a/man/oci-runtime-tool-generate.1.md b/man/oci-runtime-tool-generate.1.md index 56663679..7ddfa64b 100644 --- a/man/oci-runtime-tool-generate.1.md +++ b/man/oci-runtime-tool-generate.1.md @@ -39,6 +39,108 @@ read the configuration from `config.json`. When specified multiple times, files are loaded in order with duplicate keys overwriting previous ones. +**--freebsd-device-add**=*PATH:MODE* + Add a device file in container. e.g. --freebsd-device-add=/dev/cuau0c/fuse:438 + *PATH* is the device path. + *MODE* is the file mode of the device file. + This option can be specified multiple times. + +**--freebsd-device-remove**=*PATH* + Remove a device file in container. + This option can be specified multiple times. + +**--freebsd-jail-allow-chflags** + Allow using the *chroot* syscall inside the container. + +**--freebsd-jail-allow-mlock** + Allow using the *mlock* and *munlock* syscalls inside the container. + +**--freebsd-jail-allow-mount**=*FSTYPE* + Allow mounting filesystems of type *FSTYPE* inside the container. + This option can be specified multiple times. + +**--freebsd-jail-allow-quotas** + Allow administering quotas inside the container. + +**--freebsd-jail-allow-raw-sockets** + Allow using raw sockets inside the container. + +**--freebsd-jail-allow-reserved-ports** + Allow using reserved IP ports inside the container. + +**--freebsd-jail-allow-set-hostname** + Allow setting hostname inside the container. + +**--freebsd-jail-allow-socket-af** + Allow using non-IP network protocols inside the container. + +**--freebsd-jail-allow-suser** + Allow super-user inside the container. + +**--freebsd-jail-enforce-statfs**=*VALUE* + Specifies the visibility of mounts in the container. The value should be one of: + *0* all mount points on the host are visible. + *1* only mount points below the container's root are visible. + *2* syscalls are limited to operate only on a mount-point where the container's root directory is located. + +**--freebsd-jail-host**=*SHARING* + Specify the jail's host namespace. The value of *SHARING* should be one of: + *new* to create a new namespace for the container. + *inherit* to share the namespace from the host (or parent jail if specified). + +**--freebsd-jail-interface**=*INTERFACE* + Specifies a network interface to use for IPv4 and IPv6 addresses when not using *VNET*. + +**--freebsd-jail-ip4**=*SHARING* + Specify the jail's IPv4 namespace. The value of *SHARING* should be one of: + *new* to create a new namespace for the container. + *inherit* to share the host namespace from the host (or parent jail if specified). + *disable* to stop the container from using IPv4. + +**--freebsd-jail-ip4-addr**=*ADDRESS* + Specifies an IPv4 address that is available for the container. + This option can be specified multiple times. + +**--freebsd-jail-ip6**=*SHARING* + Specify the jail's IPv6 namespace. The value of *SHARING* should be one of: + *new* to create a new namespace for the container. + *inherit* to share the host namespace from the host (or parent jail if specified). + *disable* to stop the container from using IPv6. + +**--freebsd-jail-ip6-addr**=*ADDRESS* + Specifies an IPv6 address that is available for the container. + This option can be specified multiple times. + +**--freebsd-jail-parent**=*NAME* + Specify the name of a parent jail for this container, if required. + +**--freebsd-jail-sysvmsg**=*SHARING* + Specify the jail's SYSV IPC message namespace. The value of *SHARING* should be one of: + *new* to create a new namespace for the container. + *inherit* to share the host namespace from the host (or parent jail if specified). + *disable* to stop the container from using SYSV messages. + +**--freebsd-jail-sysvsem**=*SHARING* + Specify the jail's SYSV IPC semaphore namespace. The value of *SHARING* should be one of: + *new* to create a new namespace for the container. + *inherit* to share the host namespace from the host (or parent jail if specified). + *disable* to stop the container from using SYSV semaphores. + +**--freebsd-jail-sysvshm**=*SHARING* + Specify the jail's SYSV IPC shared memory namespace. The value of *SHARING* should be one of: + *new* to create a new namespace for the container. + *inherit* to share the host namespace from the host (or parent jail if specified). + *disable* to stop the container from using SYSV shared memory. + +**--freebsd-jail-vnet**=*SHARING* + Specify the jail's VNET namespace. The value of *SHARING* should be one of: + *new* to create a new namespace for the container. + *inherit* to share the host namespace from the host (or parent jail if specified). + +**--freebsd-jail-vnet-interface**=*INTERFACE* + Specifies an interface to move into the container's vnet during its lifetime. + This option can be specified multiple times. + **--help** Print usage statement