1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
|
package buildah
import (
"context"
"fmt"
"io"
"time"
"github.com/containers/buildah/define"
"github.com/containers/buildah/internal/mkcw"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/types"
encconfig "github.com/containers/ocicrypt/config"
"github.com/containers/storage"
"github.com/containers/storage/pkg/archive"
"github.com/opencontainers/go-digest"
"github.com/sirupsen/logrus"
)
// CWConvertImageOptions provides both required and optional bits of
// configuration for CWConvertImage().
type CWConvertImageOptions struct {
// Required parameters.
InputImage string
// If supplied, we'll tag the resulting image with the specified name.
Tag string
OutputImage types.ImageReference
// If supplied, we'll register the workload with this server.
// Practically necessary if DiskEncryptionPassphrase is not set, in
// which case we'll generate one and throw it away after.
AttestationURL string
// Used to measure the environment. If left unset (0), defaults will be applied.
CPUs int
Memory int
// Can be manually set. If left unset ("", false, nil), reasonable values will be used.
TeeType define.TeeType
IgnoreAttestationErrors bool
WorkloadID string
DiskEncryptionPassphrase string
Slop string
FirmwareLibrary string
BaseImage string
Logger *logrus.Logger
ExtraImageContent map[string]string
// Passed through to BuilderOptions. Most settings won't make
// sense to be made available here because we don't launch a process.
ContainerSuffix string
PullPolicy PullPolicy
BlobDirectory string
SignaturePolicyPath string
ReportWriter io.Writer
IDMappingOptions *IDMappingOptions
Format string
MaxPullRetries int
PullRetryDelay time.Duration
OciDecryptConfig *encconfig.DecryptConfig
MountLabel string
}
// CWConvertImage takes the rootfs and configuration from one image, generates a
// LUKS-encrypted disk image that more or less includes them both, and puts the
// result into a new container image.
// Returns the new image's ID and digest on success, along with a canonical
// reference for it if a repository name was specified.
func CWConvertImage(ctx context.Context, systemContext *types.SystemContext, store storage.Store, options CWConvertImageOptions) (string, reference.Canonical, digest.Digest, error) {
// Apply our defaults if some options aren't set.
logger := options.Logger
if logger == nil {
logger = logrus.StandardLogger()
}
// Now create the target working container, pulling the base image if
// there is one and it isn't present.
builderOptions := BuilderOptions{
FromImage: options.BaseImage,
SystemContext: systemContext,
Logger: logger,
ContainerSuffix: options.ContainerSuffix,
PullPolicy: options.PullPolicy,
BlobDirectory: options.BlobDirectory,
SignaturePolicyPath: options.SignaturePolicyPath,
ReportWriter: options.ReportWriter,
IDMappingOptions: options.IDMappingOptions,
Format: options.Format,
MaxPullRetries: options.MaxPullRetries,
PullRetryDelay: options.PullRetryDelay,
OciDecryptConfig: options.OciDecryptConfig,
MountLabel: options.MountLabel,
}
target, err := NewBuilder(ctx, store, builderOptions)
if err != nil {
return "", nil, "", fmt.Errorf("creating container from target image: %w", err)
}
defer func() {
if err := target.Delete(); err != nil {
logrus.Warnf("deleting target container: %v", err)
}
}()
targetDir, err := target.Mount("")
if err != nil {
return "", nil, "", fmt.Errorf("mounting target container: %w", err)
}
defer func() {
if err := target.Unmount(); err != nil {
logrus.Warnf("unmounting target container: %v", err)
}
}()
// Mount the source image, pulling it first if necessary.
builderOptions = BuilderOptions{
FromImage: options.InputImage,
SystemContext: systemContext,
Logger: logger,
ContainerSuffix: options.ContainerSuffix,
PullPolicy: options.PullPolicy,
BlobDirectory: options.BlobDirectory,
SignaturePolicyPath: options.SignaturePolicyPath,
ReportWriter: options.ReportWriter,
IDMappingOptions: options.IDMappingOptions,
Format: options.Format,
MaxPullRetries: options.MaxPullRetries,
PullRetryDelay: options.PullRetryDelay,
OciDecryptConfig: options.OciDecryptConfig,
MountLabel: options.MountLabel,
}
source, err := NewBuilder(ctx, store, builderOptions)
if err != nil {
return "", nil, "", fmt.Errorf("creating container from source image: %w", err)
}
defer func() {
if err := source.Delete(); err != nil {
logrus.Warnf("deleting source container: %v", err)
}
}()
sourceInfo := GetBuildInfo(source)
if err != nil {
return "", nil, "", fmt.Errorf("retrieving info about source image: %w", err)
}
sourceImageID := sourceInfo.FromImageID
sourceSize, err := store.ImageSize(sourceImageID)
if err != nil {
return "", nil, "", fmt.Errorf("computing size of source image: %w", err)
}
sourceDir, err := source.Mount("")
if err != nil {
return "", nil, "", fmt.Errorf("mounting source container: %w", err)
}
defer func() {
if err := source.Unmount(); err != nil {
logrus.Warnf("unmounting source container: %v", err)
}
}()
// Generate the image contents.
archiveOptions := mkcw.ArchiveOptions{
AttestationURL: options.AttestationURL,
CPUs: options.CPUs,
Memory: options.Memory,
TempDir: targetDir,
TeeType: options.TeeType,
IgnoreAttestationErrors: options.IgnoreAttestationErrors,
ImageSize: sourceSize,
WorkloadID: options.WorkloadID,
DiskEncryptionPassphrase: options.DiskEncryptionPassphrase,
Slop: options.Slop,
FirmwareLibrary: options.FirmwareLibrary,
Logger: logger,
GraphOptions: store.GraphOptions(),
ExtraImageContent: options.ExtraImageContent,
}
rc, workloadConfig, err := mkcw.Archive(sourceDir, &source.OCIv1, archiveOptions)
if err != nil {
return "", nil, "", fmt.Errorf("generating encrypted image content: %w", err)
}
if err = archive.Untar(rc, targetDir, &archive.TarOptions{}); err != nil {
if err = rc.Close(); err != nil {
logger.Warnf("cleaning up: %v", err)
}
return "", nil, "", fmt.Errorf("saving encrypted image content: %w", err)
}
if err = rc.Close(); err != nil {
return "", nil, "", fmt.Errorf("cleaning up: %w", err)
}
// Commit the image. Clear out most of the configuration (if there is any — we default
// to scratch as a base) so that an engine that doesn't or can't set up a TEE will just
// run the static entrypoint. The rest of the configuration which the runtime consults
// is in the .krun_config.json file in the encrypted filesystem.
logger.Log(logrus.DebugLevel, "committing disk image")
target.ClearAnnotations()
target.ClearEnv()
target.ClearLabels()
target.ClearOnBuild()
target.ClearPorts()
target.ClearVolumes()
target.SetCmd(nil)
target.SetCreatedBy(fmt.Sprintf(": convert %q for use with %q", sourceImageID, workloadConfig.Type))
target.SetDomainname("")
target.SetEntrypoint([]string{"/entrypoint"})
target.SetHealthcheck(nil)
target.SetHostname("")
target.SetMaintainer("")
target.SetShell(nil)
target.SetUser("")
target.SetWorkDir("")
commitOptions := CommitOptions{
SystemContext: systemContext,
}
if options.Tag != "" {
commitOptions.AdditionalTags = append(commitOptions.AdditionalTags, options.Tag)
}
return target.Commit(ctx, options.OutputImage, commitOptions)
}
|