-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(insecure): ensure builds are secure by default
BREAKING CHANGE: `insecure:` directive is now `true` by default. Previously, it was `false`. Fixes issue #311 Signed-off-by: Ramkumar Chinchani <rchincha@cisco.com>
- Loading branch information
Showing
5 changed files
with
277 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
package trust | ||
|
||
func VerifyCosign() { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
package trust | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
|
||
"sort" | ||
|
||
"github.com/docker/cli/cli/command/image" | ||
"github.com/docker/cli/cli/trust" | ||
"github.com/docker/distribution/reference" | ||
"github.com/docker/docker/registry" | ||
"github.com/sirupsen/logrus" | ||
"github.com/storageos/go-api/types" | ||
"github.com/theupdateframework/notary/client" | ||
"github.com/theupdateframework/notary/tuf/data" | ||
) | ||
|
||
// GetImageReferencesAndAuth retrieves the necessary reference and auth information for an image name | ||
// as an ImageRefAndAuth struct | ||
func GetImageReferencesAndAuth(ctx context.Context, rs registry.Service, | ||
authResolver func(ctx context.Context, index *registrytypes.IndexInfo) types.AuthConfig, | ||
imgName string, | ||
) (ImageRefAndAuth, error) { | ||
ref, err := reference.ParseNormalizedNamed(imgName) | ||
if err != nil { | ||
return ImageRefAndAuth{}, err | ||
} | ||
|
||
// Resolve the Repository name from fqn to RepositoryInfo | ||
var repoInfo *registry.RepositoryInfo | ||
if rs != nil { | ||
repoInfo, err = rs.ResolveRepository(ref) | ||
} else { | ||
repoInfo, err = registry.ParseRepositoryInfo(ref) | ||
} | ||
|
||
if err != nil { | ||
return ImageRefAndAuth{}, err | ||
} | ||
|
||
authConfig := authResolver(ctx, repoInfo.Index) | ||
return ImageRefAndAuth{ | ||
original: imgName, | ||
authConfig: &authConfig, | ||
reference: ref, | ||
repoInfo: repoInfo, | ||
tag: getTag(ref), | ||
digest: getDigest(ref), | ||
}, nil | ||
} | ||
|
||
// lookupTrustInfo returns processed signature and role information about a notary repository. | ||
// This information is to be pretty printed or serialized into a machine-readable format. | ||
func lookupTrustInfo(remote string) ([]trustTagRow, []client.RoleWithSignatures, []data.Role, error) { | ||
ctx := context.Background() | ||
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, nil, image.AuthResolver(cli), remote) | ||
if err != nil { | ||
return []trustTagRow{}, []client.RoleWithSignatures{}, []data.Role{}, err | ||
} | ||
tag := imgRefAndAuth.Tag() | ||
notaryRepo, err := cli.NotaryClient(imgRefAndAuth, trust.ActionsPullOnly) | ||
if err != nil { | ||
return []trustTagRow{}, []client.RoleWithSignatures{}, []data.Role{}, trust.NotaryError(imgRefAndAuth.Reference().Name(), err) | ||
} | ||
|
||
if err = clearChangeList(notaryRepo); err != nil { | ||
return []trustTagRow{}, []client.RoleWithSignatures{}, []data.Role{}, err | ||
} | ||
defer clearChangeList(notaryRepo) | ||
|
||
// Retrieve all released signatures, match them, and pretty print them | ||
allSignedTargets, err := notaryRepo.GetAllTargetMetadataByName(tag) | ||
if err != nil { | ||
logrus.Debug(trust.NotaryError(remote, err)) | ||
// print an empty table if we don't have signed targets, but have an initialized notary repo | ||
if _, ok := err.(client.ErrNoSuchTarget); !ok { | ||
return []trustTagRow{}, []client.RoleWithSignatures{}, []data.Role{}, fmt.Errorf("no signatures or cannot access %s", remote) | ||
} | ||
} | ||
signatureRows := matchReleasedSignatures(allSignedTargets) | ||
|
||
// get the administrative roles | ||
adminRolesWithSigs, err := notaryRepo.ListRoles() | ||
if err != nil { | ||
return []trustTagRow{}, []client.RoleWithSignatures{}, []data.Role{}, fmt.Errorf("no signers for %s", remote) | ||
} | ||
|
||
// get delegation roles with the canonical key IDs | ||
delegationRoles, err := notaryRepo.GetDelegationRoles() | ||
if err != nil { | ||
logrus.Debugf("no delegation roles found, or error fetching them for %s: %v", remote, err) | ||
} | ||
|
||
return signatureRows, adminRolesWithSigs, delegationRoles, nil | ||
} | ||
|
||
func getRepoTrustInfo(remote string) ([]byte, error) { | ||
signatureRows, adminRolesWithSigs, delegationRoles, err := lookupTrustInfo(cli, remote) | ||
if err != nil { | ||
return []byte{}, err | ||
} | ||
// process the signatures to include repo admin if signed by the base targets role | ||
for idx, sig := range signatureRows { | ||
if len(sig.Signers) == 0 { | ||
signatureRows[idx].Signers = append(sig.Signers, releasedRoleName) | ||
} | ||
} | ||
|
||
signerList, adminList := []trustSigner{}, []trustSigner{} | ||
|
||
signerRoleToKeyIDs := getDelegationRoleToKeyMap(delegationRoles) | ||
|
||
for signerName, signerKeys := range signerRoleToKeyIDs { | ||
signerKeyList := []trustKey{} | ||
for _, keyID := range signerKeys { | ||
signerKeyList = append(signerKeyList, trustKey{ID: keyID}) | ||
} | ||
signerList = append(signerList, trustSigner{signerName, signerKeyList}) | ||
} | ||
sort.Slice(signerList, func(i, j int) bool { return signerList[i].Name > signerList[j].Name }) | ||
|
||
for _, adminRole := range adminRolesWithSigs { | ||
switch adminRole.Name { | ||
case data.CanonicalRootRole: | ||
rootKeys := []trustKey{} | ||
for _, keyID := range adminRole.KeyIDs { | ||
rootKeys = append(rootKeys, trustKey{ID: keyID}) | ||
} | ||
adminList = append(adminList, trustSigner{"Root", rootKeys}) | ||
case data.CanonicalTargetsRole: | ||
targetKeys := []trustKey{} | ||
for _, keyID := range adminRole.KeyIDs { | ||
targetKeys = append(targetKeys, trustKey{ID: keyID}) | ||
} | ||
adminList = append(adminList, trustSigner{"Repository", targetKeys}) | ||
} | ||
} | ||
sort.Slice(adminList, func(i, j int) bool { return adminList[i].Name > adminList[j].Name }) | ||
|
||
return json.Marshal(trustRepo{ | ||
Name: remote, | ||
SignedTags: signatureRows, | ||
Signers: signerList, | ||
AdministrativeKeys: adminList, | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package trust | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"math" | ||
"os" | ||
"path" | ||
|
||
"github.com/notaryproject/notation-go" | ||
"github.com/notaryproject/notation-go/verifier" | ||
"oras.land/oras-go/v2/registry" | ||
"oras.land/oras-go/v2/registry/remote" | ||
"oras.land/oras-go/v2/registry/remote/auth" | ||
) | ||
|
||
func VerifyWithNotation(reference string, tdir string) error { | ||
// check if trustpolicy.json exists | ||
trustpolicyPath := path.Join(tdir, "notation/trustpolicy.json") | ||
|
||
if _, err := os.Stat(trustpolicyPath); errors.Is(err, os.ErrNotExist) { | ||
trustPolicy := ` | ||
{ | ||
"version": "1.0", | ||
"trustPolicies": [ | ||
{ | ||
"name": "good", | ||
"registryScopes": [ "*" ], | ||
"signatureVerification": { | ||
"level" : "audit" | ||
}, | ||
"trustStores": ["ca:good"], | ||
"trustedIdentities": [ | ||
"*" | ||
] | ||
} | ||
] | ||
}` | ||
|
||
file, err := os.Create(trustpolicyPath) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
defer file.Close() | ||
|
||
_, err = file.WriteString(trustPolicy) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
// start verifying signatures | ||
ctx := context.TODO() | ||
|
||
// getRepositoryClient | ||
authClient := &auth.Client{ | ||
Credential: func(ctx context.Context, reg string) (auth.Credential, error) { | ||
return auth.EmptyCredential, nil | ||
}, | ||
Cache: auth.NewCache(), | ||
ClientID: "notation", | ||
} | ||
|
||
authClient.SetUserAgent("notation/zot_tests") | ||
|
||
plainHTTP := true | ||
|
||
// Resolve referance | ||
ref, err := registry.ParseReference(reference) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
remoteRepo := &remote.Repository{ | ||
Client: authClient, | ||
Reference: ref, | ||
PlainHTTP: plainHTTP, | ||
} | ||
|
||
repo := notreg.NewRepository(remoteRepo) | ||
|
||
manifestDesc, err := repo.Resolve(ctx, ref.Reference) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if err := ref.ValidateReferenceAsDigest(); err != nil { | ||
ref.Reference = manifestDesc.Digest.String() | ||
} | ||
|
||
// getVerifier | ||
newVerifier, err := verifier.NewFromConfig() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
remoteRepo = &remote.Repository{ | ||
Client: authClient, | ||
Reference: ref, | ||
PlainHTTP: plainHTTP, | ||
} | ||
|
||
repo = notreg.NewRepository(remoteRepo) | ||
|
||
configs := map[string]string{} | ||
|
||
verifyOpts := notation.RemoteVerifyOptions{ | ||
ArtifactReference: ref.String(), | ||
PluginConfig: configs, | ||
MaxSignatureAttempts: math.MaxInt64, | ||
} | ||
|
||
_, outcomes, err := notation.Verify(ctx, newVerifier, repo, verifyOpts) | ||
if err != nil || len(outcomes) == 0 { | ||
return ErrSignatureVerification | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters