mirror of https://github.com/containers/podman.git
				
				
				
			
		
			
				
	
	
		
			236 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			236 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Go
		
	
	
	
| package trust
 | |
| 
 | |
| import (
 | |
| 	"bufio"
 | |
| 	"bytes"
 | |
| 	"encoding/base64"
 | |
| 	"encoding/json"
 | |
| 	"io/ioutil"
 | |
| 	"os"
 | |
| 	"os/exec"
 | |
| 	"path/filepath"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/containers/image/v5/types"
 | |
| 	"github.com/pkg/errors"
 | |
| 	"github.com/sirupsen/logrus"
 | |
| 	"gopkg.in/yaml.v2"
 | |
| )
 | |
| 
 | |
| // PolicyContent struct for policy.json file
 | |
| type PolicyContent struct {
 | |
| 	Default    []RepoContent     `json:"default"`
 | |
| 	Transports TransportsContent `json:"transports"`
 | |
| }
 | |
| 
 | |
| // RepoContent struct used under each repo
 | |
| type RepoContent struct {
 | |
| 	Type           string          `json:"type"`
 | |
| 	KeyType        string          `json:"keyType,omitempty"`
 | |
| 	KeyPath        string          `json:"keyPath,omitempty"`
 | |
| 	KeyData        string          `json:"keyData,omitempty"`
 | |
| 	SignedIdentity json.RawMessage `json:"signedIdentity,omitempty"`
 | |
| }
 | |
| 
 | |
| // RepoMap map repo name to policycontent for each repo
 | |
| type RepoMap map[string][]RepoContent
 | |
| 
 | |
| // TransportsContent struct for content under "transports"
 | |
| type TransportsContent map[string]RepoMap
 | |
| 
 | |
| // RegistryConfiguration is one of the files in registriesDirPath configuring lookaside locations, or the result of merging them all.
 | |
| // NOTE: Keep this in sync with docs/registries.d.md!
 | |
| type RegistryConfiguration struct {
 | |
| 	DefaultDocker *RegistryNamespace `json:"default-docker"`
 | |
| 	// The key is a namespace, using fully-expanded Docker reference format or parent namespaces (per dockerReference.PolicyConfiguration*),
 | |
| 	Docker map[string]RegistryNamespace `json:"docker"`
 | |
| }
 | |
| 
 | |
| // RegistryNamespace defines lookaside locations for a single namespace.
 | |
| type RegistryNamespace struct {
 | |
| 	SigStore        string `json:"sigstore"`         // For reading, and if SigStoreStaging is not present, for writing.
 | |
| 	SigStoreStaging string `json:"sigstore-staging"` // For writing only.
 | |
| }
 | |
| 
 | |
| // ShowOutput keep the fields for image trust show command
 | |
| type ShowOutput struct {
 | |
| 	Repo      string
 | |
| 	Trusttype string
 | |
| 	GPGid     string
 | |
| 	Sigstore  string
 | |
| }
 | |
| 
 | |
| // DefaultPolicyPath returns a path to the default policy of the system.
 | |
| func DefaultPolicyPath(sys *types.SystemContext) string {
 | |
| 	systemDefaultPolicyPath := "/etc/containers/policy.json"
 | |
| 	if sys != nil {
 | |
| 		if sys.SignaturePolicyPath != "" {
 | |
| 			return sys.SignaturePolicyPath
 | |
| 		}
 | |
| 		if sys.RootForImplicitAbsolutePaths != "" {
 | |
| 			return filepath.Join(sys.RootForImplicitAbsolutePaths, systemDefaultPolicyPath)
 | |
| 		}
 | |
| 	}
 | |
| 	return systemDefaultPolicyPath
 | |
| }
 | |
| 
 | |
| // RegistriesDirPath returns a path to registries.d
 | |
| func RegistriesDirPath(sys *types.SystemContext) string {
 | |
| 	systemRegistriesDirPath := "/etc/containers/registries.d"
 | |
| 	if sys != nil {
 | |
| 		if sys.RegistriesDirPath != "" {
 | |
| 			return sys.RegistriesDirPath
 | |
| 		}
 | |
| 		if sys.RootForImplicitAbsolutePaths != "" {
 | |
| 			return filepath.Join(sys.RootForImplicitAbsolutePaths, systemRegistriesDirPath)
 | |
| 		}
 | |
| 	}
 | |
| 	return systemRegistriesDirPath
 | |
| }
 | |
| 
 | |
| // LoadAndMergeConfig loads configuration files in dirPath
 | |
