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
  encryption_passphrase: for encrypting the disk image
  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_chain_retrieval_errors: ignore errors from "sevctl export -f"
  ignore_attestation_errors: ignore errors from the key broker server
  slop: extra space to allocate for the disk image

At least one of attestation_url and encryption_passphrase must be
specified in order for the encrypted disk image to be decryptable at
run-time.  Other arguments can be omitted.

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

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
  • Loading branch information
nalind committed Aug 21, 2023
1 parent 0058602 commit 65eb80c
Show file tree
Hide file tree
Showing 76 changed files with 8,857 additions and 17 deletions.
17 changes: 15 additions & 2 deletions Makefile
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
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
10 changes: 10 additions & 0 deletions cmd/buildah/commit.go
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
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
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
104 changes: 104 additions & 0 deletions cmd/buildah/mkcw.go
@@ -0,0 +1,104 @@
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 --encryption-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 type")
flags.StringVarP(&options.AttestationURL, "attestation-url", "u", "", "attestation server URL")
flags.StringVarP(&options.AttestationURL, "attestation_url", "", "", "attestation server URL (alternate flag spelling)")
if err := flags.MarkHidden("attestation_url"); err != nil {
panic("error marking attestation_url as hidden")
}
flags.StringVarP(&options.BaseImage, "base-image", "b", "", "alternate base image (default: scratch)")
flags.StringVarP(&options.BaseImage, "base_image", "", "", "alternate base image (default: scratch) (alternate flag spelling)")
if err := flags.MarkHidden("base_image"); err != nil {
panic("error marking base_image as hidden")
}
flags.StringVarP(&options.DiskEncryptionPassphrase, "encryption-passphrase", "p", "", "disk encryption passphrase")
flags.StringVarP(&options.DiskEncryptionPassphrase, "encryption_passphrase", "", "", "disk encryption passphrase (alternate flag spelling)")
if err := flags.MarkHidden("encryption_passphrase"); err != nil {
panic("error marking encryption_passphrase as hidden")
}
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.WorkloadID, "workload_id", "", "", "workload ID (alternate flag spelling)")
if err := flags.MarkHidden("workload_id"); err != nil {
panic("error marking workload_id as hidden")
}
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.StringVarP(&options.FirmwareLibrary, "firmware_library", "", "", "location of libkrunfw-sev.so (alternate flag spelling)")
if err := flags.MarkHidden("firmware_library"); err != nil {
panic("error marking firmware_library as hidden")
}
flags.BoolVarP(&options.IgnoreAttestationErrors, "ignore-attestation-errors", "", false, "ignore attestation errors")
flags.BoolVarP(&options.IgnoreAttestationErrors, "ignore_attestation_errors", "", false, "ignore attestation errors (alternate flag spelling)")
if err := flags.MarkHidden("ignore_attestation_errors"); err != nil {
panic("error marking ignore_attestation_errors as hidden")
}
flags.BoolVarP(&options.IgnoreChainRetrievalErrors, "ignore-chain-retrieval-errors", "", false, "ignore errors retrieving the certificate chain")
flags.BoolVarP(&options.IgnoreChainRetrievalErrors, "ignore_chain_retrieval_errors", "", false, "ignore errors retrieving the certificate chain (alternate flag spelling)")
if err := flags.MarkHidden("ignore_chain_retrieval_errors"); err != nil {
panic("error marking ignore_chain_retrieval_errors as hidden")
}

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
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

0 comments on commit 65eb80c

Please sign in to comment.