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