| func LoadAndMergeConfig(dirPath string) (*RegistryConfiguration, error) {
 | |
| 	mergedConfig := RegistryConfiguration{Docker: map[string]RegistryNamespace{}}
 | |
| 	dockerDefaultMergedFrom := ""
 | |
| 	nsMergedFrom := map[string]string{}
 | |
| 
 | |
| 	dir, err := os.Open(dirPath)
 | |
| 	if err != nil {
 | |
| 		if os.IsNotExist(err) {
 | |
| 			return &mergedConfig, nil
 | |
| 		}
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	configNames, err := dir.Readdirnames(0)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	for _, configName := range configNames {
 | |
| 		if !strings.HasSuffix(configName, ".yaml") {
 | |
| 			continue
 | |
| 		}
 | |
| 		configPath := filepath.Join(dirPath, configName)
 | |
| 		configBytes, err := ioutil.ReadFile(configPath)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		var config RegistryConfiguration
 | |
| 		err = yaml.Unmarshal(configBytes, &config)
 | |
| 		if err != nil {
 | |
| 			return nil, errors.Wrapf(err, "Error parsing %s", configPath)
 | |
| 		}
 | |
| 		if config.DefaultDocker != nil {
 | |
| 			if mergedConfig.DefaultDocker != nil {
 | |
| 				return nil, errors.Errorf(`Error parsing signature storage configuration: "default-docker" defined both in "%s" and "%s"`,
 | |
| 					dockerDefaultMergedFrom, configPath)
 | |
| 			}
 | |
| 			mergedConfig.DefaultDocker = config.DefaultDocker
 | |
| 			dockerDefaultMergedFrom = configPath
 | |
| 		}
 | |
| 		for nsName, nsConfig := range config.Docker { // includes config.Docker == nil
 | |
| 			if _, ok := mergedConfig.Docker[nsName]; ok {
 | |
| 				return nil, errors.Errorf(`Error parsing signature storage configuration: "docker" namespace "%s" defined both in "%s" and "%s"`,
 | |
| 					nsName, nsMergedFrom[nsName], configPath)
 | |
| 			}
 | |
| 			mergedConfig.Docker[nsName] = nsConfig
 | |
| 			nsMergedFrom[nsName] = configPath
 | |
| 		}
 | |
| 	}
 | |
| 	return &mergedConfig, nil
 | |
| }
 | |
| 
 | |
| // HaveMatchRegistry checks if trust settings for the registry have been configured in yaml file
 | |
| func HaveMatchRegistry(key string, registryConfigs *RegistryConfiguration) *RegistryNamespace {
 | |
| 	searchKey := key
 | |
| 	if !strings.Contains(searchKey, "/") {
 | |
| 		val, exists := registryConfigs.Docker[searchKey]
 | |
| 		if exists {
 | |
| 			return &val
 | |
| 		}
 | |
| 	}
 | |
| 	for range strings.Split(key, "/") {
 | |
| 		val, exists := registryConfigs.Docker[searchKey]
 | |
| 		if exists {
 | |
| 			return &val
 | |
| 		}
 | |
| 		if strings.Contains(searchKey, "/") {
 | |
| 			searchKey = searchKey[:strings.LastIndex(searchKey, "/")]
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // CreateTmpFile creates a temp file under dir and writes the content into it
 | |
| func CreateTmpFile(dir, pattern string, content []byte) (string, error) {
 | |
| 	tmpfile, err := ioutil.TempFile(dir, pattern)
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	defer tmpfile.Close()
 | |
| 
 | |
| 	if _, err := tmpfile.Write(content); err != nil {
 | |
| 		return "", err
 | |
| 
 | |
| 	}
 | |
| 	return tmpfile.Name(), nil
 | |
| }
 | |
| 
 | |
| // GetGPGIdFromKeyPath return user keyring from key path
 | |
| func GetGPGIdFromKeyPath(path string) []string {
 | |
| 	cmd := exec.Command("gpg2", "--with-colons", path)
 | |
| 	results, err := cmd.Output()
 | |
| 	if err != nil {
 | |
| 		logrus.Errorf("error getting key identity: %s", err)
 | |
| 		return nil
 | |
| 	}
 | |
| 	return parseUids(results)
 | |
| }
 | |
| 
 | |
| // GetGPGIdFromKeyData return user keyring from keydata
 | |
| func GetGPGIdFromKeyData(key string) []string {
 | |
| 	decodeKey, err := base64.StdEncoding.DecodeString(key)
 | |
| 	if err != nil {
 | |
| 		logrus.Errorf("%s, error decoding key data", err)
 | |
| 		return nil
 | |
| 	}
 | |
| 	tmpfileName, err := CreateTmpFile("", "", decodeKey)
 | |
| 	if err != nil {
 | |
| 		logrus.Errorf("error creating key date temp file %s", err)
 | |
| 	}
 | |
| 	defer os.Remove(tmpfileName)
 | |
| 	return GetGPGIdFromKeyPath(tmpfileName)
 | |
| }
 | |
| 
 | |
| func parseUids(colonDelimitKeys []byte) []string {
 | |
| 	var parseduids []string
 | |
| 	scanner := bufio.NewScanner(bytes.NewReader(colonDelimitKeys))
 | |
| 	for scanner.Scan() {
 | |
| 		line := scanner.Text()
 | |
| 		if strings.HasPrefix(line, "uid:") || strings.HasPrefix(line, "pub:") {
 | |
| 			uid := strings.Split(line, ":")[9]
 | |
| 			if uid == "" {
 | |
| 				continue
 | |
| 			}
 | |
| 			parseduid := uid
 | |
| 			if strings.Contains(uid, "<") && strings.Contains(uid, ">") {
 | |
| 				parseduid = strings.SplitN(strings.SplitAfterN(uid, "<", 2)[1], ">", 2)[0]
 | |
| 			}
 | |
| 			parseduids = append(parseduids, parseduid)
 | |
| 		}
 | |
| 	}
 | |
| 	return parseduids
 | |
| }
 | |
| 
 | |
| // GetPolicy parse policy.json into PolicyContent struct
 | |
| func GetPolicy(policyPath string) (PolicyContent, error) {
 | |
| 	var policyContentStruct PolicyContent
 | |
| 	policyContent, err := ioutil.ReadFile(policyPath)
 | |
| 	if err != nil {
 | |
| 		return policyContentStruct, errors.Wrapf(err, "unable to read policy file %s", policyPath)
 | |
| 	}
 | |
| 	if err := json.Unmarshal(policyContent, &policyContentStruct); err != nil {
 | |
| 		return policyContentStruct, errors.Wrapf(err, "could not parse trust policies")
 | |
| 	}
 | |
| 	return policyContentStruct, nil
 | |
| }
 |