Skip to content

Commit

Permalink
Merge pull request #13 from rmohr/xattrs
Browse files Browse the repository at this point in the history
Add a command to bazeldnf which allow setting capabilities and selinux labels on existing tar streams
  • Loading branch information
rmohr authored Aug 11, 2021
2 parents 75fd5e0 + 9ac9982 commit eeba90f
Show file tree
Hide file tree
Showing 15 changed files with 315 additions and 33 deletions.
2 changes: 2 additions & 0 deletions cmd/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ go_library(
"sandbox.go",
"tar2files.go",
"verify.go",
"xattr.go",
],
importpath = "github.com/rmohr/bazeldnf/cmd",
visibility = ["//visibility:private"],
Expand All @@ -31,6 +32,7 @@ go_library(
"//pkg/repo",
"//pkg/rpm",
"//pkg/sat",
"//pkg/xattr",
"@com_github_sassoftware_go_rpmutils//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_spf13_cobra//:go_default_library",
Expand Down
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ var rootCmd = &cobra.Command{
}

func Execute() {
rootCmd.AddCommand(NewXATTRCmd())
rootCmd.AddCommand(NewSandboxCmd())
rootCmd.AddCommand(NewFetchCmd())
rootCmd.AddCommand(NewInitCmd())
Expand Down
14 changes: 8 additions & 6 deletions cmd/rpm2tar.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ import (
)

type rpm2tarOpts struct {
output string
input []string
symlinks map[string]string
capabilities map[string]string
output string
input []string
symlinks map[string]string
capabilities map[string]string
selinuxLabels map[string]string
}

var rpm2taropts = rpm2tarOpts{}
Expand Down Expand Up @@ -79,13 +80,13 @@ func NewRpm2TarCmd() *cobra.Command {
return fmt.Errorf("could not open rpm at %s: %v", i, err)
}
defer rpmStream.Close()
err = rpm.RPMToTar(rpmStream, tarWriter, true, cap)
err = rpm.RPMToTar(rpmStream, tarWriter, true, cap, rpm2taropts.selinuxLabels)
if err != nil {
return fmt.Errorf("could not convert rpm at %s: %v", i, err)
}
}
} else {
err := rpm.RPMToTar(rpmStream, tarWriter, false, cap)
err := rpm.RPMToTar(rpmStream, tarWriter, false, cap, rpm2taropts.selinuxLabels)
if err != nil {
return fmt.Errorf("could not convert rpm : %v", err)
}
Expand All @@ -98,6 +99,7 @@ func NewRpm2TarCmd() *cobra.Command {
rpm2tarCmd.Flags().StringArrayVarP(&rpm2taropts.input, "input", "i", []string{}, "location from where to read the rpm file (defaults to stdin)")
rpm2tarCmd.Flags().StringToStringVarP(&rpm2taropts.symlinks, "symlinks", "s", map[string]string{}, "symlinks to add. Relative or absolute.")
rpm2tarCmd.Flags().StringToStringVarP(&rpm2taropts.capabilities, "capabilities", "c", map[string]string{}, "capabilities of files (--capabilities=/bin/ls=cap_net_bind_service)")
rpm2tarCmd.Flags().StringToStringVar(&rpm2taropts.selinuxLabels, "selinux-labels", map[string]string{}, "selinux labels of files (--selinux-labels=/bin/ls=unconfined_u:object_r:default_t:s0)")
// deprecated options
rpm2tarCmd.Flags().StringToStringVar(&rpm2taropts.capabilities, "capabilties", map[string]string{}, "capabilities of files (-c=/bin/ls=cap_net_bind_service)")
rpm2tarCmd.Flags().MarkDeprecated("capabilties", "use --capabilities instead")
Expand Down
67 changes: 67 additions & 0 deletions cmd/xattr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package main

import (
"archive/tar"
"os"
"strings"

"github.com/rmohr/bazeldnf/pkg/xattr"
"github.com/spf13/cobra"
)

type xattrOpts struct {
filePrefix string
tarFileInput string
tarFileOutput string
capabilities map[string]string
selinuxLabels map[string]string
}

var xattropts = xattrOpts{}

func NewXATTRCmd() *cobra.Command {
xattrCmd := &cobra.Command{
Use: "xattr",
Short: "Modify xattrs on tar file members",
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) (err error) {
capabilityMap := map[string][]string{}
for file, caps := range xattropts.capabilities {
split := strings.Split(caps, ":")
if len(split) > 0 {
capabilityMap["./"+strings.TrimPrefix(file, "/")] = split
}
}
labelMap := map[string]string{}
for file, label := range xattropts.selinuxLabels {
labelMap["./"+strings.TrimPrefix(file, "/")] = label
}

streamInput := os.Stdin
if xattropts.tarFileInput != "" {
streamInput, err = os.Open(xattropts.tarFileInput)
if err != nil {
return err
}
defer streamInput.Close()
}

streamOutput := os.Stdout
if xattropts.tarFileOutput != "" {
streamOutput, err = os.OpenFile(xattropts.tarFileOutput, os.O_WRONLY|os.O_CREATE, os.ModePerm)
if err != nil {
return err
}
}
tarWriter := tar.NewWriter(streamOutput)
defer tarWriter.Close()
return xattr.Apply(tar.NewReader(streamInput), tarWriter , capabilityMap, labelMap)
},
}

xattrCmd.Flags().StringVarP(&xattropts.tarFileInput, "input", "i", "", "location from where to read the tar file (defaults to stdin)")
xattrCmd.Flags().StringVarP(&xattropts.tarFileOutput, "output", "o", "", "where to write the file to (defaults to stdout)")
xattrCmd.Flags().StringToStringVarP(&xattropts.capabilities, "capabilities", "c", map[string]string{}, "capabilities of files (--capabilities=/bin/ls=cap_net_bind_service)")
xattrCmd.Flags().StringToStringVar(&xattropts.selinuxLabels, "selinux-labels", map[string]string{}, "selinux labels of files (--selinux-labels=/bin/ls=unconfined_u:object_r:default_t:s0)")
return xattrCmd
}
5 changes: 5 additions & 0 deletions deps.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@ load(
"@bazeldnf//internal:rpmtree.bzl",
_tar2files = "tar2files",
)
load(
"@bazeldnf//internal:xattrs.bzl",
_xattrs = "xattrs",
)

rpm = _rpm
rpmtree = _rpmtree
tar2files = _tar2files
xattrs = _xattrs

def bazeldnf_dependencies():
_maybe(
Expand Down
9 changes: 8 additions & 1 deletion internal/rpmtree.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ def _rpm2tar_impl(ctx):
capabilities = []
for k, v in ctx.attr.capabilities.items():
capabilities += [k + "=" + ":".join(v)]
args += ["-c", ",".join(capabilities)]
args += ["--capabilities", ",".join(capabilities)]

if ctx.attr.selinux_labels:
selinux_labels = []
for k, v in ctx.attr.selinux_labels.items():
selinux_labels += [k + "=" + v]
args += ["--selinux-labels", ",".join(selinux_labels)]

args += rpms

Expand Down Expand Up @@ -71,6 +77,7 @@ _rpm2tar_attrs = {
),
"symlinks": attr.string_dict(),
"capabilities": attr.string_list_dict(),
"selinux_labels": attr.string_list_dict(),
"out": attr.output(mandatory = True),
}

Expand Down
65 changes: 65 additions & 0 deletions internal/xattrs.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Copyright 2014 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

def _xattrs_impl(ctx):
out = ctx.outputs.out
args = ["xattr", "-i", ctx.files.tar[0].path, "-o", out.path]

if ctx.attr.capabilities:
capabilities = []
for k, v in ctx.attr.capabilities.items():
capabilities += [k + "=" + ":".join(v)]
args += ["--capabilities", ",".join(capabilities)]

if ctx.attr.selinux_labels:
selinux_labels = []
for k, v in ctx.attr.selinux_labels.items():
selinux_labels += [k + "=" + v]
args += ["--selinux-labels", ",".join(selinux_labels)]

ctx.actions.run(
inputs = ctx.files.tar,
outputs = [out],
arguments = args,
progress_message = "Enriching %s with xattrs" % ctx.label.name,
executable = ctx.executable._bazeldnf,
)

return [DefaultInfo(files = depset([ctx.outputs.out]))]

_xattrs_attrs = {
"tar": attr.label(allow_single_file = True),
"_bazeldnf": attr.label(
executable = True,
cfg = "exec",
allow_files = True,
default = Label("//cmd:cmd"),
),
"capabilities": attr.string_list_dict(),
"selinux_labels": attr.string_dict(),
"out": attr.output(mandatory = True),
}

_xattrs = rule(
implementation = _xattrs_impl,
attrs = _xattrs_attrs,
)

def xattrs(**kwargs):
basename = kwargs["name"]
tarname = basename + ".tar"
_xattrs(
out = tarname,
**kwargs
)
1 change: 1 addition & 0 deletions pkg/rpm/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//pkg/api",
"//pkg/xattr",
"@com_github_sassoftware_go_rpmutils//:go_default_library",
"@com_github_sassoftware_go_rpmutils//cpio:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
Expand Down
31 changes: 9 additions & 22 deletions pkg/rpm/cpio2tar.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,12 @@ import (
"io/ioutil"
"time"

"github.com/rmohr/bazeldnf/pkg/xattr"
"github.com/sassoftware/go-rpmutils/cpio"
)

const (
capabilities_header = "SCHILY.xattr.security.capability"
)

var cap_empty_bitmask = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
var supported_capabilities = map[string][]byte{
"cap_net_bind_service": {1, 0, 0, 2, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
}

// Extract the contents of a cpio stream from and writes it as a tar file into the provided writer
func Tar(rs io.Reader, tarfile *tar.Writer, noSymlinksAndDirs bool, capabilities map[string][]string) error {
func Tar(rs io.Reader, tarfile *tar.Writer, noSymlinksAndDirs bool, capabilities map[string][]string, selinuxLabels map[string]string) error {
hardLinks := map[int][]*tar.Header{}
inodes := map[int]string{}

Expand All @@ -54,18 +46,13 @@ func Tar(rs io.Reader, tarfile *tar.Writer, noSymlinksAndDirs bool, capabilities

pax := map[string]string{}
if caps, exists := capabilities[entry.Header.Filename()]; exists {
for _, cap := range caps {
if _, supported := supported_capabilities[cap]; !supported {
return fmt.Errorf("Requested capability '%s' for file '%s' is not supported", cap, entry.Header.Filename())
}
if _, exists := pax[capabilities_header]; !exists {
pax[capabilities_header] = string(cap_empty_bitmask)
}
val := []byte(pax[capabilities_header])
for i, b := range supported_capabilities[cap] {
val[i] = val[i] | b
}
pax[capabilities_header] = string(val)
if err := xattr.AddCapabilities(pax, caps); err != nil {
return fmt.Errorf("failed setting capabilities on %s: %v", entry.Header.Filename(), err)
}
}
if label, exists := selinuxLabels[entry.Header.Filename()]; exists {
if err := xattr.SetSELinuxLabel(pax, label); err != nil {
return fmt.Errorf("failed setting selinux label on %s: %v", entry.Header.Filename(), err)
}
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/rpm/tar.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
log "github.com/sirupsen/logrus"
)

func RPMToTar(rpmReader io.Reader, tarWriter *tar.Writer, noSymlinksAndDirs bool, capabilities map[string][]string) error {
func RPMToTar(rpmReader io.Reader, tarWriter *tar.Writer, noSymlinksAndDirs bool, capabilities map[string][]string, selinuxLabels map[string]string) error {
rpm, err := rpmutils.ReadRpm(rpmReader)
if err != nil {
return fmt.Errorf("failed to read rpm: %s", err)
Expand All @@ -24,7 +24,7 @@ func RPMToTar(rpmReader io.Reader, tarWriter *tar.Writer, noSymlinksAndDirs bool
if err != nil {
return fmt.Errorf("failed to open the payload reader: %s", err)
}
return Tar(payloadReader, tarWriter, noSymlinksAndDirs, capabilities)
return Tar(payloadReader, tarWriter, noSymlinksAndDirs, capabilities, selinuxLabels)
}

func RPMToCPIO(rpmReader io.Reader) (*cpio.CpioStream, error) {
Expand Down
4 changes: 2 additions & 2 deletions pkg/rpm/tar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func TestRPMToTar(t *testing.T) {
g.Expect(err).ToNot(HaveOccurred())
defer tarWriter.Close()

err = RPMToTar(f, tar.NewWriter(tarWriter), false, nil)
err = RPMToTar(f, tar.NewWriter(tarWriter), false, nil, nil)
g.Expect(err).ToNot(HaveOccurred())
tarWriter.Close()

Expand Down Expand Up @@ -125,7 +125,7 @@ func TestTar2Files(t *testing.T) {
defer pipeWriter.Close()

go func() {
_ = RPMToTar(f, tar.NewWriter(pipeWriter), false, nil)
_ = RPMToTar(f, tar.NewWriter(pipeWriter), false, nil, nil)
pipeWriter.Close()
}()

Expand Down
16 changes: 16 additions & 0 deletions pkg/xattr/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "xattr",
srcs = ["xattr.go"],
importpath = "github.com/rmohr/bazeldnf/pkg/xattr",
visibility = ["//visibility:public"],
)

go_test(
name = "xattr_test",
srcs = ["xattr_test.go"],
data = glob(["testdata/**"]),
embed = [":xattr"],
deps = ["@com_github_onsi_gomega//:go_default_library"],
)
Binary file added pkg/xattr/testdata/xattr.tar
Binary file not shown.
Loading

0 comments on commit eeba90f

Please sign in to comment.