Skip to content

Commit e43fc30

Browse files
authored
Update declarative capabilities; add functionality to set within QEMU (#1944)
1 parent 1cbc04d commit e43fc30

File tree

3 files changed

+111
-82
lines changed

3 files changed

+111
-82
lines changed

pkg/build/build.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"archive/tar"
1919
"compress/gzip"
2020
"context"
21+
"encoding/hex"
2122
"encoding/json"
2223
"errors"
2324
"fmt"
@@ -313,6 +314,7 @@ func (b *Build) buildGuest(ctx context.Context, imgConfig apko_types.ImageConfig
313314
b.ExtraPackages = append(b.ExtraPackages, []string{
314315
"melange-microvm-init",
315316
"gnutar",
317+
"attr",
316318
}...)
317319
}
318320

@@ -990,8 +992,18 @@ func (b *Build) BuildPackage(ctx context.Context) error {
990992

991993
for path, cap := range caps {
992994
enc := config.EncodeCapability(cap.Effective, cap.Permitted, cap.Inheritable)
993-
if err := b.WorkspaceDirFS.SetXattr(path, "security.capability", enc); err != nil {
994-
log.Warnf("failed to set capabilities on %s: %v\n", path, err)
995+
fullPath := filepath.Join(melangeOutputDirName, pkg.Name, path)
996+
if b.Runner.Name() == container.QemuName {
997+
fullPath := filepath.Join(WorkDir, melangeOutputDirName, pkg.Name, path)
998+
hex := fmt.Sprintf("0x%s", hex.EncodeToString(enc))
999+
cmd := []string{"/bin/sh", "-c", fmt.Sprintf("setfattr -n security.capability -v %s %s", hex, fullPath)}
1000+
if err := b.Runner.Run(ctx, pr.config, map[string]string{}, cmd...); err != nil {
1001+
return fmt.Errorf("failed to set capabilities within VM on %s: %v\n", path, err)
1002+
}
1003+
} else {
1004+
if err := b.WorkspaceDirFS.SetXattr(fullPath, "security.capability", enc); err != nil {
1005+
log.Warnf("failed to set capabilities on %s: %v\n", path, err)
1006+
}
9951007
}
9961008
}
9971009

pkg/config/config.go

Lines changed: 42 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,6 +1291,7 @@ func replacePackage(r *strings.Replacer, commit string, in Package) Package {
12911291
CPE: in.CPE,
12921292
Timeout: in.Timeout,
12931293
Resources: in.Resources,
1294+
SetCap: in.SetCap,
12941295
}
12951296
}
12961297

@@ -1869,30 +1870,32 @@ func ParseCapabilities(caps []Capability) (map[string]capabilityData, error) {
18691870
pathCapabilities := map[string]capabilityData{}
18701871

18711872
for _, c := range caps {
1872-
for attr, data := range c.Add {
1873-
capValues := getCapabilityValue(attr)
1874-
effective, permitted, inheritable := parseCapability(data)
1875-
1876-
caps, ok := pathCapabilities[c.Path]
1877-
if !ok {
1878-
caps = struct {
1879-
Effective uint32
1880-
Permitted uint32
1881-
Inheritable uint32
1882-
}{}
1883-
}
1873+
for attrs, data := range c.Add {
1874+
for attr := range strings.SplitSeq(attrs, ",") {
1875+
capValues := getCapabilityValue(attr)
1876+
effective, permitted, inheritable := parseCapability(data)
1877+
1878+
caps, ok := pathCapabilities[c.Path]
1879+
if !ok {
1880+
caps = struct {
1881+
Effective uint32
1882+
Permitted uint32
1883+
Inheritable uint32
1884+
}{}
1885+
}
18841886

1885-
if effective {
1886-
caps.Effective |= capValues
1887-
}
1888-
if permitted {
1889-
caps.Permitted |= capValues
1890-
}
1891-
if inheritable {
1892-
caps.Inheritable |= capValues
1893-
}
1887+
if effective {
1888+
caps.Effective |= capValues
1889+
}
1890+
if permitted {
1891+
caps.Permitted |= capValues
1892+
}
1893+
if inheritable {
1894+
caps.Inheritable |= capValues
1895+
}
18941896

1895-
pathCapabilities[c.Path] = caps
1897+
pathCapabilities[c.Path] = caps
1898+
}
18961899
}
18971900
}
18981901

@@ -1916,22 +1919,23 @@ func parseCapability(capFlag string) (effective, permitted, inheritable bool) {
19161919

19171920
// EncodeCapability returns the byte slice necessary to set the final capability xattr.
19181921
func EncodeCapability(effectiveBits, permittedBits, inheritableBits uint32) []byte {
1919-
// https://github.com/torvalds/linux/blob/a33b5a08cbbdd7aadff95f40cbb45ab86841679e/include/uapi/linux/capability.h#L36
1920-
magic := uint32(0x20080522)
1921-
// Version 3; Version 2 is deprecated
1922-
version := uint32(0x3)
1923-
1924-
data := make([]byte, 20)
1925-
binary.LittleEndian.PutUint32(data[0:], magic)
1926-
binary.LittleEndian.PutUint32(data[4:], version)
1927-
binary.LittleEndian.PutUint32(data[8:], effectiveBits)
1928-
binary.LittleEndian.PutUint32(data[12:], permittedBits)
1929-
binary.LittleEndian.PutUint32(data[16:], inheritableBits)
1930-
1931-
rootid := uint32(0)
1932-
rootidBytes := make([]byte, 4)
1933-
binary.LittleEndian.PutUint32(rootidBytes, rootid)
1934-
data = append(data, rootidBytes...)
1922+
revision := uint32(0x03000000)
1923+
1924+
var flags uint32 = 0
1925+
if effectiveBits != 0 {
1926+
flags = 0x01
1927+
}
1928+
magic := revision | flags
1929+
1930+
data := make([]byte, 24)
1931+
1932+
binary.LittleEndian.PutUint32(data[0:4], magic)
1933+
binary.LittleEndian.PutUint32(data[4:8], permittedBits)
1934+
binary.LittleEndian.PutUint32(data[8:12], inheritableBits)
1935+
1936+
binary.LittleEndian.PutUint32(data[12:16], 0)
1937+
binary.LittleEndian.PutUint32(data[16:20], 0)
1938+
binary.LittleEndian.PutUint32(data[20:24], 0)
19351939

19361940
return data
19371941
}

pkg/config/config_test.go

Lines changed: 55 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package config
22

33
import (
4+
"bytes"
45
"encoding/binary"
56
"os"
67
"path/filepath"
8+
"strings"
79
"testing"
810

911
"github.com/chainguard-dev/clog/slogtest"
@@ -852,56 +854,67 @@ func TestSetCapability(t *testing.T) {
852854
t.Fatalf("Failed to collect capabilities: %v", err)
853855
}
854856

855-
for path, caps := range caps {
856-
enc := EncodeCapability(caps.Effective, caps.Permitted, caps.Inheritable)
857-
if err := b.WorkspaceDirFS.SetXattr(path, "security.capability", enc); err != nil {
858-
t.Fatalf("Failed to set capability: %v", err)
857+
expectedAttrs := make(map[string][]byte)
858+
for path, c := range caps {
859+
encoded := EncodeCapability(c.Effective, c.Permitted, c.Inheritable)
860+
expectedAttrs[path] = encoded
861+
862+
if err := b.WorkspaceDirFS.SetXattr(path, "security.capability", encoded); err != nil {
863+
t.Fatalf("failed to set xattr for %s: %v", path, err)
859864
}
860865
}
861866

862-
for path, attrs := range tc.expectedAttrs {
863-
for attr := range attrs {
864-
data, err := b.WorkspaceDirFS.GetXattr(path, attr)
865-
if err != nil {
866-
t.Errorf("Failed to get xattr %s for path %s: %v", attr, path, err)
867-
continue
868-
}
867+
for path, expected := range expectedAttrs {
868+
data, err := b.WorkspaceDirFS.GetXattr(path, "security.capability")
869+
if err != nil {
870+
t.Errorf("Failed to get xattr %s: %v", path, err)
871+
continue
872+
}
869873

870-
if len(data) < 24 {
871-
t.Errorf("Capability data for %s is too short: %d bytes", path, len(data))
872-
continue
873-
}
874+
if !bytes.Equal(data, expected) {
875+
t.Errorf("Mismatched xattr for %s:\ngot: %x\nwant: %x", path, data, expected)
876+
}
874877

875-
magic := binary.LittleEndian.Uint32(data[0:4])
876-
if magic != 0x20080522 {
877-
t.Errorf("Invalid magic number: %x", magic)
878-
}
878+
if len(data) < 24 {
879+
t.Errorf("Capability data too short for %s: got %d bytes", path, len(data))
880+
continue
881+
}
882+
883+
magic := binary.LittleEndian.Uint32(data[0:4])
884+
revision := magic & 0xFF000000
885+
flags := magic & 0x000000FF
879886

880-
version := binary.LittleEndian.Uint32(data[4:8])
881-
if version != 0x3 {
882-
t.Errorf("Invalid version: %d, expected 3", version)
887+
if revision != 0x03000000 {
888+
t.Errorf("Invalid revision: %x", revision)
889+
}
890+
891+
permitted := binary.LittleEndian.Uint32(data[4:8])
892+
inheritable := binary.LittleEndian.Uint32(data[8:12])
893+
rootid := binary.LittleEndian.Uint32(data[20:24])
894+
895+
if rootid != 0 {
896+
t.Errorf("Unexpected rootid: %d", rootid)
897+
}
898+
899+
effective := flags & 0x01
900+
901+
for _, capEntry := range tc.caps {
902+
if capEntry.Path != path {
903+
continue
883904
}
905+
for attr, flag := range capEntry.Add {
906+
for _, a := range strings.Split(attr, ",") {
907+
val := getCapabilityValue(a)
908+
e, p, i := parseCapability(flag)
884909

885-
effective := binary.LittleEndian.Uint32(data[8:12])
886-
permitted := binary.LittleEndian.Uint32(data[12:16])
887-
inheritable := binary.LittleEndian.Uint32(data[16:20])
888-
889-
caps := b.Configuration.Package.SetCap
890-
for _, c := range caps {
891-
if c.Path == path {
892-
for attr, flag := range c.Add {
893-
capValues := getCapabilityValue(attr)
894-
e, p, i := parseCapability(flag)
895-
896-
if e && (effective&capValues != capValues) {
897-
t.Errorf("Expected capabilities %s to be in effective set for %s", attr, path)
898-
}
899-
if p && (permitted&capValues != capValues) {
900-
t.Errorf("Expected capabilities %s to be in permitted set for %s", attr, path)
901-
}
902-
if i && (inheritable&capValues != capValues) {
903-
t.Errorf("Expected capabilities %s to be in inheritable set for %s", attr, path)
904-
}
910+
if e && effective != 1 {
911+
t.Errorf("Expected effective bit set for %s", path)
912+
}
913+
if p && (permitted&val != val) {
914+
t.Errorf("Expected permitted cap %s in %s", a, path)
915+
}
916+
if i && (inheritable&val != val) {
917+
t.Errorf("Expected inheritable cap %s in %s", a, path)
905918
}
906919
}
907920
}

0 commit comments

Comments
 (0)