github.com/itscaro/cli@v0.0.0-20190705081621-c9db0fe93829/cli/command/trust/common.go (about) 1 package trust 2 3 import ( 4 "context" 5 "encoding/hex" 6 "fmt" 7 "sort" 8 "strings" 9 10 "github.com/docker/cli/cli/command" 11 "github.com/docker/cli/cli/command/image" 12 "github.com/docker/cli/cli/trust" 13 "github.com/sirupsen/logrus" 14 "github.com/theupdateframework/notary" 15 "github.com/theupdateframework/notary/client" 16 "github.com/theupdateframework/notary/tuf/data" 17 "vbom.ml/util/sortorder" 18 ) 19 20 // trustTagKey represents a unique signed tag and hex-encoded hash pair 21 type trustTagKey struct { 22 SignedTag string 23 Digest string 24 } 25 26 // trustTagRow encodes all human-consumable information for a signed tag, including signers 27 type trustTagRow struct { 28 trustTagKey 29 Signers []string 30 } 31 32 // trustRepo represents consumable information about a trusted repository 33 type trustRepo struct { 34 Name string 35 SignedTags []trustTagRow 36 Signers []trustSigner 37 AdministrativeKeys []trustSigner 38 } 39 40 // trustSigner represents a trusted signer in a trusted repository 41 // a signer is defined by a name and list of trustKeys 42 type trustSigner struct { 43 Name string `json:",omitempty"` 44 Keys []trustKey `json:",omitempty"` 45 } 46 47 // trustKey contains information about trusted keys 48 type trustKey struct { 49 ID string `json:",omitempty"` 50 } 51 52 // lookupTrustInfo returns processed signature and role information about a notary repository. 53 // This information is to be pretty printed or serialized into a machine-readable format. 54 func lookupTrustInfo(cli command.Cli, remote string) ([]trustTagRow, []client.RoleWithSignatures, []data.Role, error) { 55 ctx := context.Background() 56 imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, nil, image.AuthResolver(cli), remote) 57 if err != nil { 58 return []trustTagRow{}, []client.RoleWithSignatures{}, []data.Role{}, err 59 } 60 tag := imgRefAndAuth.Tag() 61 notaryRepo, err := cli.NotaryClient(imgRefAndAuth, trust.ActionsPullOnly) 62 if err != nil { 63 return []trustTagRow{}, []client.RoleWithSignatures{}, []data.Role{}, trust.NotaryError(imgRefAndAuth.Reference().Name(), err) 64 } 65 66 if err = clearChangeList(notaryRepo); err != nil { 67 return []trustTagRow{}, []client.RoleWithSignatures{}, []data.Role{}, err 68 } 69 defer clearChangeList(notaryRepo) 70 71 // Retrieve all released signatures, match them, and pretty print them 72 allSignedTargets, err := notaryRepo.GetAllTargetMetadataByName(tag) 73 if err != nil { 74 logrus.Debug(trust.NotaryError(remote, err)) 75 // print an empty table if we don't have signed targets, but have an initialized notary repo 76 if _, ok := err.(client.ErrNoSuchTarget); !ok { 77 return []trustTagRow{}, []client.RoleWithSignatures{}, []data.Role{}, fmt.Errorf("No signatures or cannot access %s", remote) 78 } 79 } 80 signatureRows := matchReleasedSignatures(allSignedTargets) 81 82 // get the administrative roles 83 adminRolesWithSigs, err := notaryRepo.ListRoles() 84 if err != nil { 85 return []trustTagRow{}, []client.RoleWithSignatures{}, []data.Role{}, fmt.Errorf("No signers for %s", remote) 86 } 87 88 // get delegation roles with the canonical key IDs 89 delegationRoles, err := notaryRepo.GetDelegationRoles() 90 if err != nil { 91 logrus.Debugf("no delegation roles found, or error fetching them for %s: %v", remote, err) 92 } 93 94 return signatureRows, adminRolesWithSigs, delegationRoles, nil 95 } 96 97 func formatAdminRole(roleWithSigs client.RoleWithSignatures) string { 98 adminKeyList := roleWithSigs.KeyIDs 99 sort.Strings(adminKeyList) 100 101 var role string 102 switch roleWithSigs.Name { 103 case data.CanonicalTargetsRole: 104 role = "Repository Key" 105 case data.CanonicalRootRole: 106 role = "Root Key" 107 default: 108 return "" 109 } 110 return fmt.Sprintf("%s:\t%s\n", role, strings.Join(adminKeyList, ", ")) 111 } 112 113 func getDelegationRoleToKeyMap(rawDelegationRoles []data.Role) map[string][]string { 114 signerRoleToKeyIDs := make(map[string][]string) 115 for _, delRole := range rawDelegationRoles { 116 switch delRole.Name { 117 case trust.ReleasesRole, data.CanonicalRootRole, data.CanonicalSnapshotRole, data.CanonicalTargetsRole, data.CanonicalTimestampRole: 118 continue 119 default: 120 signerRoleToKeyIDs[notaryRoleToSigner(delRole.Name)] = delRole.KeyIDs 121 } 122 } 123 return signerRoleToKeyIDs 124 } 125 126 // aggregate all signers for a "released" hash+tagname pair. To be "released," the tag must have been 127 // signed into the "targets" or "targets/releases" role. Output is sorted by tag name 128 func matchReleasedSignatures(allTargets []client.TargetSignedStruct) []trustTagRow { 129 signatureRows := []trustTagRow{} 130 // do a first pass to get filter on tags signed into "targets" or "targets/releases" 131 releasedTargetRows := map[trustTagKey][]string{} 132 for _, tgt := range allTargets { 133 if isReleasedTarget(tgt.Role.Name) { 134 releasedKey := trustTagKey{tgt.Target.Name, hex.EncodeToString(tgt.Target.Hashes[notary.SHA256])} 135 releasedTargetRows[releasedKey] = []string{} 136 } 137 } 138 139 // now fill out all signers on released keys 140 for _, tgt := range allTargets { 141 targetKey := trustTagKey{tgt.Target.Name, hex.EncodeToString(tgt.Target.Hashes[notary.SHA256])} 142 // only considered released targets 143 if _, ok := releasedTargetRows[targetKey]; ok && !isReleasedTarget(tgt.Role.Name) { 144 releasedTargetRows[targetKey] = append(releasedTargetRows[targetKey], notaryRoleToSigner(tgt.Role.Name)) 145 } 146 } 147 148 // compile the final output as a sorted slice 149 for targetKey, signers := range releasedTargetRows { 150 signatureRows = append(signatureRows, trustTagRow{targetKey, signers}) 151 } 152 sort.Slice(signatureRows, func(i, j int) bool { 153 return sortorder.NaturalLess(signatureRows[i].SignedTag, signatureRows[j].SignedTag) 154 }) 155 return signatureRows 156 }