get.porter.sh/porter@v1.3.0/tests/integration/signing_test.go (about) 1 //go:build integration 2 3 package integration 4 5 import ( 6 "fmt" 7 "os" 8 "path/filepath" 9 "regexp" 10 "testing" 11 12 "get.porter.sh/porter/pkg/cnab" 13 "get.porter.sh/porter/tests/tester" 14 "github.com/distribution/reference" 15 "github.com/google/go-containerregistry/pkg/crane" 16 "github.com/opencontainers/go-digest" 17 "github.com/stretchr/testify/require" 18 "github.com/uwu-tools/magex/shx" 19 ) 20 21 func TestCosign(t *testing.T) { 22 testr, err := tester.NewTestWithConfig(t, "tests/integration/testdata/signing/config/config-cosign.yaml") 23 require.NoError(t, err, "tester.NewTest failed") 24 defer testr.Close() 25 reg := testr.StartTestRegistry(tester.TestRegistryOptions{UseTLS: true}) 26 defer reg.Close() 27 ref := cnab.MustParseOCIReference(fmt.Sprintf("%s/sign:v1.0.0", reg.String())) 28 29 setupCosign(t, testr) 30 _, output, err := testr.RunPorterWith(func(pc *shx.PreparedCommand) { 31 pc.Args("publish", "--sign-bundle", "--insecure-registry", "-f", "testdata/bundles/signing/porter.yaml", "-r", ref.String()) 32 pc.Env("COSIGN_PASSWORD='test'") 33 }) 34 require.NoError(t, err, "Publish failed") 35 36 ref = toRefWithDigest(t, ref) 37 bundleImageRef := resolveBundleImageDigest(t, output, "sign") 38 39 _, output = testr.RequirePorter("install", "--verify-bundle", "--reference", ref.String(), "--insecure-registry", "--force") 40 require.Contains(t, output, fmt.Sprintf("bundle signature verified for %s", ref.String())) 41 require.Contains(t, output, fmt.Sprintf("bundle image signature verified for %s", bundleImageRef.String())) 42 } 43 44 func TestCosignFromArchive(t *testing.T) { 45 testr, err := tester.NewTestWithConfig(t, "tests/integration/testdata/signing/config/config-cosign.yaml") 46 require.NoError(t, err, "tester.NewTest failed") 47 defer testr.Close() 48 reg := testr.StartTestRegistry(tester.TestRegistryOptions{UseTLS: true}) 49 defer reg.Close() 50 ref := cnab.MustParseOCIReference(fmt.Sprintf("%s/sign:v1.0.0", reg.String())) 51 52 setupCosign(t, testr) 53 _, output, err := testr.RunPorterWith(func(pc *shx.PreparedCommand) { 54 pc.Args("publish", "--insecure-registry", "-f", "testdata/bundles/signing/porter.yaml", "-r", ref.String()) 55 pc.Env("COSIGN_PASSWORD='test'") 56 }) 57 require.NoError(t, err, "Publish failed") 58 59 tmpDir, err := os.MkdirTemp("", "signBundle") 60 require.NoError(t, err, "Error creating temporary directory") 61 defer func() { 62 os.RemoveAll(tmpDir) 63 }() 64 archivePath := filepath.Join(tmpDir, "signBundle.tgz") 65 _, output = testr.RequirePorter("archive", archivePath, "--insecure-registry", "--reference", ref.String()) 66 ref, err = cnab.ParseOCIReference(fmt.Sprintf("%s/sign-from-archive:v1.0.0", reg.String())) 67 require.NoError(t, err, "error parsing OCI reference") 68 69 _, output, err = testr.RunPorterWith(func(pc *shx.PreparedCommand) { 70 pc.Args("publish", "--sign-bundle", "--insecure-registry", "--archive", archivePath, "-r", ref.String()) 71 pc.Env("COSIGN_PASSWORD='test'") 72 }) 73 fmt.Println(output) 74 require.NoError(t, err, "Publish archive failed") 75 76 ref = toRefWithDigest(t, ref) 77 bundleImageRef := getBundleImageDigest(t, output, "sign-from-archive") 78 79 _, output = testr.RequirePorter("install", "--verify-bundle", "--reference", ref.String(), "--insecure-registry", "--force") 80 require.Contains(t, output, fmt.Sprintf("bundle signature verified for %s", ref.String())) 81 require.Contains(t, output, fmt.Sprintf("bundle image signature verified for %s", bundleImageRef.String())) 82 } 83 84 func TestCosignCopyBundle(t *testing.T) { 85 testr, err := tester.NewTestWithConfig(t, "tests/integration/testdata/signing/config/config-cosign.yaml") 86 require.NoError(t, err, "tester.NewTest failed") 87 defer testr.Close() 88 reg := testr.StartTestRegistry(tester.TestRegistryOptions{UseTLS: true}) 89 defer reg.Close() 90 ref := cnab.MustParseOCIReference(fmt.Sprintf("%s/sign:v1.0.0", reg.String())) 91 secondReg := testr.StartTestRegistry(tester.TestRegistryOptions{UseTLS: true}) 92 defer secondReg.Close() 93 copiedRef := cnab.MustParseOCIReference(fmt.Sprintf("%s/sign:v1.0.0", secondReg.String())) 94 95 setupCosign(t, testr) 96 _, output, err := testr.RunPorterWith(func(pc *shx.PreparedCommand) { 97 pc.Args("publish", "--insecure-registry", "-f", "testdata/bundles/signing/porter.yaml", "-r", ref.String()) 98 pc.Env("COSIGN_PASSWORD='test'") 99 }) 100 require.NoError(t, err, "Publish failed") 101 102 _, output, err = testr.RunPorterWith(func(pc *shx.PreparedCommand) { 103 pc.Args("copy", "--insecure-registry", "--sign-bundle", "--source", ref.String(), "--destination", copiedRef.String()) 104 pc.Env("COSIGN_PASSWORD='test'") 105 }) 106 fmt.Println(output) 107 require.NoError(t, err, "Copy failed") 108 109 ref = toRefWithDigest(t, ref) 110 bundleImageRef := getBundleImageDigest(t, output, "sign") 111 112 _, output = testr.RequirePorter("install", "--verify-bundle", "--reference", copiedRef.String(), "--insecure-registry", "--force") 113 require.Contains(t, output, fmt.Sprintf("bundle signature verified for %s", copiedRef.String())) 114 require.Contains(t, output, fmt.Sprintf("bundle image signature verified for %s", bundleImageRef.String())) 115 } 116 117 func setupCosign(t *testing.T, testr tester.Tester) { 118 cmd := shx.Command("cosign", "generate-key-pair").Env("COSIGN_PASSWORD='test'").In(testr.PorterHomeDir) 119 err := cmd.RunE() 120 require.NoError(t, err, "Generate cosign key pair failed") 121 } 122 123 func TestNotation(t *testing.T) { 124 testr, err := tester.NewTestWithConfig(t, "tests/integration/testdata/signing/config/config-notation.yaml") 125 require.NoError(t, err, "tester.NewTest failed") 126 defer testr.Close() 127 reg := testr.StartTestRegistry(tester.TestRegistryOptions{UseTLS: false}) 128 defer reg.Close() 129 ref := cnab.MustParseOCIReference(fmt.Sprintf("%s/sign:v1.0.0", reg.String())) 130 131 setupNotation(t, testr) 132 defer cleanupNotation(t) 133 _, output, err := testr.RunPorterWith(func(pc *shx.PreparedCommand) { 134 pc.Args("publish", "--sign-bundle", "--insecure-registry", "-f", "testdata/bundles/signing/porter.yaml", "-r", ref.String()) 135 }) 136 require.NoError(t, err, "Publish failed") 137 138 ref = toRefWithDigest(t, ref) 139 bundleImageRef := resolveBundleImageDigest(t, output, "sign") 140 141 _, output = testr.RequirePorter("install", "--verify-bundle", "--reference", ref.String(), "--insecure-registry", "--force") 142 fmt.Println(output) 143 require.Contains(t, output, fmt.Sprintf("bundle signature verified for %s", ref.String())) 144 require.Contains(t, output, fmt.Sprintf("bundle image signature verified for %s", bundleImageRef.String())) 145 } 146 147 func TestNotationFromArchive(t *testing.T) { 148 testr, err := tester.NewTestWithConfig(t, "tests/integration/testdata/signing/config/config-notation.yaml") 149 require.NoError(t, err, "tester.NewTest failed") 150 defer testr.Close() 151 reg := testr.StartTestRegistry(tester.TestRegistryOptions{UseTLS: false}) 152 defer reg.Close() 153 ref := cnab.MustParseOCIReference(fmt.Sprintf("%s/sign:v1.0.0", reg.String())) 154 155 setupNotation(t, testr) 156 defer cleanupNotation(t) 157 _, output, err := testr.RunPorterWith(func(pc *shx.PreparedCommand) { 158 pc.Args("publish", "--insecure-registry", "-f", "testdata/bundles/signing/porter.yaml", "-r", ref.String()) 159 }) 160 require.NoError(t, err, "Publish failed") 161 162 tmpDir, err := os.MkdirTemp("", "signBundle") 163 require.NoError(t, err, "Error creating temporary directory") 164 defer func() { 165 os.RemoveAll(tmpDir) 166 }() 167 archivePath := filepath.Join(tmpDir, "signBundle.tgz") 168 _, output = testr.RequirePorter("archive", archivePath, "--insecure-registry", "--reference", ref.String()) 169 ref, err = cnab.ParseOCIReference(fmt.Sprintf("%s/sign-from-archive:v1.0.0", reg.String())) 170 require.NoError(t, err, "error parsing OCI reference") 171 172 _, output, err = testr.RunPorterWith(func(pc *shx.PreparedCommand) { 173 pc.Args("publish", "--sign-bundle", "--insecure-registry", "--archive", archivePath, "-r", ref.String()) 174 }) 175 fmt.Println(output) 176 require.NoError(t, err, "Publish archive failed") 177 178 ref = toRefWithDigest(t, ref) 179 bundleImageRef := getBundleImageDigest(t, output, "sign-from-archive") 180 181 _, output = testr.RequirePorter("install", "--verify-bundle", "--reference", ref.String(), "--insecure-registry", "--force") 182 require.Contains(t, output, fmt.Sprintf("bundle signature verified for %s", ref.String())) 183 require.Contains(t, output, fmt.Sprintf("bundle image signature verified for %s", bundleImageRef.String())) 184 } 185 186 func TestNotationCopyBundle(t *testing.T) { 187 testr, err := tester.NewTestWithConfig(t, "tests/integration/testdata/signing/config/config-notation.yaml") 188 require.NoError(t, err, "tester.NewTest failed") 189 defer testr.Close() 190 reg := testr.StartTestRegistry(tester.TestRegistryOptions{UseTLS: false}) 191 defer reg.Close() 192 ref := cnab.MustParseOCIReference(fmt.Sprintf("%s/sign:v1.0.0", reg.String())) 193 secondReg := testr.StartTestRegistry(tester.TestRegistryOptions{UseTLS: false}) 194 defer secondReg.Close() 195 copiedRef := cnab.MustParseOCIReference(fmt.Sprintf("%s/sign:v1.0.0", secondReg.String())) 196 197 setupNotation(t, testr) 198 defer cleanupNotation(t) 199 _, output, err := testr.RunPorterWith(func(pc *shx.PreparedCommand) { 200 pc.Args("publish", "--insecure-registry", "-f", "testdata/bundles/signing/porter.yaml", "-r", ref.String()) 201 }) 202 require.NoError(t, err, "Publish failed") 203 204 _, output, err = testr.RunPorterWith(func(pc *shx.PreparedCommand) { 205 pc.Args("copy", "--insecure-registry", "--sign-bundle", "--source", ref.String(), "--destination", copiedRef.String(), "--force") 206 }) 207 require.NoError(t, err, "Copy failed") 208 209 ref = toRefWithDigest(t, ref) 210 bundleImageRef := getBundleImageDigest(t, output, "sign") 211 212 _, output = testr.RequirePorter("install", "--verify-bundle", "--reference", copiedRef.String(), "--insecure-registry", "--force") 213 require.Contains(t, output, fmt.Sprintf("bundle signature verified for %s", copiedRef.String())) 214 require.Contains(t, output, fmt.Sprintf("bundle image signature verified for %s", bundleImageRef.String())) 215 } 216 217 func setupNotation(t *testing.T, testr tester.Tester) { 218 cmd := shx.Command("notation", "cert", "generate-test", "porter-test.org") 219 err := cmd.RunE() 220 require.NoError(t, err, "Generate notation certificate failed") 221 trustPolicy := ` 222 { 223 "version": "1.0", 224 "trustPolicies": [ 225 { 226 "name": "porter-test-images", 227 "registryScopes": [ "*" ], 228 "signatureVerification": { 229 "level" : "strict" 230 }, 231 "trustStores": [ "ca:porter-test.org" ], 232 "trustedIdentities": [ 233 "*" 234 ] 235 } 236 ] 237 }` 238 trustPolicyPath := filepath.Join(testr.PorterHomeDir, "trustpolicy.json") 239 err = os.WriteFile(trustPolicyPath, []byte(trustPolicy), 0644) 240 require.NoError(t, err, "Creation of trust policy failed") 241 err = shx.Command("notation", "policy", "import", trustPolicyPath).RunE() 242 require.NoError(t, err, "importing trust policy failed") 243 } 244 245 func cleanupNotation(t *testing.T) { 246 output, err := shx.Command("notation", "key", "ls").Output() 247 require.NoError(t, err) 248 keyRegex := regexp.MustCompile(`(/.+porter-test\.org\.key)`) 249 keyMatches := keyRegex.FindAllStringSubmatch(output, -1) 250 require.Len(t, keyMatches, 1) 251 crtRegex := regexp.MustCompile(`key\s+(/.+porter-test\.org\.crt)`) 252 crtMatches := crtRegex.FindAllStringSubmatch(output, -1) 253 require.Len(t, crtMatches, 1) 254 err = shx.Command("notation", "key", "delete", "porter-test.org").RunV() 255 require.NoError(t, err) 256 err = shx.Command("notation", "cert", "delete", "--type", "ca", "--store", "porter-test.org", "porter-test.org.crt", "--yes").RunV() 257 require.NoError(t, err) 258 err = os.Remove(keyMatches[0][1]) 259 require.NoError(t, err) 260 err = os.Remove(crtMatches[0][1]) 261 require.NoError(t, err) 262 } 263 264 func toRefWithDigest(t *testing.T, ref cnab.OCIReference) cnab.OCIReference { 265 desc, err := crane.Head(ref.String(), crane.Insecure) 266 require.NoError(t, err) 267 ref.Named = reference.TrimNamed(ref.Named) 268 ref, err = ref.WithDigest(digest.Digest(desc.Digest.String())) 269 require.NoError(t, err) 270 return ref 271 } 272 273 func resolveBundleImageDigest(t *testing.T, output string, imageName string) cnab.OCIReference { 274 r := regexp.MustCompile(fmt.Sprintf(`(?m:^Signing bundle image (localhost:\d+/%s:porter-[0-9a-z]+)\.)`, imageName)) 275 matches := r.FindAllStringSubmatch(output, -1) 276 require.Len(t, matches, 1) 277 invocationImageRefString := matches[0][1] 278 desc, err := crane.Head(invocationImageRefString, crane.Insecure) 279 require.NoError(t, err) 280 ref := cnab.MustParseOCIReference(invocationImageRefString) 281 ref.Named = reference.TrimNamed(ref.Named) 282 ref, err = ref.WithDigest(digest.Digest(desc.Digest.String())) 283 require.NoError(t, err) 284 return ref 285 } 286 287 func getBundleImageDigest(t *testing.T, output string, imageName string) cnab.OCIReference { 288 r := regexp.MustCompile(fmt.Sprintf(`(?m:^Signing bundle image (localhost:\d+/%s@sha256:[0-9a-z]+)\.)`, imageName)) 289 matches := r.FindAllStringSubmatch(output, -1) 290 require.Len(t, matches, 1) 291 invocationImageRefString := matches[0][1] 292 return cnab.MustParseOCIReference(invocationImageRefString) 293 }