diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/domain/entities/engine_image.go | 4 | ||||
-rw-r--r-- | pkg/domain/entities/images.go | 24 | ||||
-rw-r--r-- | pkg/domain/entities/manifest.go | 5 | ||||
-rw-r--r-- | pkg/domain/infra/abi/manifest.go | 71 | ||||
-rw-r--r-- | pkg/domain/infra/abi/trust.go | 171 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/manifest.go | 15 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/trust.go | 16 | ||||
-rw-r--r-- | pkg/specgen/generate/container.go | 29 | ||||
-rw-r--r-- | pkg/specgen/specgen.go | 1 | ||||
-rw-r--r-- | pkg/trust/config.go | 12 |
10 files changed, 336 insertions, 12 deletions
diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go index c46ba815a..ffa71abd6 100644 --- a/pkg/domain/entities/engine_image.go +++ b/pkg/domain/entities/engine_image.go @@ -22,6 +22,8 @@ type ImageEngine interface { Remove(ctx context.Context, images []string, opts ImageRemoveOptions) (*ImageRemoveReport, []error) Save(ctx context.Context, nameOrId string, tags []string, options ImageSaveOptions) error Search(ctx context.Context, term string, opts ImageSearchOptions) ([]ImageSearchReport, error) + SetTrust(ctx context.Context, args []string, options SetTrustOptions) error + ShowTrust(ctx context.Context, args []string, options ShowTrustOptions) (*ShowTrustReport, error) Shutdown(ctx context.Context) Tag(ctx context.Context, nameOrId string, tags []string, options ImageTagOptions) error Tree(ctx context.Context, nameOrId string, options ImageTreeOptions) (*ImageTreeReport, error) @@ -30,4 +32,6 @@ type ImageEngine interface { ManifestInspect(ctx context.Context, name string) ([]byte, error) ManifestAdd(ctx context.Context, opts ManifestAddOptions) (string, error) ManifestAnnotate(ctx context.Context, names []string, opts ManifestAnnotateOptions) (string, error) + ManifestRemove(ctx context.Context, names []string) (string, error) + ManifestPush(ctx context.Context, names []string, manifestPushOpts ManifestPushOptions) error } diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index 74f27e25f..e116a90b9 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -7,6 +7,7 @@ import ( "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/types" "github.com/containers/libpod/pkg/inspect" + "github.com/containers/libpod/pkg/trust" docker "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/opencontainers/go-digest" @@ -285,3 +286,26 @@ type ImageTreeOptions struct { type ImageTreeReport struct { Tree string // TODO: Refactor move presentation work out of server } + +// ShowTrustOptions are the cli options for showing trust +type ShowTrustOptions struct { + JSON bool + PolicyPath string + Raw bool + RegistryPath string +} + +// ShowTrustReport describes the results of show trust +type ShowTrustReport struct { + Raw []byte + SystemRegistriesDirPath string + JSONOutput []byte + Policies []*trust.TrustPolicy +} + +// SetTrustOptions describes the CLI options for setting trust +type SetTrustOptions struct { + PolicyPath string + PubKeysFile []string + Type string +} diff --git a/pkg/domain/entities/manifest.go b/pkg/domain/entities/manifest.go index d92b1dc9b..273052bb9 100644 --- a/pkg/domain/entities/manifest.go +++ b/pkg/domain/entities/manifest.go @@ -24,3 +24,8 @@ type ManifestAnnotateOptions struct { OSVersion string `json:"os_version" schema:"os_version"` Variant string `json:"variant" schema:"variant"` } + +type ManifestPushOptions struct { + Purge, Quiet, All, TlsVerify, RemoveSignatures bool + Authfile, CertDir, Creds, DigestFile, Format, SignBy string +} diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go index 812507f0a..fca34dda2 100644 --- a/pkg/domain/infra/abi/manifest.go +++ b/pkg/domain/infra/abi/manifest.go @@ -6,15 +6,21 @@ import ( "context" "encoding/json" "fmt" + "io/ioutil" + "os" "strings" + "github.com/containers/buildah/manifests" buildahUtil "github.com/containers/buildah/util" + cp "github.com/containers/image/v5/copy" "github.com/containers/image/v5/docker" + "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/transports/alltransports" libpodImage "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/util" "github.com/opencontainers/go-digest" + imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" ) @@ -137,3 +143,68 @@ func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, names []string, opt } return "", err } + +// ManifestRemove removes specified digest from the specified manifest list +func (ir *ImageEngine) ManifestRemove(ctx context.Context, names []string) (string, error) { + instanceDigest, err := digest.Parse(names[1]) + if err != nil { + return "", errors.Errorf(`invalid image digest "%s": %v`, names[1], err) + } + listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(names[0]) + if err != nil { + return "", errors.Wrapf(err, "error retriving local image from image name %s", names[0]) + } + updatedListID, err := listImage.RemoveManifest(instanceDigest) + if err == nil { + return fmt.Sprintf("%s :%s\n", updatedListID, instanceDigest.String()), nil + } + return "", err +} + +// ManifestPush pushes a manifest list or image index to the destination +func (ir *ImageEngine) ManifestPush(ctx context.Context, names []string, opts entities.ManifestPushOptions) error { + listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(names[0]) + if err != nil { + return errors.Wrapf(err, "error retriving local image from image name %s", names[0]) + } + dest, err := alltransports.ParseImageName(names[1]) + if err != nil { + return err + } + var manifestType string + if opts.Format != "" { + switch opts.Format { + case "oci": + manifestType = imgspecv1.MediaTypeImageManifest + case "v2s2", "docker": + manifestType = manifest.DockerV2Schema2MediaType + default: + return errors.Errorf("unknown format %q. Choose on of the supported formats: 'oci' or 'v2s2'", opts.Format) + } + } + options := manifests.PushOptions{ + Store: ir.Libpod.GetStore(), + SystemContext: ir.Libpod.SystemContext(), + ImageListSelection: cp.CopySpecificImages, + Instances: nil, + RemoveSignatures: opts.RemoveSignatures, + SignBy: opts.SignBy, + ManifestType: manifestType, + } + if opts.All { + options.ImageListSelection = cp.CopyAllImages + } + if !opts.Quiet { + options.ReportWriter = os.Stderr + } + digest, err := listImage.PushManifest(dest, options) + if err == nil && opts.Purge { + _, err = ir.Libpod.GetStore().DeleteImage(listImage.ID(), true) + } + if opts.DigestFile != "" { + if err = ioutil.WriteFile(opts.DigestFile, []byte(digest.String()), 0644); err != nil { + return buildahUtil.GetFailureCause(err, errors.Wrapf(err, "failed to write digest to file %q", opts.DigestFile)) + } + } + return err +} diff --git a/pkg/domain/infra/abi/trust.go b/pkg/domain/infra/abi/trust.go new file mode 100644 index 000000000..5b89c91d9 --- /dev/null +++ b/pkg/domain/infra/abi/trust.go @@ -0,0 +1,171 @@ +package abi + +import ( + "context" + "encoding/json" + "io/ioutil" + "os" + "strings" + + "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/trust" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +func (ir *ImageEngine) ShowTrust(ctx context.Context, args []string, options entities.ShowTrustOptions) (*entities.ShowTrustReport, error) { + var ( + err error + report entities.ShowTrustReport + ) + policyPath := trust.DefaultPolicyPath(ir.Libpod.SystemContext()) + if len(options.PolicyPath) > 0 { + policyPath = options.PolicyPath + } + report.Raw, err = ioutil.ReadFile(policyPath) + if err != nil { + return nil, errors.Wrapf(err, "unable to read %s", policyPath) + } + if options.Raw { + return &report, nil + } + report.SystemRegistriesDirPath = trust.RegistriesDirPath(ir.Libpod.SystemContext()) + if len(options.RegistryPath) > 0 { + report.SystemRegistriesDirPath = options.RegistryPath + } + policyContentStruct, err := trust.GetPolicy(policyPath) + if err != nil { + return nil, errors.Wrapf(err, "could not read trust policies") + } + report.Policies, err = getPolicyShowOutput(policyContentStruct, report.SystemRegistriesDirPath) + if err != nil { + return nil, errors.Wrapf(err, "could not show trust policies") + } + return &report, nil +} + +func (ir *ImageEngine) SetTrust(ctx context.Context, args []string, options entities.SetTrustOptions) error { + var ( + policyContentStruct trust.PolicyContent + newReposContent []trust.RepoContent + ) + trustType := options.Type + if trustType == "accept" { + trustType = "insecureAcceptAnything" + } + + pubkeysfile := options.PubKeysFile + if len(pubkeysfile) == 0 && trustType == "signedBy" { + return errors.Errorf("At least one public key must be defined for type 'signedBy'") + } + + policyPath := trust.DefaultPolicyPath(ir.Libpod.SystemContext()) + if len(options.PolicyPath) > 0 { + policyPath = options.PolicyPath + } + _, err := os.Stat(policyPath) + if !os.IsNotExist(err) { + policyContent, err := ioutil.ReadFile(policyPath) + if err != nil { + return errors.Wrapf(err, "unable to read %s", policyPath) + } + if err := json.Unmarshal(policyContent, &policyContentStruct); err != nil { + return errors.Errorf("could not read trust policies") + } + } + if len(pubkeysfile) != 0 { + for _, filepath := range pubkeysfile { + newReposContent = append(newReposContent, trust.RepoContent{Type: trustType, KeyType: "GPGKeys", KeyPath: filepath}) + } + } else { + newReposContent = append(newReposContent, trust.RepoContent{Type: trustType}) + } + if args[0] == "default" { + policyContentStruct.Default = newReposContent + } else { + if len(policyContentStruct.Default) == 0 { + return errors.Errorf("Default trust policy must be set.") + } + registryExists := false + for transport, transportval := range policyContentStruct.Transports { + _, registryExists = transportval[args[0]] + if registryExists { + policyContentStruct.Transports[transport][args[0]] = newReposContent + break + } + } + if !registryExists { + if policyContentStruct.Transports == nil { + policyContentStruct.Transports = make(map[string]trust.RepoMap) + } + if policyContentStruct.Transports["docker"] == nil { + policyContentStruct.Transports["docker"] = make(map[string][]trust.RepoContent) + } + policyContentStruct.Transports["docker"][args[0]] = append(policyContentStruct.Transports["docker"][args[0]], newReposContent...) + } + } + + data, err := json.MarshalIndent(policyContentStruct, "", " ") + if err != nil { + return errors.Wrapf(err, "error setting trust policy") + } + return ioutil.WriteFile(policyPath, data, 0644) +} + +func getPolicyShowOutput(policyContentStruct trust.PolicyContent, systemRegistriesDirPath string) ([]*trust.TrustPolicy, error) { + var output []*trust.TrustPolicy + + registryConfigs, err := trust.LoadAndMergeConfig(systemRegistriesDirPath) + if err != nil { + return nil, err + } + + if len(policyContentStruct.Default) > 0 { + defaultPolicyStruct := trust.TrustPolicy{ + Name: "* (default)", + RepoName: "default", + Type: trustTypeDescription(policyContentStruct.Default[0].Type), + } + output = append(output, &defaultPolicyStruct) + } + for _, transval := range policyContentStruct.Transports { + for repo, repoval := range transval { + tempTrustShowOutput := trust.TrustPolicy{ + Name: repo, + RepoName: repo, + Type: repoval[0].Type, + } + // TODO - keyarr is not used and I don't know its intent; commenting out for now for someone to fix later + //keyarr := []string{} + uids := []string{} + for _, repoele := range repoval { + if len(repoele.KeyPath) > 0 { + //keyarr = append(keyarr, repoele.KeyPath) + uids = append(uids, trust.GetGPGIdFromKeyPath(repoele.KeyPath)...) + } + if len(repoele.KeyData) > 0 { + //keyarr = append(keyarr, string(repoele.KeyData)) + uids = append(uids, trust.GetGPGIdFromKeyData(repoele.KeyData)...) + } + } + tempTrustShowOutput.GPGId = strings.Join(uids, ", ") + + registryNamespace := trust.HaveMatchRegistry(repo, registryConfigs) + if registryNamespace != nil { + tempTrustShowOutput.SignatureStore = registryNamespace.SigStore + } + output = append(output, &tempTrustShowOutput) + } + } + return output, nil +} + +var typeDescription = map[string]string{"insecureAcceptAnything": "accept", "signedBy": "signed", "reject": "reject"} + +func trustTypeDescription(trustType string) string { + trustDescription, exist := typeDescription[trustType] + if !exist { + logrus.Warnf("invalid trust type %s", trustType) + } + return trustDescription +} diff --git a/pkg/domain/infra/tunnel/manifest.go b/pkg/domain/infra/tunnel/manifest.go index 3d3196019..7d9a0fce1 100644 --- a/pkg/domain/infra/tunnel/manifest.go +++ b/pkg/domain/infra/tunnel/manifest.go @@ -91,3 +91,18 @@ func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, names []string, opt } return fmt.Sprintf("%s :%s", updatedListID, names[1]), nil } + +// ManifestRemove removes the digest from manifest list +func (ir *ImageEngine) ManifestRemove(ctx context.Context, names []string) (string, error) { + updatedListID, err := manifests.Remove(ctx, names[0], names[1]) + if err != nil { + return updatedListID, errors.Wrapf(err, "error removing from manifest %s", names[0]) + } + return fmt.Sprintf("%s :%s\n", updatedListID, names[1]), nil +} + +// ManifestPush pushes a manifest list or image index to the destination +func (ir *ImageEngine) ManifestPush(ctx context.Context, names []string, opts entities.ManifestPushOptions) error { + _, err := manifests.Push(ctx, names[0], &names[1], &opts.All) + return err +} diff --git a/pkg/domain/infra/tunnel/trust.go b/pkg/domain/infra/tunnel/trust.go new file mode 100644 index 000000000..a976bfdc2 --- /dev/null +++ b/pkg/domain/infra/tunnel/trust.go @@ -0,0 +1,16 @@ +package tunnel + +import ( + "context" + "errors" + + "github.com/containers/libpod/pkg/domain/entities" +) + +func (ir *ImageEngine) ShowTrust(ctx context.Context, args []string, options entities.ShowTrustOptions) (*entities.ShowTrustReport, error) { + return nil, errors.New("not implemented") +} + +func (ir *ImageEngine) SetTrust(ctx context.Context, args []string, options entities.SetTrustOptions) error { + return errors.New("not implemented") +} diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index 92a2b4d35..e4bd2991a 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -9,6 +9,7 @@ import ( envLib "github.com/containers/libpod/pkg/env" "github.com/containers/libpod/pkg/signal" "github.com/containers/libpod/pkg/specgen" + "github.com/pkg/errors" "golang.org/x/sys/unix" ) @@ -48,24 +49,28 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat s.StopSignal = &sig } + rtc, err := r.GetConfig() + if err != nil { + return err + } + // Get Default Environment + defaultEnvs, err := envLib.ParseSlice(rtc.Containers.Env) + if err != nil { + return errors.Wrap(err, "Env fields in containers.conf failed to parse") + } + // Image envs from the image if they don't exist - // already - env, err := newImage.Env(ctx) + // already, overriding the default environments + imageEnvs, err := newImage.Env(ctx) if err != nil { return err } - if len(env) > 0 { - envs, err := envLib.ParseSlice(env) - if err != nil { - return err - } - for k, v := range envs { - if _, exists := s.Env[k]; !exists { - s.Env[v] = k - } - } + envs, err := envLib.ParseSlice(imageEnvs) + if err != nil { + return errors.Wrap(err, "Env fields from image failed to parse") } + s.Env = envLib.Join(envLib.Join(defaultEnvs, envs), s.Env) labels, err := newImage.Labels(ctx) if err != nil { diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index 4ad6dd6fb..bb01a5d14 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -47,6 +47,7 @@ type ContainerBasicConfig struct { // Optional. Env map[string]string `json:"env,omitempty"` // Terminal is whether the container will create a PTY. + // Optional. Terminal bool `json:"terminal,omitempty"` // Stdin is whether the container will keep its STDIN open. Stdin bool `json:"stdin,omitempty"` diff --git a/pkg/trust/config.go b/pkg/trust/config.go new file mode 100644 index 000000000..0bafc722b --- /dev/null +++ b/pkg/trust/config.go @@ -0,0 +1,12 @@ +package trust + +// Trust Policy describes a basic trust policy configuration +type TrustPolicy struct { + Name string `json:"name"` + RepoName string `json:"repo_name,omitempty"` + Keys []string `json:"keys,omitempty"` + SignatureStore string `json:"sigstore"` + Transport string `json:"transport"` + Type string `json:"type"` + GPGId string `json:"gpg_id,omitempty"` +} |