From 052732857aa163f5f9d1dd65f9936f2787a953c6 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Tue, 30 Mar 2021 06:39:49 -0400 Subject: Fix missing podman-remote build options Fix handling of SecurityOpts LabelOpts SeccompProfilePath ApparmorProfile Fix Ulimits Fixes: https://github.com/containers/podman/issues/9869 Signed-off-by: Daniel J Walsh --- pkg/api/handlers/compat/images_build.go | 202 ++++++++++++++++++++++---------- pkg/bindings/images/build.go | 32 +++++ test/system/070-build.bats | 40 +++++++ 3 files changed, 210 insertions(+), 64 deletions(-) diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go index 6dac789d6..ab92434b1 100644 --- a/pkg/api/handlers/compat/images_build.go +++ b/pkg/api/handlers/compat/images_build.go @@ -10,6 +10,7 @@ import ( "os" "path/filepath" "strconv" + "strings" "time" "github.com/containers/buildah" @@ -63,52 +64,55 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { }() query := struct { - AddHosts string `schema:"extrahosts"` - AdditionalCapabilities string `schema:"addcaps"` - Annotations string `schema:"annotations"` - BuildArgs string `schema:"buildargs"` - CacheFrom string `schema:"cachefrom"` - Compression uint64 `schema:"compression"` - ConfigureNetwork string `schema:"networkmode"` - CpuPeriod uint64 `schema:"cpuperiod"` // nolint - CpuQuota int64 `schema:"cpuquota"` // nolint - CpuSetCpus string `schema:"cpusetcpus"` // nolint - CpuShares uint64 `schema:"cpushares"` // nolint - Devices string `schema:"devices"` - Dockerfile string `schema:"dockerfile"` - DropCapabilities string `schema:"dropcaps"` - DNSServers string `schema:"dnsservers"` - DNSOptions string `schema:"dnsoptions"` - DNSSearch string `schema:"dnssearch"` - Excludes string `schema:"excludes"` - ForceRm bool `schema:"forcerm"` - From string `schema:"from"` - HTTPProxy bool `schema:"httpproxy"` - Isolation string `schema:"isolation"` - Ignore bool `schema:"ignore"` - Jobs int `schema:"jobs"` // nolint - Labels string `schema:"labels"` - Layers bool `schema:"layers"` - LogRusage bool `schema:"rusage"` - Manifest string `schema:"manifest"` - MemSwap int64 `schema:"memswap"` - Memory int64 `schema:"memory"` - NamespaceOptions string `schema:"nsoptions"` - NoCache bool `schema:"nocache"` - OutputFormat string `schema:"outputformat"` - Platform string `schema:"platform"` - Pull bool `schema:"pull"` - PullPolicy string `schema:"pullpolicy"` - Quiet bool `schema:"q"` - Registry string `schema:"registry"` - Rm bool `schema:"rm"` - //FIXME SecurityOpt in remote API is not handled - SecurityOpt string `schema:"securityopt"` - ShmSize int `schema:"shmsize"` - Squash bool `schema:"squash"` - Tag []string `schema:"t"` - Target string `schema:"target"` - Timestamp int64 `schema:"timestamp"` + AddHosts string `schema:"extrahosts"` + AdditionalCapabilities string `schema:"addcaps"` + Annotations string `schema:"annotations"` + AppArmor string `schema:"apparmor"` + BuildArgs string `schema:"buildargs"` + CacheFrom string `schema:"cachefrom"` + Compression uint64 `schema:"compression"` + ConfigureNetwork string `schema:"networkmode"` + CpuPeriod uint64 `schema:"cpuperiod"` // nolint + CpuQuota int64 `schema:"cpuquota"` // nolint + CpuSetCpus string `schema:"cpusetcpus"` // nolint + CpuShares uint64 `schema:"cpushares"` // nolint + DNSOptions string `schema:"dnsoptions"` + DNSSearch string `schema:"dnssearch"` + DNSServers string `schema:"dnsservers"` + Devices string `schema:"devices"` + Dockerfile string `schema:"dockerfile"` + DropCapabilities string `schema:"dropcaps"` + Excludes string `schema:"excludes"` + ForceRm bool `schema:"forcerm"` + From string `schema:"from"` + HTTPProxy bool `schema:"httpproxy"` + Ignore bool `schema:"ignore"` + Isolation string `schema:"isolation"` + Jobs int `schema:"jobs"` // nolint + LabelOpts string `schema:"labelopts"` + Labels string `schema:"labels"` + Layers bool `schema:"layers"` + LogRusage bool `schema:"rusage"` + Manifest string `schema:"manifest"` + MemSwap int64 `schema:"memswap"` + Memory int64 `schema:"memory"` + NamespaceOptions string `schema:"nsoptions"` + NoCache bool `schema:"nocache"` + OutputFormat string `schema:"outputformat"` + Platform string `schema:"platform"` + Pull bool `schema:"pull"` + PullPolicy string `schema:"pullpolicy"` + Quiet bool `schema:"q"` + Registry string `schema:"registry"` + Rm bool `schema:"rm"` + Seccomp string `schema:"seccomp"` + SecurityOpt string `schema:"securityopt"` + ShmSize int `schema:"shmsize"` + Squash bool `schema:"squash"` + Tag []string `schema:"t"` + Target string `schema:"target"` + Timestamp int64 `schema:"timestamp"` + Ulimits string `schema:"ulimits"` }{ Dockerfile: "Dockerfile", Registry: "docker.io", @@ -123,7 +127,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { return } - // convert label formats + // convert addcaps formats var addCaps = []string{} if _, found := r.URL.Query()["addcaps"]; found { var m = []string{} @@ -133,6 +137,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { } addCaps = m } + addhosts := []string{} if _, found := r.URL.Query()["extrahosts"]; found { if err := json.Unmarshal([]byte(query.AddHosts), &addhosts); err != nil { @@ -142,7 +147,8 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { } compression := archive.Compression(query.Compression) - // convert label formats + + // convert dropcaps formats var dropCaps = []string{} if _, found := r.URL.Query()["dropcaps"]; found { var m = []string{} @@ -153,7 +159,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { dropCaps = m } - // convert label formats + // convert devices formats var devices = []string{} if _, found := r.URL.Query()["devices"]; found { var m = []string{} @@ -233,7 +239,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { } } - // convert label formats + // convert annotations formats var annotations = []string{} if _, found := r.URL.Query()["annotations"]; found { if err := json.Unmarshal([]byte(query.Annotations), &annotations); err != nil { @@ -242,7 +248,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { } } - // convert label formats + // convert nsoptions formats nsoptions := buildah.NamespaceOptions{} if _, found := r.URL.Query()["nsoptions"]; found { if err := json.Unmarshal([]byte(query.NamespaceOptions), &nsoptions); err != nil { @@ -271,11 +277,75 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { } } } + jobs := 1 if _, found := r.URL.Query()["jobs"]; found { jobs = query.Jobs } + var ( + labelOpts = []string{} + seccomp string + apparmor string + ) + + if utils.IsLibpodRequest(r) { + seccomp = query.Seccomp + apparmor = query.AppArmor + // convert labelopts formats + if _, found := r.URL.Query()["labelopts"]; found { + var m = []string{} + if err := json.Unmarshal([]byte(query.LabelOpts), &m); err != nil { + utils.BadRequest(w, "labelopts", query.LabelOpts, err) + return + } + labelOpts = m + } + } else { + // handle security-opt + if _, found := r.URL.Query()["securityopt"]; found { + var securityOpts = []string{} + if err := json.Unmarshal([]byte(query.SecurityOpt), &securityOpts); err != nil { + utils.BadRequest(w, "securityopt", query.SecurityOpt, err) + return + } + for _, opt := range securityOpts { + if opt == "no-new-privileges" { + utils.BadRequest(w, "securityopt", query.SecurityOpt, errors.New("no-new-privileges is not supported")) + return + } + con := strings.SplitN(opt, "=", 2) + if len(con) != 2 { + utils.BadRequest(w, "securityopt", query.SecurityOpt, errors.Errorf("Invalid --security-opt name=value pair: %q", opt)) + return + } + + switch con[0] { + case "label": + labelOpts = append(labelOpts, con[1]) + case "apparmor": + apparmor = con[1] + case "seccomp": + seccomp = con[1] + default: + utils.BadRequest(w, "securityopt", query.SecurityOpt, errors.Errorf("Invalid --security-opt 2: %q", opt)) + return + } + } + } + } + + // convert ulimits formats + var ulimits = []string{} + if _, found := r.URL.Query()["ulimits"]; found { + var m = []string{} + if err := json.Unmarshal([]byte(query.Ulimits), &m); err != nil { + utils.BadRequest(w, "ulimits", query.Ulimits, err) + return + } + ulimits = m + } + pullPolicy := buildahDefine.PullIfMissing if utils.IsLibpodRequest(r) { pullPolicy = buildahDefine.PolicyMap[query.PullPolicy] @@ -320,18 +390,22 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { Annotations: annotations, Args: buildArgs, CommonBuildOpts: &buildah.CommonBuildOptions{ - AddHost: addhosts, - CPUPeriod: query.CpuPeriod, - CPUQuota: query.CpuQuota, - CPUShares: query.CpuShares, - CPUSetCPUs: query.CpuSetCpus, - DNSServers: dnsservers, - DNSOptions: dnsoptions, - DNSSearch: dnssearch, - HTTPProxy: query.HTTPProxy, - Memory: query.Memory, - MemorySwap: query.MemSwap, - ShmSize: strconv.Itoa(query.ShmSize), + AddHost: addhosts, + ApparmorProfile: apparmor, + CPUPeriod: query.CpuPeriod, + CPUQuota: query.CpuQuota, + CPUSetCPUs: query.CpuSetCpus, + CPUShares: query.CpuShares, + DNSOptions: dnsoptions, + DNSSearch: dnssearch, + DNSServers: dnsservers, + HTTPProxy: query.HTTPProxy, + LabelOpts: labelOpts, + Memory: query.Memory, + MemorySwap: query.MemSwap, + SeccompProfilePath: seccomp, + ShmSize: strconv.Itoa(query.ShmSize), + Ulimit: ulimits, }, CNIConfigDir: rtc.Network.CNIPluginDirs[0], CNIPluginPath: util.DefaultCNIPluginPath, @@ -364,11 +438,11 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { RemoveIntermediateCtrs: query.Rm, ReportWriter: reporter, Squash: query.Squash, + Target: query.Target, SystemContext: &types.SystemContext{ AuthFilePath: authfile, DockerAuthConfig: creds, }, - Target: query.Target, } if _, found := r.URL.Query()["timestamp"]; found { diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go index c79d79136..c47a16551 100644 --- a/pkg/bindings/images/build.go +++ b/pkg/bindings/images/build.go @@ -120,6 +120,9 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO if options.ForceRmIntermediateCtrs { params.Set("forcerm", "1") } + if options.RemoveIntermediateCtrs { + params.Set("rm", "1") + } if len(options.From) > 0 { params.Set("from", options.From) } @@ -140,6 +143,23 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO } params.Set("labels", l) } + + if opt := options.CommonBuildOpts.LabelOpts; len(opt) > 0 { + o, err := jsoniter.MarshalToString(opt) + if err != nil { + return nil, err + } + params.Set("labelopts", o) + } + + if len(options.CommonBuildOpts.SeccompProfilePath) > 0 { + params.Set("seccomp", options.CommonBuildOpts.SeccompProfilePath) + } + + if len(options.CommonBuildOpts.ApparmorProfile) > 0 { + params.Set("apparmor", options.CommonBuildOpts.ApparmorProfile) + } + if options.Layers { params.Set("layers", "1") } @@ -174,6 +194,7 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO if len(platform) > 0 { params.Set("platform", platform) } + params.Set("pullpolicy", options.PullPolicy.String()) if options.Quiet { @@ -182,6 +203,10 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO if options.RemoveIntermediateCtrs { params.Set("rm", "1") } + if len(options.Target) > 0 { + params.Set("target", options.Target) + } + if hosts := options.CommonBuildOpts.AddHost; len(hosts) > 0 { h, err := jsoniter.MarshalToString(hosts) if err != nil { @@ -212,6 +237,13 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO params.Set("timestamp", strconv.FormatInt(t.Unix(), 10)) } + if len(options.CommonBuildOpts.Ulimit) > 0 { + ulimitsJSON, err := json.Marshal(options.CommonBuildOpts.Ulimit) + if err != nil { + return nil, err + } + params.Set("ulimits", string(ulimitsJSON)) + } var ( headers map[string]string err error diff --git a/test/system/070-build.bats b/test/system/070-build.bats index e5b68a0d8..2e97c93e0 100644 --- a/test/system/070-build.bats +++ b/test/system/070-build.bats @@ -712,6 +712,46 @@ EOF run_podman rmi -f build_test } +@test "podman build check_label" { + skip_if_no_selinux + tmpdir=$PODMAN_TMPDIR/build-test + mkdir -p $tmpdir + tmpbuilddir=$tmpdir/build + mkdir -p $tmpbuilddir + dockerfile=$tmpbuilddir/Dockerfile + cat >$dockerfile <$dockerfile <