Skip to content

Commit

Permalink
Add buildah mkcw, add --cw to buildah commit and buildah build
Browse files Browse the repository at this point in the history
Add a --cw option to `buildah build` and `buildah commit`, which takes a
comma-separated list of arguments and produces an image laid out for use
as a confidential workload:
  type: sev or snp
  attestation_url: location of a key broker server
  cpus: expected number of virtual CPUs to run with
  memory: expected megabytes of memory to run with
  workload_id: a distinguishing identifier for the key broker server
  ignore_attestation_errors: ignore errors registering the workload
  passphrase: for encrypting the disk image
  slop: extra space to allocate for the disk image

At least one of attestation_url and passphrase must be specified in
order for the encrypted disk image to be decryptable at run-time.  Other
arguments can be omitted.  ignore_attestation_errors is intentionally
undocumented, as it's mainly used to permit some amount of testing on
systems which don't have the required hardware.

Add an `mkcw` top-level command, for converting directly from an image
to a confidential workload.

Signed-off-by: Nalin Dahyabhai <[email protected]>
  • Loading branch information
nalind committed Sep 7, 2023
1 parent 25473ec commit e89fac6
Show file tree
Hide file tree
Showing 75 changed files with 8,680 additions and 20 deletions.
17 changes: 15 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ LIBSECCOMP_COMMIT := release-2.3

