Skip to content

Commit

Permalink
Add Cosign env pass-through
Browse files Browse the repository at this point in the history
Signed-off-by: Ziwen Ning <[email protected]>
  • Loading branch information
ningziwen committed Mar 19, 2023
1 parent b38af9f commit f41e67b
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 56 deletions.
20 changes: 19 additions & 1 deletion cmd/finch/nerdctl.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ func (nc *nerdctlCommand) run(cmdName string, args []string) error {
var (
nerdctlArgs, envs, fileEnvs []string
skip bool
cosign bool
)

for i, arg := range args {
Expand Down Expand Up @@ -121,10 +122,27 @@ func (nc *nerdctlCommand) run(cmdName string, args []string) error {
arg = fmt.Sprintf("%s%s", arg[0:11], resolvedIP)
}
nerdctlArgs = append(nerdctlArgs, arg)
case arg == "--sign=cosign" || arg == "--verify=cosign":
cosign = true
nerdctlArgs = append(nerdctlArgs, arg)
default:
nerdctlArgs = append(nerdctlArgs, arg)
}
}
limaArgs := []string{"shell", limaInstanceName, "sudo"}

// The env variables are not passed through by `limactl shell` by default so we need to assign the variables explicitly.
// https://github.com/lima-vm/lima/issues/1419
if cosign {
cosignEnv := "COSIGN_PASSWORD"
v, b := nc.systemDeps.LookupEnv(cosignEnv)
if b {
limaArgs = append(limaArgs, fmt.Sprintf("%s=%s", cosignEnv, v))
}
}

limaArgs = append(limaArgs, nerdctlCmdName, cmdName)

// to handle environment variables properly, we add all entries found via
// env-file includes to the map first and then all command line environment
// flags, making sure that command line overrides environment file options,
Expand All @@ -146,7 +164,7 @@ func (nc *nerdctlCommand) run(cmdName string, args []string) error {
}
finalArgs = append(finalArgs, nerdctlArgs...)

limaArgs := append([]string{"shell", limaInstanceName, "sudo", nerdctlCmdName, cmdName}, finalArgs...)
limaArgs = append(limaArgs, finalArgs...)

if nc.shouldReplaceForHelp(cmdName, args) {
return nc.creator.RunWithReplacingStdout([]command.Replacement{{Source: "nerdctl", Target: "finch"}}, limaArgs...)
Expand Down
70 changes: 70 additions & 0 deletions cmd/finch/nerdctl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,76 @@ func TestNerdctlCommand_run(t *testing.T) {
Return(fmt.Errorf("failed to replace"))
},
},
{
name: "with COSIGN_PASSWORD env var and --sign=cosign",
cmdName: "push",
args: []string{"--sign=cosign", "test:tag"},
wantErr: nil,
mockSvc: func(
t *testing.T,
lcc *mocks.LimaCmdCreator,
ncsd *mocks.NerdctlCommandSystemDeps,
logger *mocks.Logger,
ctrl *gomock.Controller,
fs afero.Fs,
) {
getVMStatusC := mocks.NewCommand(ctrl)
lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC)
getVMStatusC.EXPECT().Output().Return([]byte("Running"), nil)
logger.EXPECT().Debugf("Status of virtual machine: %s", "Running")
ncsd.EXPECT().LookupEnv("COSIGN_PASSWORD").Return("test", true)
c := mocks.NewCommand(ctrl)
lcc.EXPECT().Create("shell", limaInstanceName, "sudo", "COSIGN_PASSWORD=test", nerdctlCmdName,
"push", "--sign=cosign", "test:tag").Return(c)
c.EXPECT().Run()
},
},
{
name: "with COSIGN_PASSWORD env var and --verify=cosign",
cmdName: "pull",
args: []string{"--verify=cosign", "test:tag"},
wantErr: nil,
mockSvc: func(
t *testing.T,
lcc *mocks.LimaCmdCreator,
ncsd *mocks.NerdctlCommandSystemDeps,
logger *mocks.Logger,
ctrl *gomock.Controller,
fs afero.Fs,
) {
getVMStatusC := mocks.NewCommand(ctrl)
lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC)
getVMStatusC.EXPECT().Output().Return([]byte("Running"), nil)
logger.EXPECT().Debugf("Status of virtual machine: %s", "Running")
ncsd.EXPECT().LookupEnv("COSIGN_PASSWORD").Return("test", true)
c := mocks.NewCommand(ctrl)
lcc.EXPECT().Create("shell", limaInstanceName, "sudo", "COSIGN_PASSWORD=test", nerdctlCmdName,
"pull", "--verify=cosign", "test:tag").Return(c)
c.EXPECT().Run()
},
},
{
name: "with COSIGN_PASSWORD env var without cosign arg",
cmdName: "pull",
args: []string{"test:tag"},
wantErr: nil,
mockSvc: func(
t *testing.T,
lcc *mocks.LimaCmdCreator,
ncsd *mocks.NerdctlCommandSystemDeps,
logger *mocks.Logger,
ctrl *gomock.Controller,
fs afero.Fs,
) {
getVMStatusC := mocks.NewCommand(ctrl)
lcc.EXPECT().CreateWithoutStdio("ls", "-f", "{{.Status}}", limaInstanceName).Return(getVMStatusC)
getVMStatusC.EXPECT().Output().Return([]byte("Running"), nil)
logger.EXPECT().Debugf("Status of virtual machine: %s", "Running")
c := mocks.NewCommand(ctrl)
lcc.EXPECT().Create("shell", limaInstanceName, "sudo", nerdctlCmdName, "pull", "test:tag").Return(c)
c.EXPECT().Run()
},
},
}

