github.com/containerd/nerdctl@v1.7.7/cmd/nerdctl/image_encrypt_linux_test.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"os"
    23  	"os/exec"
    24  	"path/filepath"
    25  	"testing"
    26  
    27  	"github.com/containerd/containerd"
    28  	"github.com/containerd/containerd/content"
    29  	"github.com/containerd/nerdctl/pkg/buildkitutil"
    30  	"github.com/containerd/nerdctl/pkg/testutil"
    31  	"github.com/containerd/nerdctl/pkg/testutil/testregistry"
    32  	"gotest.tools/v3/assert"
    33  )
    34  
    35  type jweKeyPair struct {
    36  	prv     string
    37  	pub     string
    38  	cleanup func()
    39  }
    40  
    41  func newJWEKeyPair(t testing.TB) *jweKeyPair {
    42  	testutil.RequireExecutable(t, "openssl")
    43  	td, err := os.MkdirTemp(t.TempDir(), "jwe-key-pair")
    44  	assert.NilError(t, err)
    45  	prv := filepath.Join(td, "mykey.pem")
    46  	pub := filepath.Join(td, "mypubkey.pem")
    47  	cmds := [][]string{
    48  		// Exec openssl commands to ensure that nerdctl is compatible with the output of openssl commands.
    49  		// Do NOT refactor this function to use "crypto/rsa" stdlib.
    50  		{"openssl", "genrsa", "-out", prv},
    51  		{"openssl", "rsa", "-in", prv, "-pubout", "-out", pub},
    52  	}
    53  	for _, f := range cmds {
    54  		cmd := exec.Command(f[0], f[1:]...)
    55  		if out, err := cmd.CombinedOutput(); err != nil {
    56  			t.Fatalf("failed to run %v: %v (%q)", cmd.Args, err, string(out))
    57  		}
    58  	}
    59  	return &jweKeyPair{
    60  		prv: prv,
    61  		pub: pub,
    62  		cleanup: func() {
    63  			_ = os.RemoveAll(td)
    64  		},
    65  	}
    66  }
    67  
    68  func rmiAll(base *testutil.Base) {
    69  	base.T.Logf("Pruning images")
    70  	imageIDs := base.Cmd("images", "--no-trunc", "-a", "-q").OutLines()
    71  	// remove empty output line at the end
    72  	imageIDs = imageIDs[:len(imageIDs)-1]
    73  	// use `Run` on purpose (same below) because `rmi all` may fail on individual
    74  	// image id that has an expected running container (e.g. a registry)
    75  	base.Cmd(append([]string{"rmi", "-f"}, imageIDs...)...).Run()
    76  
    77  	base.T.Logf("Pruning build caches")
    78  	if _, err := buildkitutil.GetBuildkitHost(testutil.Namespace); err == nil {
    79  		base.Cmd("builder", "prune").AssertOK()
    80  	}
    81  
    82  	// For BuildKit >= 0.11, pruning cache isn't enough to remove manifest blobs that are referred by build history blobs
    83  	// https://github.com/containerd/nerdctl/pull/1833
    84  	if base.Target == testutil.Nerdctl {
    85  		base.T.Logf("Pruning all content blobs")
    86  		addr := base.ContainerdAddress()
    87  		client, err := containerd.New(addr, containerd.WithDefaultNamespace(testutil.Namespace))
    88  		assert.NilError(base.T, err)
    89  		cs := client.ContentStore()
    90  		ctx := context.TODO()
    91  		wf := func(info content.Info) error {
    92  			base.T.Logf("Pruning blob %+v", info)
    93  			if err := cs.Delete(ctx, info.Digest); err != nil {
    94  				base.T.Log(err)
    95  			}
    96  			return nil
    97  		}
    98  		if err := cs.Walk(ctx, wf); err != nil {
    99  			base.T.Log(err)
   100  		}
   101  
   102  		base.T.Logf("Pruning all images (again?)")
   103  		imageIDs = base.Cmd("images", "--no-trunc", "-a", "-q").OutLines()
   104  		base.T.Logf("pruning following images: %+v", imageIDs)
   105  		base.Cmd(append([]string{"rmi", "-f"}, imageIDs...)...).Run()
   106  	}
   107  }
   108  
   109  func TestImageEncryptJWE(t *testing.T) {
   110  	testutil.RequiresBuild(t)
   111  	testutil.DockerIncompatible(t)
   112  	keyPair := newJWEKeyPair(t)
   113  	defer keyPair.cleanup()
   114  	base := testutil.NewBase(t)
   115  	tID := testutil.Identifier(t)
   116  	reg := testregistry.NewPlainHTTP(base, 5000)
   117  	defer reg.Cleanup()
   118  	base.Cmd("pull", testutil.CommonImage).AssertOK()
   119  	encryptImageRef := fmt.Sprintf("127.0.0.1:%d/%s:encrypted", reg.ListenPort, tID)
   120  	defer base.Cmd("rmi", encryptImageRef).Run()
   121  	base.Cmd("image", "encrypt", "--recipient=jwe:"+keyPair.pub, testutil.CommonImage, encryptImageRef).AssertOK()
   122  	base.Cmd("image", "inspect", "--mode=native", "--format={{len .Index.Manifests}}", encryptImageRef).AssertOutExactly("1\n")
   123  	base.Cmd("image", "inspect", "--mode=native", "--format={{json .Manifest.Layers}}", encryptImageRef).AssertOutContains("org.opencontainers.image.enc.keys.jwe")
   124  	base.Cmd("push", encryptImageRef).AssertOK()
   125  	// remove all local images (in the nerdctl-test namespace), to ensure that we do not have blobs of the original image.
   126  	rmiAll(base)
   127  	base.Cmd("pull", encryptImageRef).AssertFail() // defaults to --unpack=true, and fails due to missing prv key
   128  	base.Cmd("pull", "--unpack=false", encryptImageRef).AssertOK()
   129  	decryptImageRef := tID + ":decrypted"
   130  	defer base.Cmd("rmi", decryptImageRef).Run()
   131  	base.Cmd("image", "decrypt", "--key="+keyPair.pub, encryptImageRef, decryptImageRef).AssertFail() // decryption needs prv key, not pub key
   132  	base.Cmd("image", "decrypt", "--key="+keyPair.prv, encryptImageRef, decryptImageRef).AssertOK()
   133  }