forked from goharbor/harbor
-
Notifications
You must be signed in to change notification settings - Fork 0
/
helper.go
134 lines (122 loc) · 4.51 KB
/
helper.go
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
// Copyright Project Harbor 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.
package notary
import (
"encoding/hex"
"fmt"
"net/http"
"os"
"path"
"strings"
"github.com/docker/distribution/registry/auth/token"
"github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/common/utils/registry"
"github.com/goharbor/harbor/src/core/config"
tokenutil "github.com/goharbor/harbor/src/core/service/token"
"github.com/theupdateframework/notary"
"github.com/theupdateframework/notary/client"
"github.com/theupdateframework/notary/trustpinning"
"github.com/theupdateframework/notary/tuf/data"
digest "github.com/opencontainers/go-digest"
)
var (
notaryCachePath = "/tmp/notary-cache"
trustPin trustpinning.TrustPinConfig
mockRetriever notary.PassRetriever
)
// Target represents the json object of a target of a docker image in notary.
// The struct will be used when repository is know so it won'g contain the name of a repository.
type Target struct {
Tag string `json:"tag"`
Hashes data.Hashes `json:"hashes"`
// TODO: update fields as needed.
}
func init() {
mockRetriever = func(keyName, alias string, createNew bool, attempts int) (passphrase string, giveup bool, err error) {
passphrase = "hardcode"
giveup = false
err = nil
return
}
trustPin = trustpinning.TrustPinConfig{}
}
// GetInternalTargets wraps GetTargets to read config values for getting full-qualified repo from internal notary instance.
func GetInternalTargets(notaryEndpoint string, username string, repo string) ([]Target, error) {
ext, err := config.ExtEndpoint()
if err != nil {
log.Errorf("Error while reading external endpoint: %v", err)
return nil, err
}
endpoint := strings.Split(ext, "//")[1]
fqRepo := path.Join(endpoint, repo)
return GetTargets(notaryEndpoint, username, fqRepo)
}
// GetTargets is a help function called by API to fetch signature information of a given repository.
// Per docker's convention the repository should contain the information of endpoint, i.e. it should look
// like "192.168.0.1/library/ubuntu", instead of "library/ubuntu" (fqRepo for fully-qualified repo)
func GetTargets(notaryEndpoint string, username string, fqRepo string) ([]Target, error) {
res := []Target{}
t, err := tokenutil.MakeToken(username, tokenutil.Notary,
[]*token.ResourceActions{
{
Type: "repository",
Name: fqRepo,
Actions: []string{"pull"},
}})
if err != nil {
return nil, err
}
authorizer := ¬aryAuthorizer{
token: t.Token,
}
tr := registry.NewTransport(registry.GetHTTPTransport(), authorizer)
gun := data.GUN(fqRepo)
notaryRepo, err := client.NewFileCachedRepository(notaryCachePath, gun, notaryEndpoint, tr, mockRetriever, trustPin)
if err != nil {
return res, err
}
targets, err := notaryRepo.ListTargets(data.CanonicalTargetsRole)
if _, ok := err.(client.ErrRepositoryNotExist); ok {
log.Errorf("Repository not exist, repo: %s, error: %v, returning empty signature", fqRepo, err)
return res, nil
} else if err != nil {
return res, err
}
// Remove root.json such that when remote repository is removed the local cache can't be reused.
rootJSON := path.Join(notaryCachePath, "tuf", fqRepo, "metadata/root.json")
rmErr := os.Remove(rootJSON)
if rmErr != nil {
log.Warningf("Failed to clear cached root.json: %s, error: %v, when repo is removed from notary the signature status maybe incorrect", rootJSON, rmErr)
}
for _, t := range targets {
res = append(res, Target{t.Name, t.Hashes})
}
return res, nil
}
// DigestFromTarget get a target and return the value of digest, in accordance to Docker-Content-Digest
func DigestFromTarget(t Target) (string, error) {
sha, ok := t.Hashes["sha256"]
if !ok {
return "", fmt.Errorf("no valid hash, expecting sha256")
}
return digest.NewDigestFromHex("sha256", hex.EncodeToString(sha)).String(), nil
}
type notaryAuthorizer struct {
token string
}
func (n *notaryAuthorizer) Modify(req *http.Request) error {
req.Header.Add(http.CanonicalHeaderKey("Authorization"),
fmt.Sprintf("Bearer %s", n.token))
return nil
}