EXTRA_LDFLAGS ?=
BUILDAH_LDFLAGS := $(GO_LDFLAGS) '-X main.GitCommit=$(GIT_COMMIT) -X main.buildInfo=$(SOURCE_DATE_EPOCH) -X main.cniVersion=$(CNI_COMMIT) $(EXTRA_LDFLAGS)'
SOURCES=*.go imagebuildah/*.go bind/*.go chroot/*.go copier/*.go define/*.go docker/*.go internal/parse/*.go internal/source/*.go internal/util/*.go manifests/*.go pkg/chrootuser/*.go pkg/cli/*.go pkg/completion/*.go pkg/formats/*.go pkg/overlay/*.go pkg/parse/*.go pkg/rusage/*.go pkg/sshagent/*.go pkg/umask/*.go pkg/util/*.go util/*.go
SOURCES=*.go imagebuildah/*.go bind/*.go chroot/*.go copier/*.go define/*.go docker/*.go internal/mkcw/*.go internal/mkcw/types/*.go internal/parse/*.go internal/source/*.go internal/util/*.go manifests/*.go pkg/chrootuser/*.go pkg/cli/*.go pkg/completion/*.go pkg/formats/*.go pkg/overlay/*.go pkg/parse/*.go pkg/rusage/*.go pkg/sshagent/*.go pkg/umask/*.go pkg/util/*.go util/*.go

LINTFLAGS ?=

Expand Down Expand Up @@ -69,9 +69,22 @@ static:
mkdir -p ./bin
cp -rfp ./result/bin/* ./bin/

bin/buildah: $(SOURCES) cmd/buildah/*.go
bin/buildah: $(SOURCES) cmd/buildah/*.go internal/mkcw/embed/entrypoint.gz
$(GO_BUILD) $(BUILDAH_LDFLAGS) $(GO_GCFLAGS) "$(GOGCFLAGS)" -o $@ $(BUILDFLAGS) ./cmd/buildah

ifneq ($(shell as --version | grep x86_64),)
internal/mkcw/embed/entrypoint: internal/mkcw/embed/entrypoint.s
$(AS) -o $(patsubst %.s,%.o,$^) $^
$(LD) -o $@ $(patsubst %.s,%.o,$^)
strip $@
else
.PHONY: internal/mkcw/embed/entrypoint
endif

internal/mkcw/embed/entrypoint.gz: internal/mkcw/embed/entrypoint
$(RM) $@
gzip -k $^

.PHONY: buildah
buildah: bin/buildah

Expand Down
5 changes: 5 additions & 0 deletions buildah.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,11 @@ type ImportFromImageOptions struct {
SystemContext *types.SystemContext
}

// ConfidentialWorkloadOptions encapsulates options which control whether or not
// we output an image whose rootfs contains a LUKS-compatibly-encrypted disk image
// instead of the usual rootfs contents.
type ConfidentialWorkloadOptions = define.ConfidentialWorkloadOptions

// NewBuilder creates a new build container.
func NewBuilder(ctx context.Context, store storage.Store, options BuilderOptions) (*Builder, error) {
if options.CommonBuildOpts == nil {
Expand Down
12 changes: 9 additions & 3 deletions buildah_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,22 @@ import (
)

func TestMain(m *testing.M) {
var logLevel string
debug := false
if InitReexec() {
return
}
flag.BoolVar(&debug, "debug", false, "turn on debug logging")
flag.StringVar(&logLevel, "log-level", "error", "log level")
flag.Parse()
logrus.SetLevel(logrus.ErrorLevel)
if debug {
logrus.SetLevel(logrus.DebugLevel)
level, err := logrus.ParseLevel(logLevel)
if err != nil {
logrus.Fatalf("error parsing log level %q: %v", logLevel, err)
}
if debug && level < logrus.DebugLevel {
level = logrus.DebugLevel
}
logrus.SetLevel(level)
os.Exit(m.Run())
}

Expand Down
10 changes: 10 additions & 0 deletions cmd/buildah/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type commitInputOptions struct {
blobCache string
certDir string
creds string
cwOptions string
disableCompression bool
format string
iidfile string
Expand Down Expand Up @@ -87,6 +88,7 @@ func commitListFlagSet(cmd *cobra.Command, opts *commitInputOptions) {
_ = cmd.RegisterFlagCompletionFunc("cert-dir", completion.AutocompleteDefault)
flags.StringVar(&opts.creds, "creds", "", "use `[username[:password]]` for accessing the registry")
_ = cmd.RegisterFlagCompletionFunc("creds", completion.AutocompleteNone)
flags.StringVar(&opts.cwOptions, "cw", "", "confidential workload `options`")
flags.BoolVarP(&opts.disableCompression, "disable-compression", "D", true, "don't compress layers")
flags.StringVarP(&opts.format, "format", "f", defaultFormat(), "`format` of the image manifest and metadata")
_ = cmd.RegisterFlagCompletionFunc("format", completion.AutocompleteNone)
Expand Down Expand Up @@ -239,6 +241,14 @@ func commitCmd(c *cobra.Command, args []string, iopts commitInputOptions) error
options.HistoryTimestamp = &timestamp
}

if iopts.cwOptions != "" {
confidentialWorkloadOptions, err := parse.GetConfidentialWorkloadOptions(iopts.cwOptions)
if err != nil {
return fmt.Errorf("parsing --cw arguments: %w", err)
}
options.ConfidentialWorkloadOptions = confidentialWorkloadOptions
}

if exclusiveFlags > 1 {
return errors.New("can not use more then one timestamp option at at time")
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/buildah/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,8 @@ func Tail(a []string) []string {
return []string{}
}

// UsageTemplate returns the usage template for podman commands
// This blocks the displaying of the global options. The main podman
// UsageTemplate returns the usage template for buildah commands
// This blocks the displaying of the global options. The main buildah
// command should not use this.
func UsageTemplate() string {
return `Usage:{{if .Runnable}}
Expand Down
2 changes: 1 addition & 1 deletion cmd/buildah/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func init() {
rootCmd.PersistentFlags().StringSliceVar(&globalFlagResults.UserNSUID, "userns-uid-map", []string{}, "default `ctrID:hostID:length` UID mapping to use")
rootCmd.PersistentFlags().StringSliceVar(&globalFlagResults.UserNSGID, "userns-gid-map", []string{}, "default `ctrID:hostID:length` GID mapping to use")
rootCmd.PersistentFlags().StringVar(&globalFlagResults.DefaultMountsFile, "default-mounts-file", "", "path to default mounts file")
rootCmd.PersistentFlags().StringVar(&globalFlagResults.LogLevel, logLevel, "warn", `The log level to be used. Either "trace", "debug", "info", "warn", "error", "fatal", or "panic".`)
rootCmd.PersistentFlags().StringVar(&globalFlagResults.LogLevel, logLevel, "warn", `the log level to be used, one of "trace", "debug", "info", "warn", "error", "fatal", or "panic"`)
rootCmd.PersistentFlags().StringVar(&globalFlagResults.CPUProfile, "cpu-profile", "", "`file` to write CPU profile")
rootCmd.PersistentFlags().StringVar(&globalFlagResults.MemoryProfile, "memory-profile", "", "`file` to write memory profile")

Expand Down
77 changes: 77 additions & 0 deletions cmd/buildah/mkcw.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package main

import (
"fmt"
"os"

"github.com/containers/buildah"
"github.com/containers/buildah/define"
"github.com/containers/buildah/pkg/parse"
"github.com/spf13/cobra"
)

func mkcwCmd(c *cobra.Command, args []string, options buildah.CWConvertImageOptions) error {
ctx := getContext()

systemContext, err := parse.SystemContextFromOptions(c)
if err != nil {
return err
}

if options.AttestationURL == "" && options.DiskEncryptionPassphrase == "" {
return fmt.Errorf("neither --attestation-url nor --passphrase flags provided, disk would not be decryptable")
}

store, err := getStore(c)
if err != nil {
return err
}

options.InputImage = args[0]
options.Tag = args[1]
options.ReportWriter = os.Stderr
imageID, _, _, err := buildah.CWConvertImage(ctx, systemContext, store, options)
if err == nil {
fmt.Printf("%s\n", imageID)
}
return err
}

func init() {
var teeType string
var options buildah.CWConvertImageOptions
mkcwDescription := `Convert a conventional image to a confidential workload image.`
mkcwCommand := &cobra.Command{
Use: "mkcw",
Short: "Convert a conventional image to a confidential workload image",
Long: mkcwDescription,
RunE: func(cmd *cobra.Command, args []string) error {
options.TeeType = define.TeeType(teeType)
return mkcwCmd(cmd, args, options)
},
Example: `buildah mkcw localhost/repository:typical localhost/repository:cw`,
Args: cobra.ExactArgs(2),
}
mkcwCommand.SetUsageTemplate(UsageTemplate())
rootCmd.AddCommand(mkcwCommand)
flags := mkcwCommand.Flags()
flags.SetInterspersed(false)

flags.StringVarP(&teeType, "type", "t", "", "TEE (trusted execution environment) type: SEV,SNP (default: SNP)")
flags.StringVarP(&options.AttestationURL, "attestation-url", "u", "", "attestation server URL")
flags.StringVarP(&options.BaseImage, "base-image", "b", "", "alternate base image (default: scratch)")
flags.StringVarP(&options.DiskEncryptionPassphrase, "passphrase", "p", "", "disk encryption passphrase")
flags.IntVarP(&options.CPUs, "cpus", "c", 0, "number of CPUs to expect")
flags.IntVarP(&options.Memory, "memory", "m", 0, "amount of memory to expect (MB)")
flags.StringVarP(&options.WorkloadID, "workload-id", "w", "", "workload ID")
flags.StringVarP(&options.Slop, "slop", "s", "25%", "extra space needed for converting a container rootfs to a disk image")
flags.StringVarP(&options.FirmwareLibrary, "firmware-library", "f", "", "location of libkrunfw-sev.so")
flags.BoolVarP(&options.IgnoreAttestationErrors, "ignore-attestation-errors", "", false, "ignore attestation errors")
if err := flags.MarkHidden("ignore-attestation-errors"); err != nil {
panic(fmt.Sprintf("error marking ignore-attestation-errors as hidden: %v", err))
}
flags.String("signature-policy", "", "`pathname` of signature policy file (not usually used)")
if err := flags.MarkHidden("signature-policy"); err != nil {
panic(fmt.Sprintf("error marking signature-policy as hidden: %v", err))
}
}
4 changes: 4 additions & 0 deletions commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ type CommitOptions struct {
// integers in the slice represent 0-indexed layer indices, with support for negative
// indexing. i.e. 0 is the first layer, -1 is the last (top-most) layer.
OciEncryptLayers *[]int
// ConfidentialWorkloadOptions is used to force the output image's rootfs to contain a
// LUKS-compatibly encrypted disk image (for use with krun) instead of the usual
// contents of a rootfs.
ConfidentialWorkloadOptions ConfidentialWorkloadOptions
// UnsetEnvs is a list of environments to not add to final image.
// Deprecated: use UnsetEnv() before committing instead.
UnsetEnvs []string
Expand Down
Loading

0 comments on commit e89fac6

Please sign in to comment.