github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/trust/trust.go (about) 1 package trust 2 3 import ( 4 "bufio" 5 "bytes" 6 "encoding/base64" 7 "encoding/json" 8 "io/ioutil" 9 "os" 10 "os/exec" 11 "path/filepath" 12 "strings" 13 14 "github.com/containers/image/v5/types" 15 "github.com/ghodss/yaml" 16 "github.com/pkg/errors" 17 "github.com/sirupsen/logrus" 18 ) 19 20 // PolicyContent struct for policy.json file 21 type PolicyContent struct { 22 Default []RepoContent `json:"default"` 23 Transports TransportsContent `json:"transports"` 24 } 25 26 // RepoContent struct used under each repo 27 type RepoContent struct { 28 Type string `json:"type"` 29 KeyType string `json:"keyType,omitempty"` 30 KeyPath string `json:"keyPath,omitempty"` 31 KeyData string `json:"keyData,omitempty"` 32 SignedIdentity json.RawMessage `json:"signedIdentity,omitempty"` 33 } 34 35 // RepoMap map repo name to policycontent for each repo 36 type RepoMap map[string][]RepoContent 37 38 // TransportsContent struct for content under "transports" 39 type TransportsContent map[string]RepoMap 40 41 // RegistryConfiguration is one of the files in registriesDirPath configuring lookaside locations, or the result of merging them all. 42 // NOTE: Keep this in sync with docs/registries.d.md! 43 type RegistryConfiguration struct { 44 DefaultDocker *RegistryNamespace `json:"default-docker"` 45 // The key is a namespace, using fully-expanded Docker reference format or parent namespaces (per dockerReference.PolicyConfiguration*), 46 Docker map[string]RegistryNamespace `json:"docker"` 47 } 48 49 // RegistryNamespace defines lookaside locations for a single namespace. 50 type RegistryNamespace struct { 51 SigStore string `json:"sigstore"` // For reading, and if SigStoreStaging is not present, for writing. 52 SigStoreStaging string `json:"sigstore-staging"` // For writing only. 53 } 54 55 // ShowOutput keep the fields for image trust show command 56 type ShowOutput struct { 57 Repo string 58 Trusttype string 59 GPGid string 60 Sigstore string 61 } 62 63 // DefaultPolicyPath returns a path to the default policy of the system. 64 func DefaultPolicyPath(sys *types.SystemContext) string { 65 systemDefaultPolicyPath := "/etc/containers/policy.json" 66 if sys != nil { 67 if sys.SignaturePolicyPath != "" { 68 return sys.SignaturePolicyPath 69 } 70 if sys.RootForImplicitAbsolutePaths != "" { 71 return filepath.Join(sys.RootForImplicitAbsolutePaths, systemDefaultPolicyPath) 72 } 73 } 74 return systemDefaultPolicyPath 75 } 76 77 // RegistriesDirPath returns a path to registries.d 78 func RegistriesDirPath(sys *types.SystemContext) string { 79 systemRegistriesDirPath := "/etc/containers/registries.d" 80 if sys != nil { 81 if sys.RegistriesDirPath != "" { 82 return sys.RegistriesDirPath 83 } 84 if sys.RootForImplicitAbsolutePaths != "" { 85 return filepath.Join(sys.RootForImplicitAbsolutePaths, systemRegistriesDirPath) 86 } 87 } 88 return systemRegistriesDirPath 89 } 90 91 // LoadAndMergeConfig loads configuration files in dirPath 92 func LoadAndMergeConfig(dirPath string) (*RegistryConfiguration, error) { 93 mergedConfig := RegistryConfiguration{Docker: map[string]RegistryNamespace{}} 94 dockerDefaultMergedFrom := "" 95 nsMergedFrom := map[string]string{} 96 97 dir, err := os.Open(dirPath) 98 if err != nil { 99 if os.IsNotExist(err) { 100 return &mergedConfig, nil 101 } 102 return nil, err 103 } 104 configNames, err := dir.Readdirnames(0) 105 if err != nil { 106 return nil, err 107 } 108 for _, configName := range configNames { 109 if !strings.HasSuffix(configName, ".yaml") { 110 continue 111 } 112 configPath := filepath.Join(dirPath, configName) 113 configBytes, err := ioutil.ReadFile(configPath) 114 if err != nil { 115 return nil, err 116 } 117 var config RegistryConfiguration 118 err = yaml.Unmarshal(configBytes, &config) 119 if err != nil { 120 return nil, errors.Wrapf(err, "error parsing %s", configPath) 121 } 122 if config.DefaultDocker != nil { 123 if mergedConfig.DefaultDocker != nil { 124 return nil, errors.Errorf(`Error parsing signature storage configuration: "default-docker" defined both in "%s" and "%s"`, 125 dockerDefaultMergedFrom, configPath) 126 } 127 mergedConfig.DefaultDocker = config.DefaultDocker 128 dockerDefaultMergedFrom = configPath 129 } 130 for nsName, nsConfig := range config.Docker { // includes config.Docker == nil 131 if _, ok := mergedConfig.Docker[nsName]; ok { 132 return nil, errors.Errorf(`Error parsing signature storage configuration: "docker" namespace "%s" defined both in "%s" and "%s"`, 133 nsName, nsMergedFrom[nsName], configPath) 134 } 135 mergedConfig.Docker[nsName] = nsConfig 136 nsMergedFrom[nsName] = configPath 137 } 138 } 139 return &mergedConfig, nil 140 } 141 142 // HaveMatchRegistry checks if trust settings for the registry have been configured in yaml file 143 func HaveMatchRegistry(key string, registryConfigs *RegistryConfiguration) *RegistryNamespace { 144 searchKey := key 145 if !strings.Contains(searchKey, "/") { 146 val, exists := registryConfigs.Docker[searchKey] 147 if exists { 148 return &val 149 } 150 } 151 for range strings.Split(key, "/") { 152 val, exists := registryConfigs.Docker[searchKey] 153 if exists { 154 return &val 155 } 156 if strings.Contains(searchKey, "/") { 157 searchKey = searchKey[:strings.LastIndex(searchKey, "/")] 158 } 159 } 160 return registryConfigs.DefaultDocker 161 } 162 163 // CreateTmpFile creates a temp file under dir and writes the content into it 164 func CreateTmpFile(dir, pattern string, content []byte) (string, error) { 165 tmpfile, err := ioutil.TempFile(dir, pattern) 166 if err != nil { 167 return "", err 168 } 169 defer tmpfile.Close() 170 171 if _, err := tmpfile.Write(content); err != nil { 172 return "", err 173 174 } 175 return tmpfile.Name(), nil 176 } 177 178 // GetGPGIdFromKeyPath return user keyring from key path 179 func GetGPGIdFromKeyPath(path string) []string { 180 cmd := exec.Command("gpg2", "--with-colons", path) 181 results, err := cmd.Output() 182 if err != nil { 183 logrus.Errorf("error getting key identity: %s", err) 184 return nil 185 } 186 return parseUids(results) 187 } 188 189 // GetGPGIdFromKeyData return user keyring from keydata 190 func GetGPGIdFromKeyData(key string) []string { 191 decodeKey, err := base64.StdEncoding.DecodeString(key) 192 if err != nil { 193 logrus.Errorf("%s, error decoding key data", err) 194 return nil 195 } 196 tmpfileName, err := CreateTmpFile("", "", decodeKey) 197 if err != nil { 198 logrus.Errorf("error creating key date temp file %s", err) 199 } 200 defer os.Remove(tmpfileName) 201 return GetGPGIdFromKeyPath(tmpfileName) 202 } 203 204 func parseUids(colonDelimitKeys []byte) []string { 205 var parseduids []string 206 scanner := bufio.NewScanner(bytes.NewReader(colonDelimitKeys)) 207 for scanner.Scan() { 208 line := scanner.Text() 209 if strings.HasPrefix(line, "uid:") || strings.HasPrefix(line, "pub:") { 210 uid := strings.Split(line, ":")[9] 211 if uid == "" { 212 continue 213 } 214 parseduid := uid 215 if strings.Contains(uid, "<") && strings.Contains(uid, ">") { 216 parseduid = strings.SplitN(strings.SplitAfterN(uid, "<", 2)[1], ">", 2)[0] 217 } 218 parseduids = append(parseduids, parseduid) 219 } 220 } 221 return parseduids 222 } 223 224 // GetPolicy parse policy.json into PolicyContent struct 225 func GetPolicy(policyPath string) (PolicyContent, error) { 226 var policyContentStruct PolicyContent 227 policyContent, err := ioutil.ReadFile(policyPath) 228 if err != nil { 229 return policyContentStruct, errors.Wrap(err, "unable to read policy file") 230 } 231 if err := json.Unmarshal(policyContent, &policyContentStruct); err != nil { 232 return policyContentStruct, errors.Wrapf(err, "could not parse trust policies from %s", policyPath) 233 } 234 return policyContentStruct, nil 235 }