1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
|
// Copyright 2024 The Update Framework Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"fmt"
stdlog "log"
"os"
"path/filepath"
"strings"
"github.com/go-logr/stdr"
"github.com/theupdateframework/go-tuf/v2/metadata"
"github.com/theupdateframework/go-tuf/v2/metadata/config"
"github.com/theupdateframework/go-tuf/v2/metadata/multirepo"
"github.com/theupdateframework/go-tuf/v2/metadata/updater"
)
const (
metadataURL = "https://raw.githubusercontent.com/theupdateframework/go-tuf/master/examples/multirepo/repository/metadata"
targetsURL = "https://raw.githubusercontent.com/theupdateframework/go-tuf/master/examples/multirepo/repository/targets"
verbosity = 4
)
func main() {
// set logger to stdout with info level
metadata.SetLogger(stdr.New(stdlog.New(os.Stdout, "multirepo_client_example", stdlog.LstdFlags)))
stdr.SetVerbosity(verbosity)
// Bootstrap TUF
fmt.Printf("Bootstrapping the initial TUF repo - fetching map.json file and necessary trusted root files\n\n")
mapBytes, trustedRoots, err := BootstrapTUF() // returns the map.json and the trusted root files
if err != nil {
panic(err)
}
// Initialize the multi-repository TUF client
fmt.Printf("Initializing the multi-repository TUF client with the given map.json file\n\n")
client, err := InitMultiRepoTUF(mapBytes, trustedRoots)
if err != nil {
panic(err)
}
// Refresh all repositories
fmt.Printf("Refreshing each TUF client (updating metadata/client update workflow)\n\n")
err = client.Refresh()
if err != nil {
panic(err)
}
// Get target info for the given target
fmt.Printf("Searching for a target using the multi-repository TUF client\n\n")
targetInfo, repositories, err := client.GetTargetInfo("rekor.pub") // rekor.pub trusted_root.json fulcio_v1.crt.pem
if err != nil {
panic(err)
}
// Download the target using that target info
fmt.Println("Downloading a target using the multi-repository TUF client")
_, _, err = client.DownloadTarget(repositories, targetInfo, "", "")
if err != nil {
panic(err)
}
}
// BootstrapTUF returns the map file and the related trusted root metadata files
func BootstrapTUF() ([]byte, map[string][]byte, error) {
log := metadata.GetLogger()
trustedRoots := map[string][]byte{}
mapBytes := []byte{}
// get working directory
cwd, err := os.Getwd()
if err != nil {
return nil, nil, fmt.Errorf("failed to get current working directory: %w", err)
}
targetsDir := filepath.Join(cwd, "bootstrap/targets")
// ensure the necessary folder layout
err = os.MkdirAll(targetsDir, os.ModePerm)
if err != nil {
return nil, nil, err
}
// read the trusted root metadata
rootBytes, err := os.ReadFile(filepath.Join(cwd, "root.json"))
if err != nil {
return nil, nil, err
}
// create updater configuration
cfg, err := config.New(metadataURL, rootBytes) // default config
if err != nil {
return nil, nil, err
}
cfg.LocalMetadataDir = filepath.Join(cwd, "bootstrap")
cfg.LocalTargetsDir = targetsDir
cfg.RemoteTargetsURL = targetsURL
// create a new Updater instance
up, err := updater.New(cfg)
if err != nil {
return nil, nil, fmt.Errorf("failed to create Updater instance: %w", err)
}
// build the top-level metadata
err = up.Refresh()
if err != nil {
return nil, nil, fmt.Errorf("failed to refresh trusted metadata: %w", err)
}
// download all target files
for name, targetInfo := range up.GetTopLevelTargets() {
// see if the target is already present locally
path, _, err := up.FindCachedTarget(targetInfo, "")
if err != nil {
return nil, nil, fmt.Errorf("failed while finding a cached target: %w", err)
}
if path != "" {
log.Info("Target is already present", "target", name, "path", path)
}
// target is not present locally, so let's try to download it
// keeping the same path layout as its target path
expectedTargetLocation := filepath.Join(targetsDir, name)
dirName, _ := filepath.Split(expectedTargetLocation)
err = os.MkdirAll(dirName, os.ModePerm)
if err != nil {
return nil, nil, err
}
// download targets (we don't have to actually store them other than for the sake of the example)
path, bytes, err := up.DownloadTarget(targetInfo, expectedTargetLocation, "")
if err != nil {
return nil, nil, fmt.Errorf("failed to download target file %s - %w", name, err)
}
// populate the return values
if name == "map.json" {
mapBytes = bytes
} else {
// Target names uses forwardslash even on Windows
repositoryName := strings.Split(name, "/")
trustedRoots[repositoryName[0]] = bytes
}
log.Info("Successfully downloaded target", "target", name, "path", path)
}
return mapBytes, trustedRoots, nil
}
func InitMultiRepoTUF(mapBytes []byte, trustedRoots map[string][]byte) (*multirepo.MultiRepoClient, error) {
// get working directory
cwd, err := os.Getwd()
if err != nil {
return nil, fmt.Errorf("failed to get current working directory: %w", err)
}
// create a new configuration for a multi-repository client
cfg, err := multirepo.NewConfig(mapBytes, trustedRoots)
if err != nil {
return nil, err
}
cfg.LocalMetadataDir = filepath.Join(cwd, "metadata")
cfg.LocalTargetsDir = filepath.Join(cwd, "download")
// create a new instance of a multi-repository TUF client
return multirepo.New(cfg)
}
|