for _, tc := range testCases {
Expand Down
107 changes: 52 additions & 55 deletions e2e/container/container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import (

"github.com/onsi/ginkgo/v2"
"github.com/onsi/gomega"
"github.com/runfinch/common-tests/command"
"github.com/runfinch/common-tests/tests"

"github.com/runfinch/finch/e2e"
)
Expand All @@ -24,61 +22,60 @@ func TestContainer(t *testing.T) {
t.Fatal(err)
}

ginkgo.SynchronizedBeforeSuite(func() []byte {
command.New(o, "vm", "init").WithTimeoutInSeconds(600).Run()
tests.SetupLocalRegistry(o)
return nil
}, func(bytes []byte) {})

ginkgo.SynchronizedAfterSuite(func() {
command.New(o, "vm", "stop").WithTimeoutInSeconds(90).Run()
command.New(o, "vm", "remove").WithTimeoutInSeconds(60).Run()
}, func() {})
// ginkgo.SynchronizedBeforeSuite(func() []byte {
// command.New(o, "vm", "init").WithTimeoutInSeconds(600).Run()
// tests.SetupLocalRegistry(o)
// return nil
// }, func(bytes []byte) {})
//
// ginkgo.SynchronizedAfterSuite(func() {
// command.New(o, "vm", "stop").WithTimeoutInSeconds(90).Run()
// command.New(o, "vm", "remove").WithTimeoutInSeconds(60).Run()
// }, func() {})

ginkgo.Describe(description, func() {
tests.Pull(o)
tests.Rm(o)
tests.Rmi(o)
tests.Run(&tests.RunOption{BaseOpt: o, CGMode: tests.Unified, DefaultHostGatewayIP: "192.168.5.2"})
tests.Start(o)
tests.Stop(o)
tests.Cp(o)
tests.Tag(o)
tests.Save(o)
tests.Load(o)
tests.Build(o)
tests.Push(o)
tests.Images(o)
tests.ComposeBuild(o)
tests.ComposeDown(o)
tests.ComposeKill(o)
tests.ComposePs(o)
tests.ComposePull(o)
tests.ComposeLogs(o)
tests.Create(o)
tests.Port(o)
tests.Kill(o)
tests.Stats(o)
tests.BuilderPrune(o)
tests.Exec(o)
tests.Logs(o)
tests.Login(o)
tests.Logout(o)
tests.VolumeCreate(o)
tests.VolumeInspect(o)
tests.VolumeLs(o)
tests.VolumeRm(o)
tests.VolumePrune(o)
tests.ImageHistory(o)
tests.ImageInspect(o)
tests.ImagePrune(o)
tests.Info(o)
tests.Events(o)
tests.Inspect(o)
tests.NetworkCreate(o)
tests.NetworkInspect(o)
tests.NetworkLs(o)
tests.NetworkRm(o)
// tests.Rm(o)

// tests.Start(o)
// tests.Stop(o)
// tests.Cp(o)
// tests.Tag(o)
// tests.Save(o)
// tests.Load(o)
// tests.Build(o)
// tests.Push(o)
// tests.Images(o)
// tests.ComposeBuild(o)
// tests.ComposeDown(o)
// tests.ComposeKill(o)
// tests.ComposePs(o)
// tests.ComposePull(o)
// tests.ComposeLogs(o)
// tests.Create(o)
// tests.Port(o)
// tests.Kill(o)
// tests.Stats(o)
// tests.BuilderPrune(o)
// tests.Exec(o)
// tests.Logs(o)
// tests.Login(o)
// tests.Logout(o)
// tests.VolumeCreate(o)
// tests.VolumeInspect(o)
// tests.VolumeLs(o)
// tests.VolumeRm(o)
// tests.VolumePrune(o)
// tests.ImageHistory(o)
// tests.ImageInspect(o)
// tests.ImagePrune(o)
// tests.Info(o)
// tests.Events(o)
// tests.Inspect(o)
// tests.NetworkCreate(o)
// tests.NetworkInspect(o)
// tests.NetworkLs(o)
// tests.NetworkRm(o)
testCosign(o)
})

gomega.RegisterFailHandler(ginkgo.Fail)
Expand Down
11 changes: 11 additions & 0 deletions e2e/container/cosign_data/test-1.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-----BEGIN ENCRYPTED COSIGN PRIVATE KEY-----
eyJrZGYiOnsibmFtZSI6InNjcnlwdCIsInBhcmFtcyI6eyJOIjozMjc2OCwiciI6
OCwicCI6MX0sInNhbHQiOiIvYW9saHRuZEZTSHZsQjBZSnBTOVI1VlcyOE5HUmox
VkJNL2VDZWlvVEV3PSJ9LCJjaXBoZXIiOnsibmFtZSI6Im5hY2wvc2VjcmV0Ym94
Iiwibm9uY2UiOiJGRGpKU1BTdnN4WmQ0N2orRlgvUjlPbVB0WlpkTVh4dyJ9LCJj
aXBoZXJ0ZXh0IjoibzlCOXJJbmZPNXZaeE9PMFBSdFdjYlNUQmxibXA5OVVWTnEv
ZFhJN0hzd09yZFpVeTA1MmdUT3AyVkFsSjk2aTNFZitiY095QlU1MWt1UDd2R2gy
U1ljU2VkbWQvejEzM3owNUovZytjUll3bHRuNkowOTgwZ0xUR1NKdWxobFNIYWpC
Q25LS1RmY2tIb0dUU0dsZkU1aFk1UFdyRGlQTmc3VVA4bk1lc2JCWlRPMnFjaUdE
bTI0a21ON1RIOEljRlJ4T3Y1NkFNWm1tTUE9PSJ9
-----END ENCRYPTED COSIGN PRIVATE KEY-----
4 changes: 4 additions & 0 deletions e2e/container/cosign_data/test-1.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfkODzHTFabSz0T+X758IqIB6pi3u
Km4JQCcEDFv94s6J4msvNOhmiAv3PQ/b9dutQ7QODWJAdm3cp6CMd87e1w==
-----END PUBLIC KEY-----
11 changes: 11 additions & 0 deletions e2e/container/cosign_data/test-2.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-----BEGIN ENCRYPTED COSIGN PRIVATE KEY-----
eyJrZGYiOnsibmFtZSI6InNjcnlwdCIsInBhcmFtcyI6eyJOIjozMjc2OCwiciI6
OCwicCI6MX0sInNhbHQiOiJiYlVrSzkzdmY2dEhIM1czNTJ1UWM2ajNidzduUjdW
cTdReHJaRVlJYXM4PSJ9LCJjaXBoZXIiOnsibmFtZSI6Im5hY2wvc2VjcmV0Ym94
Iiwibm9uY2UiOiJBWlhuSThncGhjbzdFdDBVOGhzKy91Tk5OdFpyTWNBOSJ9LCJj
aXBoZXJ0ZXh0IjoiaVJsUG9IRlJnQmJTamc2NnhvTTM4dm0vNExwelJXeUlKZi8x
dFIraEJ3dHdRMTQwZjBsQlpHdHRRMXZYcEE4UktHK09vTnNUcFNUSGh6R2phd3VN
ZXNzaVdsWkxHVTVjSTU5UVZ4TW40bGI4ME5iNWF2NlA5ZjVYT2x2aHhXbnMzUnpt
R091L0hCOHd1ZnBUUnBlQlZNLzZkZmFyV05ZbE92VzRYaWVvZXNEQ0hOV3JWbzFJ
WWdCbysxTEFYbzdYWkQ4cXNVWFdJd3ZONkE9PSJ9
-----END ENCRYPTED COSIGN PRIVATE KEY-----
4 changes: 4 additions & 0 deletions e2e/container/cosign_data/test-2.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERyYVqiPX1IGvTiAuJCZIOfPllOm/
HTi7DaswFLwNXVOOC7FLP3L9YzQ0q24bFBqkSQqgWeycKsOOPCbF0nMLHQ==
-----END PUBLIC KEY-----
85 changes: 85 additions & 0 deletions e2e/container/cosign_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package container

import (
"fmt"
"os"
"path/filepath"

"github.com/onsi/ginkgo/v2"
"github.com/onsi/gomega"
"github.com/runfinch/common-tests/command"
"github.com/runfinch/common-tests/ffs"
"github.com/runfinch/common-tests/fnet"
"github.com/runfinch/common-tests/option"
)

const (
alpineImage = "public.ecr.aws/docker/library/alpine:latest"
registryImage = "public.ecr.aws/docker/library/registry:latest"
)

var testCosign = func(o *option.Option) {
ginkgo.Describe("Cosign", func() {
var buildContext string
var port int
var registry string
var tag string
ginkgo.BeforeEach(func() {
command.RemoveAll(o)
buildContext = ffs.CreateBuildContext(fmt.Sprintf(`FROM %s
CMD ["echo", "bar"]
`, alpineImage))
ginkgo.DeferCleanup(os.RemoveAll, buildContext)
filename := "htpasswd"
// The htpasswd is generated by
// `<subject> run --entrypoint htpasswd public.ecr.aws/docker/library/httpd:2 -Bbn testUser testPassword`.
// We don't want to generate it on the fly because:
// 1. Pulling the httpd image can take a long time, sometimes even more 10 seconds.
// 2. It's unlikely that we will have to update this in the future.
// 3. It's not the thing we want to validate by the functional tests. We only want the output produced by it.
//nolint:gosec // This password is only used for testing purpose.
htpasswd := "testUser:$2y$05$wE0sj3r9O9K9q7R0MXcfPuIerl/06L1IsxXkCuUr3QZ8lHWwicIdS"
htpasswdDir := filepath.Dir(ffs.CreateTempFile(filename, htpasswd))
ginkgo.DeferCleanup(os.RemoveAll, htpasswdDir)
port = fnet.GetFreePort()
command.Run(o, "run",
"-dp", fmt.Sprintf("%d:5000", port),
"--name", "registry",
"-v", fmt.Sprintf("%s:/auth", htpasswdDir),
"-e", "REGISTRY_AUTH=htpasswd",
"-e", "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm",
"-e", fmt.Sprintf("REGISTRY_AUTH_HTPASSWD_PATH=/auth/%s", filename),
registryImage)
registry = fmt.Sprintf(`localhost:%d`, port)
tag = fmt.Sprintf(`%s/test-login:tag`, registry)
})

ginkgo.AfterEach(func() {
command.RemoveAll(o)
})

ginkgo.It("should succeed to verify the signature of the image only when it is signed with the matched key", func() {
command.Run(o, "build", "-t", tag, buildContext)
gomega.Expect(os.Setenv("COSIGN_PASSWORD", "test-1")).To(gomega.Succeed())
command.Run(o, "login", registry, "-u", "testUser", "-p", "testPassword")
ginkgo.DeferCleanup(func() {
command.Run(o, "logout", registry)
})
command.Run(o, "push", "--sign=cosign", "--cosign-key=./cosign_data/test-1.key", tag)
command.Run(o, "pull", "--verify=cosign", "--cosign-key=./cosign_data/test-1.pub", tag)
command.Run(o, "run", "-d", "--verify=cosign", "--cosign-key=./cosign_data/test-1.pub", tag)

gomega.Expect(command.RunWithoutSuccessfulExit(o, "pull", "--verify=cosign", "--cosign-key=./cosign_data/test-2.pub",
tag).Err.Contents()).Should(gomega.ContainSubstring("no matching signatures"))
gomega.Expect(command.RunWithoutSuccessfulExit(o, "run", "-d", "--verify=cosign", "--cosign-key=./cosign_data/test-2.pub",
tag).Err.Contents()).Should(gomega.ContainSubstring("no matching signatures"))
gomega.Expect(command.RunWithoutSuccessfulExit(o, "pull", "--verify=cosign", "--cosign-key=./cosign_data/test-2.pub",
alpineImage).Err.Contents()).Should(gomega.ContainSubstring("no matching signatures"))
gomega.Expect(command.RunWithoutSuccessfulExit(o, "run", "-d", "--verify=cosign", "--cosign-key=./cosign_data/test-2.pub",
alpineImage).Err.Contents()).Should(gomega.ContainSubstring("no matching signatures"))
})
})
}

0 comments on commit f41e67b

Please sign in to comment.