зеркало из https://github.com/microsoft/docker.git
Merge pull request #19760 from cyli/re-vendor-notary
Re-vendor Notary and docker/go
This commit is contained in:
Коммит
c39c7e6edf
|
@ -167,7 +167,7 @@ RUN set -x \
|
|||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install notary server
|
||||
ENV NOTARY_VERSION docker-v1.10-3
|
||||
ENV NOTARY_VERSION docker-v1.10-4
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||
|
|
|
@ -110,11 +110,11 @@ RUN set -x \
|
|||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install notary server
|
||||
ENV NOTARY_COMMIT 0c11a970826e62479379ccc75a45184460b9200f
|
||||
ENV NOTARY_VERSION docker-v1.10-4
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||
&& (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_COMMIT") \
|
||||
&& (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_VERSION") \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/Godeps/_workspace:$GOPATH" \
|
||||
go build -o /usr/local/bin/notary-server github.com/docker/notary/cmd/notary-server \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
|
|
@ -144,7 +144,7 @@ RUN set -x \
|
|||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install notary server
|
||||
ENV NOTARY_VERSION docker-v1.10-2
|
||||
ENV NOTARY_VERSION docker-v1.10-4
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||
|
|
|
@ -123,11 +123,11 @@ RUN set -x \
|
|||
|
||||
# TODO update this when we upgrade to Go 1.5.1+
|
||||
# Install notary server
|
||||
#ENV NOTARY_COMMIT 8e8122eb5528f621afcd4e2854c47302f17392f7
|
||||
#ENV NOTARY_VERSION docker-v1.10-4
|
||||
#RUN set -x \
|
||||
# && export GOPATH="$(mktemp -d)" \
|
||||
# && git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||
# && (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_COMMIT") \
|
||||
# && (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_VERSION") \
|
||||
# && GOPATH="$GOPATH/src/github.com/docker/notary/Godeps/_workspace:$GOPATH" \
|
||||
# go build -o /usr/local/bin/notary-server github.com/docker/notary/cmd/notary-server \
|
||||
# && rm -rf "$GOPATH"
|
||||
|
|
|
@ -116,11 +116,11 @@ RUN set -x \
|
|||
&& rm -rf "$GOPATH"
|
||||
|
||||
# Install notary server
|
||||
ENV NOTARY_COMMIT 8e8122eb5528f621afcd4e2854c47302f17392f7
|
||||
ENV NOTARY_VERSION docker-v1.10-4
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||
&& (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_COMMIT") \
|
||||
&& (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_VERSION") \
|
||||
&& GOPATH="$GOPATH/src/github.com/docker/notary/Godeps/_workspace:$GOPATH" \
|
||||
go build -o /usr/local/bin/notary-server github.com/docker/notary/cmd/notary-server \
|
||||
&& rm -rf "$GOPATH"
|
||||
|
|
|
@ -50,11 +50,11 @@ clone git github.com/docker/distribution c301f8ab27f4913c968b8d73a38e5dda79b9d3d
|
|||
clone git github.com/vbatts/tar-split v0.9.11
|
||||
|
||||
# get desired notary commit, might also need to be updated in Dockerfile
|
||||
clone git github.com/docker/notary docker-v1.10-3
|
||||
clone git github.com/docker/notary docker-v1.10-4
|
||||
|
||||
clone git google.golang.org/grpc 174192fc93efcb188fc8f46ca447f0da606b6885 https://github.com/grpc/grpc-go.git
|
||||
clone git github.com/miekg/pkcs11 80f102b5cac759de406949c47f0928b99bd64cdf
|
||||
clone git github.com/jfrazelle/go v1.5.1-1
|
||||
clone git github.com/docker/go v1.5.1-1-1-gbaf439e
|
||||
clone git github.com/agl/ed25519 d2b94fd789ea21d12fac1a4443dd3a3f79cda72c
|
||||
|
||||
clone git github.com/opencontainers/runc 3d8a20bb772defc28c355534d83486416d1719b4 # libcontainer
|
||||
|
|
|
@ -301,18 +301,19 @@ func (s *DockerTrustSuite) TestTrustedCreate(c *check.C) {
|
|||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestUntrustedCreate(c *check.C) {
|
||||
repoName := fmt.Sprintf("%v/dockercli/trusted:latest", privateRegistryURL)
|
||||
repoName := fmt.Sprintf("%v/dockercliuntrusted/createtest", privateRegistryURL)
|
||||
withTagName := fmt.Sprintf("%s:latest", repoName)
|
||||
// tag the image and upload it to the private registry
|
||||
dockerCmd(c, "tag", "busybox", repoName)
|
||||
dockerCmd(c, "push", repoName)
|
||||
dockerCmd(c, "rmi", repoName)
|
||||
dockerCmd(c, "tag", "busybox", withTagName)
|
||||
dockerCmd(c, "push", withTagName)
|
||||
dockerCmd(c, "rmi", withTagName)
|
||||
|
||||
// Try trusted create on untrusted tag
|
||||
createCmd := exec.Command(dockerBinary, "create", repoName)
|
||||
createCmd := exec.Command(dockerBinary, "create", withTagName)
|
||||
s.trustedCmd(createCmd)
|
||||
out, _, err := runCommandWithOutput(createCmd)
|
||||
c.Assert(err, check.Not(check.IsNil))
|
||||
c.Assert(string(out), checker.Contains, "trust data unavailable. Has a notary repository been initialized?", check.Commentf("Missing expected output on trusted create:\n%s", out))
|
||||
c.Assert(string(out), checker.Contains, fmt.Sprintf("does not have trust data for %s", repoName), check.Commentf("Missing expected output on trusted create:\n%s", out))
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ func (s *DockerTrustSuite) TestTrustedIsolatedPull(c *check.C) {
|
|||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestUntrustedPull(c *check.C) {
|
||||
repoName := fmt.Sprintf("%v/dockercli/trusted:latest", privateRegistryURL)
|
||||
repoName := fmt.Sprintf("%v/dockercliuntrusted/pulltest:latest", privateRegistryURL)
|
||||
// tag the image and upload it to the private registry
|
||||
dockerCmd(c, "tag", "busybox", repoName)
|
||||
dockerCmd(c, "push", repoName)
|
||||
|
|
|
@ -215,7 +215,7 @@ func (s *DockerSchema1RegistrySuite) TestCrossRepositoryLayerPushNotSupported(c
|
|||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedPush(c *check.C) {
|
||||
repoName := fmt.Sprintf("%v/dockercli/trusted:latest", privateRegistryURL)
|
||||
repoName := fmt.Sprintf("%v/dockerclitrusted/pushtest:latest", privateRegistryURL)
|
||||
// tag the image and upload it to the private registry
|
||||
dockerCmd(c, "tag", "busybox", repoName)
|
||||
|
||||
|
@ -267,7 +267,7 @@ func (s *DockerTrustSuite) TestTrustedPushWithDeprecatedEnvPasswords(c *check.C)
|
|||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedPushWithFailingServer(c *check.C) {
|
||||
repoName := fmt.Sprintf("%v/dockercli/trusted:latest", privateRegistryURL)
|
||||
repoName := fmt.Sprintf("%v/dockerclitrusted/failingserver:latest", privateRegistryURL)
|
||||
// tag the image and upload it to the private registry
|
||||
dockerCmd(c, "tag", "busybox", repoName)
|
||||
|
||||
|
@ -279,7 +279,7 @@ func (s *DockerTrustSuite) TestTrustedPushWithFailingServer(c *check.C) {
|
|||
}
|
||||
|
||||
func (s *DockerTrustSuite) TestTrustedPushWithoutServerAndUntrusted(c *check.C) {
|
||||
repoName := fmt.Sprintf("%v/dockercli/trusted:latest", privateRegistryURL)
|
||||
repoName := fmt.Sprintf("%v/dockerclitrusted/trustedandnot:latest", privateRegistryURL)
|
||||
// tag the image and upload it to the private registry
|
||||
dockerCmd(c, "tag", "busybox", repoName)
|
||||
|
||||
|
|
|
@ -3179,7 +3179,7 @@ func (s *DockerTrustSuite) TestTrustedRun(c *check.C) {
|
|||
func (s *DockerTrustSuite) TestUntrustedRun(c *check.C) {
|
||||
// Windows does not support this functionality
|
||||
testRequires(c, DaemonIsLinux)
|
||||
repoName := fmt.Sprintf("%v/dockercli/trusted:latest", privateRegistryURL)
|
||||
repoName := fmt.Sprintf("%v/dockercliuntrusted/runtest:latest", privateRegistryURL)
|
||||
// tag the image and upload it to the private registry
|
||||
dockerCmd(c, "tag", "busybox", repoName)
|
||||
dockerCmd(c, "push", repoName)
|
||||
|
|
|
@ -118,12 +118,13 @@ protos:
|
|||
# be run first
|
||||
|
||||
define gocover
|
||||
$(GO_EXC) test $(OPTS) $(TESTOPTS) -covermode="$(COVERMODE)" -coverprofile="$(COVERDIR)/$(subst /,-,$(1)).$(subst $(_space),.,$(NOTARY_BUILDTAGS)).cover" "$(1)" || exit 1;
|
||||
$(GO_EXC) test $(OPTS) $(TESTOPTS) -covermode="$(COVERMODE)" -coverprofile="$(COVERDIR)/$(subst /,-,$(1)).$(subst $(_space),.,$(NOTARY_BUILDTAGS)).coverage.txt" "$(1)" || exit 1;
|
||||
endef
|
||||
|
||||
gen-cover: go_version
|
||||
@mkdir -p "$(COVERDIR)"
|
||||
$(foreach PKG,$(PKGS),$(call gocover,$(PKG)))
|
||||
rm "$(COVERDIR)"/*testutils*.coverage.txt
|
||||
|
||||
# Generates the cover binaries and runs them all in serial, so this can be used
|
||||
# run all tests with a yubikey without any problems
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
|
@ -13,14 +12,6 @@ import (
|
|||
"github.com/docker/notary/tuf/signed"
|
||||
)
|
||||
|
||||
// Manager is an abstraction around trusted root CA stores
|
||||
type Manager struct {
|
||||
trustedCAStore trustmanager.X509Store
|
||||
trustedCertificateStore trustmanager.X509Store
|
||||
}
|
||||
|
||||
const trustDir = "trusted_certificates"
|
||||
|
||||
// ErrValidationFail is returned when there is no valid trusted certificates
|
||||
// being served inside of the roots.json
|
||||
type ErrValidationFail struct {
|
||||
|
@ -45,63 +36,6 @@ func (err ErrRootRotationFail) Error() string {
|
|||
return fmt.Sprintf("could not rotate trust to a new trusted root: %s", err.Reason)
|
||||
}
|
||||
|
||||
// NewManager returns an initialized Manager, or an error
|
||||
// if it fails to load certificates
|
||||
func NewManager(baseDir string) (*Manager, error) {
|
||||
trustPath := filepath.Join(baseDir, trustDir)
|
||||
|
||||
// Load all CAs that aren't expired and don't use SHA1
|
||||
trustedCAStore, err := trustmanager.NewX509FilteredFileStore(trustPath, func(cert *x509.Certificate) bool {
|
||||
return cert.IsCA && cert.BasicConstraintsValid && cert.SubjectKeyId != nil &&
|
||||
time.Now().Before(cert.NotAfter) &&
|
||||
cert.SignatureAlgorithm != x509.SHA1WithRSA &&
|
||||
cert.SignatureAlgorithm != x509.DSAWithSHA1 &&
|
||||
cert.SignatureAlgorithm != x509.ECDSAWithSHA1
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Load all individual (non-CA) certificates that aren't expired and don't use SHA1
|
||||
trustedCertificateStore, err := trustmanager.NewX509FilteredFileStore(trustPath, func(cert *x509.Certificate) bool {
|
||||
return !cert.IsCA &&
|
||||
time.Now().Before(cert.NotAfter) &&
|
||||
cert.SignatureAlgorithm != x509.SHA1WithRSA &&
|
||||
cert.SignatureAlgorithm != x509.DSAWithSHA1 &&
|
||||
cert.SignatureAlgorithm != x509.ECDSAWithSHA1
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Manager{
|
||||
trustedCAStore: trustedCAStore,
|
||||
trustedCertificateStore: trustedCertificateStore,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// TrustedCertificateStore returns the trusted certificate store being managed
|
||||
// by this Manager
|
||||
func (m *Manager) TrustedCertificateStore() trustmanager.X509Store {
|
||||
return m.trustedCertificateStore
|
||||
}
|
||||
|
||||
// TrustedCAStore returns the CA store being managed by this Manager
|
||||
func (m *Manager) TrustedCAStore() trustmanager.X509Store {
|
||||
return m.trustedCAStore
|
||||
}
|
||||
|
||||
// AddTrustedCert adds a cert to the trusted certificate store (not the CA
|
||||
// store)
|
||||
func (m *Manager) AddTrustedCert(cert *x509.Certificate) {
|
||||
m.trustedCertificateStore.AddCert(cert)
|
||||
}
|
||||
|
||||
// AddTrustedCACert adds a cert to the trusted CA certificate store
|
||||
func (m *Manager) AddTrustedCACert(cert *x509.Certificate) {
|
||||
m.trustedCAStore.AddCert(cert)
|
||||
}
|
||||
|
||||
/*
|
||||
ValidateRoot receives a new root, validates its correctness and attempts to
|
||||
do root key rotation if needed.
|
||||
|
@ -111,7 +45,7 @@ that list is non-empty means that we've already seen this repository before, and
|
|||
have a list of trusted certificates for it. In this case, we use this list of
|
||||
certificates to attempt to validate this root file.
|
||||
|
||||
If the previous validation suceeds, or in the case where we found no trusted
|
||||
If the previous validation succeeds, or in the case where we found no trusted
|
||||
certificates for this particular GUN, we check the integrity of the root by
|
||||
making sure that it is validated by itself. This means that we will attempt to
|
||||
validate the root data with the certificates that are included in the root keys
|
||||
|
@ -129,7 +63,7 @@ we are using the current public PKI to validate the first download of the certif
|
|||
adding an extra layer of security over the normal (SSH style) trust model.
|
||||
We shall call this: TOFUS.
|
||||
*/
|
||||
func (m *Manager) ValidateRoot(root *data.Signed, gun string) error {
|
||||
func ValidateRoot(certStore trustmanager.X509Store, root *data.Signed, gun string) error {
|
||||
logrus.Debugf("entered ValidateRoot with dns: %s", gun)
|
||||
signedRoot, err := data.RootFromSigned(root)
|
||||
if err != nil {
|
||||
|
@ -144,7 +78,7 @@ func (m *Manager) ValidateRoot(root *data.Signed, gun string) error {
|
|||
}
|
||||
|
||||
// Retrieve all the trusted certificates that match this gun
|
||||
certsForCN, err := m.trustedCertificateStore.GetCertificatesByCN(gun)
|
||||
certsForCN, err := certStore.GetCertificatesByCN(gun)
|
||||
if err != nil {
|
||||
// If the error that we get back is different than ErrNoCertificatesFound
|
||||
// we couldn't check if there are any certificates with this CN already
|
||||
|
@ -183,7 +117,7 @@ func (m *Manager) ValidateRoot(root *data.Signed, gun string) error {
|
|||
// Do root certificate rotation: we trust only the certs present in the new root
|
||||
// First we add all the new certificates (even if they already exist)
|
||||
for _, cert := range allValidCerts {
|
||||
err := m.trustedCertificateStore.AddCert(cert)
|
||||
err := certStore.AddCert(cert)
|
||||
if err != nil {
|
||||
// If the error is already exists we don't fail the rotation
|
||||
if _, ok := err.(*trustmanager.ErrCertExists); ok {
|
||||
|
@ -197,7 +131,7 @@ func (m *Manager) ValidateRoot(root *data.Signed, gun string) error {
|
|||
// Now we delete old certificates that aren't present in the new root
|
||||
for certID, cert := range certsToRemove(certsForCN, allValidCerts) {
|
||||
logrus.Debugf("removing certificate with certID: %s", certID)
|
||||
err = m.trustedCertificateStore.RemoveCert(cert)
|
||||
err = certStore.RemoveCert(cert)
|
||||
if err != nil {
|
||||
logrus.Debugf("failed to remove trusted certificate with keyID: %s, %v", certID, err)
|
||||
return &ErrRootRotationFail{Reason: "failed to rotate root keys"}
|
||||
|
@ -208,7 +142,7 @@ func (m *Manager) ValidateRoot(root *data.Signed, gun string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// validRootLeafCerts returns a list of non-exipired, non-sha1 certificates whoose
|
||||
// validRootLeafCerts returns a list of non-exipired, non-sha1 certificates whose
|
||||
// Common-Names match the provided GUN
|
||||
func validRootLeafCerts(root *data.SignedRoot, gun string) ([]*x509.Certificate, error) {
|
||||
// Get a list of all of the leaf certificates present in root
|
||||
|
@ -219,7 +153,8 @@ func validRootLeafCerts(root *data.SignedRoot, gun string) ([]*x509.Certificate,
|
|||
for _, cert := range allLeafCerts {
|
||||
// Validate that this leaf certificate has a CN that matches the exact gun
|
||||
if cert.Subject.CommonName != gun {
|
||||
logrus.Debugf("error leaf certificate CN: %s doesn't match the given GUN: %s", cert.Subject.CommonName)
|
||||
logrus.Debugf("error leaf certificate CN: %s doesn't match the given GUN: %s",
|
||||
cert.Subject.CommonName, gun)
|
||||
continue
|
||||
}
|
||||
// Make sure the certificate is not expired
|
|
@ -18,6 +18,8 @@ machine:
|
|||
CIRCLE_PAIN: "mode: set"
|
||||
# Put the coverage profile somewhere codecov's script can find it
|
||||
COVERPROFILE: coverage.out
|
||||
# Set the pull request number so codecov can figure it out
|
||||
PULL_REQUEST: ${CI_PULL_REQUEST##*/}
|
||||
|
||||
hosts:
|
||||
# Not used yet
|
||||
|
@ -40,8 +42,7 @@ dependencies:
|
|||
# For the stable go version, additionally install linting tools
|
||||
- >
|
||||
gvm use stable &&
|
||||
go get github.com/golang/lint/golint github.com/wadey/gocovmerge &&
|
||||
go install github.com/wadey/gocovmerge
|
||||
go get github.com/golang/lint/golint
|
||||
test:
|
||||
pre:
|
||||
# Output the go versions we are going to test
|
||||
|
@ -72,11 +73,6 @@ test:
|
|||
pwd: $BASE_STABLE
|
||||
|
||||
post:
|
||||
- gvm use stable && make covmerge:
|
||||
timeout: 600
|
||||
parallel: true
|
||||
pwd: $BASE_STABLE
|
||||
|
||||
# Report to codecov.io
|
||||
- bash <(curl -s https://codecov.io/bash):
|
||||
parallel: true
|
||||
|
|
|
@ -9,10 +9,10 @@ import (
|
|||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/notary"
|
||||
"github.com/docker/notary/certs"
|
||||
"github.com/docker/notary/client/changelist"
|
||||
"github.com/docker/notary/cryptoservice"
|
||||
|
@ -53,9 +53,9 @@ type ErrInvalidRemoteRole struct {
|
|||
Role string
|
||||
}
|
||||
|
||||
func (e ErrInvalidRemoteRole) Error() string {
|
||||
func (err ErrInvalidRemoteRole) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"notary does not support the server managing the %s key", e.Role)
|
||||
"notary does not support the server managing the %s key", err.Role)
|
||||
}
|
||||
|
||||
// ErrRepositoryNotExist is returned when an action is taken on a remote
|
||||
|
@ -84,7 +84,7 @@ type NotaryRepository struct {
|
|||
CryptoService signed.CryptoService
|
||||
tufRepo *tuf.Repo
|
||||
roundTrip http.RoundTripper
|
||||
CertManager *certs.Manager
|
||||
CertStore trustmanager.X509Store
|
||||
}
|
||||
|
||||
// repositoryFromKeystores is a helper function for NewNotaryRepository that
|
||||
|
@ -93,7 +93,11 @@ type NotaryRepository struct {
|
|||
func repositoryFromKeystores(baseDir, gun, baseURL string, rt http.RoundTripper,
|
||||
keyStores []trustmanager.KeyStore) (*NotaryRepository, error) {
|
||||
|
||||
certManager, err := certs.NewManager(baseDir)
|
||||
certPath := filepath.Join(baseDir, notary.TrustedCertsDir)
|
||||
certStore, err := trustmanager.NewX509FilteredFileStore(
|
||||
certPath,
|
||||
trustmanager.FilterCertsExpiredSha1,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -107,7 +111,7 @@ func repositoryFromKeystores(baseDir, gun, baseURL string, rt http.RoundTripper,
|
|||
tufRepoPath: filepath.Join(baseDir, tufDir, filepath.FromSlash(gun)),
|
||||
CryptoService: cryptoService,
|
||||
roundTrip: rt,
|
||||
CertManager: certManager,
|
||||
CertStore: certStore,
|
||||
}
|
||||
|
||||
fileStore, err := store.NewFilesystemStore(
|
||||
|
@ -165,7 +169,7 @@ func (r *NotaryRepository) Initialize(rootKeyID string, serverManagedRoles ...st
|
|||
// currently we only support server managing timestamps and snapshots, and
|
||||
// nothing else - timestamps are always managed by the server, and implicit
|
||||
// (do not have to be passed in as part of `serverManagedRoles`, so that
|
||||
// the API of Initialize doens't change).
|
||||
// the API of Initialize doesn't change).
|
||||
var serverManagesSnapshot bool
|
||||
locallyManagedKeys := []string{
|
||||
data.CanonicalTargetsRole,
|
||||
|
@ -197,7 +201,7 @@ func (r *NotaryRepository) Initialize(rootKeyID string, serverManagedRoles ...st
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.CertManager.AddTrustedCert(rootCert)
|
||||
r.CertStore.AddCert(rootCert)
|
||||
|
||||
// The root key gets stored in the TUF metadata X509 encoded, linking
|
||||
// the tuf root.json to our X509 PKI.
|
||||
|
@ -275,8 +279,6 @@ func addChange(cl *changelist.FileChangelist, c changelist.Change, roles ...stri
|
|||
|
||||
var changes []changelist.Change
|
||||
for _, role := range roles {
|
||||
role = strings.ToLower(role)
|
||||
|
||||
// Ensure we can only add targets to the CanonicalTargetsRole,
|
||||
// or a Delegation role (which is <CanonicalTargetsRole>/something else)
|
||||
if role != data.CanonicalTargetsRole && !data.IsDelegation(role) {
|
||||
|
@ -347,7 +349,7 @@ func (r *NotaryRepository) AddDelegation(name string, threshold int,
|
|||
// the repository when the changelist gets applied at publish time.
|
||||
// This does not validate that the delegation exists, since one might exist
|
||||
// after applying all changes.
|
||||
func (r *NotaryRepository) RemoveDelegation(name string) error {
|
||||
func (r *NotaryRepository) RemoveDelegation(name string, keyIDs, paths []string, removeAll bool) error {
|
||||
|
||||
if !data.IsDelegation(name) {
|
||||
return data.ErrInvalidRole{Role: name, Reason: "invalid delegation role name"}
|
||||
|
@ -360,20 +362,41 @@ func (r *NotaryRepository) RemoveDelegation(name string) error {
|
|||
defer cl.Close()
|
||||
|
||||
logrus.Debugf(`Removing delegation "%s"\n`, name)
|
||||
var template *changelist.TufChange
|
||||
|
||||
template := changelist.NewTufChange(
|
||||
changelist.ActionDelete,
|
||||
name,
|
||||
changelist.TypeTargetsDelegation,
|
||||
"", // no path
|
||||
nil,
|
||||
)
|
||||
// We use the Delete action only for force removal, Update is used for removing individual keys and paths
|
||||
if removeAll {
|
||||
template = changelist.NewTufChange(
|
||||
changelist.ActionDelete,
|
||||
name,
|
||||
changelist.TypeTargetsDelegation,
|
||||
"", // no path
|
||||
nil, // deleting role, no data needed
|
||||
)
|
||||
|
||||
} else {
|
||||
tdJSON, err := json.Marshal(&changelist.TufDelegation{
|
||||
RemoveKeys: keyIDs,
|
||||
RemovePaths: paths,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
template = changelist.NewTufChange(
|
||||
changelist.ActionUpdate,
|
||||
name,
|
||||
changelist.TypeTargetsDelegation,
|
||||
"", // no path
|
||||
tdJSON,
|
||||
)
|
||||
}
|
||||
|
||||
return addChange(cl, template, name)
|
||||
}
|
||||
|
||||
// AddTarget creates new changelist entries to add a target to the given roles
|
||||
// in the repository when the changelist gets appied at publish time.
|
||||
// in the repository when the changelist gets applied at publish time.
|
||||
// If roles are unspecified, the default role is "targets".
|
||||
func (r *NotaryRepository) AddTarget(target *Target, roles ...string) error {
|
||||
|
||||
|
@ -431,7 +454,7 @@ func (r *NotaryRepository) ListTargets(roles ...string) ([]*TargetWithRole, erro
|
|||
for _, role := range roles {
|
||||
// we don't need to do anything special with removing role from
|
||||
// roles because listSubtree always processes role and only excludes
|
||||
// descendent delegations that appear in roles.
|
||||
// descendant delegations that appear in roles.
|
||||
r.listSubtree(targets, role, roles...)
|
||||
}
|
||||
|
||||
|
@ -509,6 +532,92 @@ func (r *NotaryRepository) GetChangelist() (changelist.Changelist, error) {
|
|||
return cl, nil
|
||||
}
|
||||
|
||||
// GetDelegationRoles returns the keys and roles of the repository's delegations
|
||||
func (r *NotaryRepository) GetDelegationRoles() ([]*data.Role, error) {
|
||||
// Update state of the repo to latest
|
||||
if _, err := r.Update(false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// All top level delegations (ex: targets/level1) are stored exclusively in targets.json
|
||||
targets, ok := r.tufRepo.Targets[data.CanonicalTargetsRole]
|
||||
if !ok {
|
||||
return nil, store.ErrMetaNotFound{Resource: data.CanonicalTargetsRole}
|
||||
}
|
||||
|
||||
allDelegations := targets.Signed.Delegations.Roles
|
||||
|
||||
// make a copy for traversing nested delegations
|
||||
delegationsList := make([]*data.Role, len(allDelegations))
|
||||
copy(delegationsList, allDelegations)
|
||||
|
||||
// Now traverse to lower level delegations (ex: targets/level1/level2)
|
||||
for len(delegationsList) > 0 {
|
||||
// Pop off first delegation to traverse
|
||||
delegation := delegationsList[0]
|
||||
delegationsList = delegationsList[1:]
|
||||
|
||||
// Get metadata
|
||||
delegationMeta, ok := r.tufRepo.Targets[delegation.Name]
|
||||
// If we get an error, don't try to traverse further into this subtree because it doesn't exist or is malformed
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Add nested delegations to return list and exploration list
|
||||
allDelegations = append(allDelegations, delegationMeta.Signed.Delegations.Roles...)
|
||||
delegationsList = append(delegationsList, delegationMeta.Signed.Delegations.Roles...)
|
||||
}
|
||||
return allDelegations, nil
|
||||
}
|
||||
|
||||
// RoleWithSignatures is a Role with its associated signatures
|
||||
type RoleWithSignatures struct {
|
||||
Signatures []data.Signature
|
||||
data.Role
|
||||
}
|
||||
|
||||
// ListRoles returns a list of RoleWithSignatures objects for this repo
|
||||
// This represents the latest metadata for each role in this repo
|
||||
func (r *NotaryRepository) ListRoles() ([]RoleWithSignatures, error) {
|
||||
// Update to latest repo state
|
||||
_, err := r.Update(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get all role info from our updated keysDB, can be empty
|
||||
roles := r.tufRepo.GetAllLoadedRoles()
|
||||
|
||||
var roleWithSigs []RoleWithSignatures
|
||||
|
||||
// Populate RoleWithSignatures with Role from keysDB and signatures from TUF metadata
|
||||
for _, role := range roles {
|
||||
roleWithSig := RoleWithSignatures{Role: *role, Signatures: nil}
|
||||
switch role.Name {
|
||||
case data.CanonicalRootRole:
|
||||
roleWithSig.Signatures = r.tufRepo.Root.Signatures
|
||||
case data.CanonicalTargetsRole:
|
||||
roleWithSig.Signatures = r.tufRepo.Targets[data.CanonicalTargetsRole].Signatures
|
||||
case data.CanonicalSnapshotRole:
|
||||
roleWithSig.Signatures = r.tufRepo.Snapshot.Signatures
|
||||
case data.CanonicalTimestampRole:
|
||||
roleWithSig.Signatures = r.tufRepo.Timestamp.Signatures
|
||||
default:
|
||||
// If the role isn't a delegation, we should error -- this is only possible if we have invalid keyDB state
|
||||
if !data.IsDelegation(role.Name) {
|
||||
return nil, data.ErrInvalidRole{Role: role.Name, Reason: "invalid role name"}
|
||||
}
|
||||
if _, ok := r.tufRepo.Targets[role.Name]; ok {
|
||||
// We'll only find a signature if we've published any targets with this delegation
|
||||
roleWithSig.Signatures = r.tufRepo.Targets[role.Name].Signatures
|
||||
}
|
||||
}
|
||||
roleWithSigs = append(roleWithSigs, roleWithSig)
|
||||
}
|
||||
return roleWithSigs, nil
|
||||
}
|
||||
|
||||
// Publish pushes the local changes in signed material to the remote notary-server
|
||||
// Conceptually it performs an operation similar to a `git rebase`
|
||||
func (r *NotaryRepository) Publish() error {
|
||||
|
@ -837,7 +946,7 @@ func (r *NotaryRepository) validateRoot(rootJSON []byte) (*data.SignedRoot, erro
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err = r.CertManager.ValidateRoot(root, r.gun)
|
||||
err = certs.ValidateRoot(r.CertStore, root, r.gun)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -904,3 +1013,27 @@ func (r *NotaryRepository) rootFileKeyChange(role, action string, key data.Publi
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteTrustData removes the trust data stored for this repo in the TUF cache and certificate store on the client side
|
||||
func (r *NotaryRepository) DeleteTrustData() error {
|
||||
// Clear TUF files and cache
|
||||
if err := r.fileStore.RemoveAll(); err != nil {
|
||||
return fmt.Errorf("error clearing TUF repo data: %v", err)
|
||||
}
|
||||
r.tufRepo = tuf.NewRepo(nil, nil)
|
||||
// Clear certificates
|
||||
certificates, err := r.CertStore.GetCertificatesByCN(r.gun)
|
||||
if err != nil {
|
||||
// If there were no certificates to delete, we're done
|
||||
if _, ok := err.(*trustmanager.ErrNoCertificatesFound); ok {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("error retrieving certificates for %s: %v", r.gun, err)
|
||||
}
|
||||
for _, cert := range certificates {
|
||||
if err := r.CertStore.RemoveCert(cert); err != nil {
|
||||
return fmt.Errorf("error removing certificate: %v: %v", cert, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
|
@ -85,13 +86,13 @@ func changeTargetsDelegation(repo *tuf.Repo, c changelist.Change) error {
|
|||
return err
|
||||
}
|
||||
if err == nil {
|
||||
// role existed
|
||||
return data.ErrInvalidRole{
|
||||
Role: c.Scope(),
|
||||
Reason: "cannot create a role that already exists",
|
||||
// role existed, attempt to merge paths and keys
|
||||
if err := r.AddPaths(td.AddPaths); err != nil {
|
||||
return err
|
||||
}
|
||||
return repo.UpdateDelegations(r, td.AddKeys)
|
||||
}
|
||||
// role doesn't exist, create brand new
|
||||
// create brand new role
|
||||
r, err = td.ToNewRole(c.Scope())
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -107,7 +108,12 @@ func changeTargetsDelegation(repo *tuf.Repo, c changelist.Change) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// role exists, merge
|
||||
// If we specify the only keys left delete the role, else just delete specified keys
|
||||
if strings.Join(r.KeyIDs, ";") == strings.Join(td.RemoveKeys, ";") && len(td.AddKeys) == 0 {
|
||||
r := data.Role{Name: c.Scope()}
|
||||
return repo.DeleteDelegation(r)
|
||||
}
|
||||
// if we aren't deleting and the role exists, merge
|
||||
if err := r.AddPaths(td.AddPaths); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -2,6 +2,14 @@ package notary
|
|||
|
||||
// application wide constants
|
||||
const (
|
||||
// MinThreshold requires a minimum of one threshold for roles; currently we do not support a higher threshold
|
||||
MinThreshold = 1
|
||||
// PrivKeyPerms are the file permissions to use when writing private keys to disk
|
||||
PrivKeyPerms = 0700
|
||||
// PubCertPerms are the file permissions to use when writing public certificates to disk
|
||||
PubCertPerms = 0755
|
||||
// Sha256HexSize is how big a Sha256 hex is in number of characters
|
||||
Sha256HexSize = 64
|
||||
// TrustedCertsDir is the directory, under the notary repo base directory, where trusted certs are stored
|
||||
TrustedCertsDir = "trusted_certificates"
|
||||
)
|
||||
|
|
|
@ -69,8 +69,8 @@ func (cs *CryptoService) Create(role, algorithm string) (data.PublicKey, error)
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to add key to filestore: %v", err)
|
||||
}
|
||||
return nil, fmt.Errorf("keystores would not accept new private keys for unknown reasons")
|
||||
|
||||
return nil, fmt.Errorf("keystores would not accept new private keys for unknown reasons")
|
||||
}
|
||||
|
||||
// GetPrivateKey returns a private key and role if present by ID.
|
||||
|
|
|
@ -205,7 +205,8 @@ func GetLeafCerts(certs []*x509.Certificate) []*x509.Certificate {
|
|||
|
||||
// GetIntermediateCerts parses a list of x509 Certificates and returns all of the
|
||||
// ones marked as a CA, to be used as intermediates
|
||||
func GetIntermediateCerts(certs []*x509.Certificate) (intCerts []*x509.Certificate) {
|
||||
func GetIntermediateCerts(certs []*x509.Certificate) []*x509.Certificate {
|
||||
var intCerts []*x509.Certificate
|
||||
for _, cert := range certs {
|
||||
if cert.IsCA {
|
||||
intCerts = append(intCerts, cert)
|
||||
|
@ -299,6 +300,44 @@ func ParsePEMPrivateKey(pemBytes []byte, passphrase string) (data.PrivateKey, er
|
|||
}
|
||||
}
|
||||
|
||||
// ParsePEMPublicKey returns a data.PublicKey from a PEM encoded public key or certificate.
|
||||
func ParsePEMPublicKey(pubKeyBytes []byte) (data.PublicKey, error) {
|
||||
pemBlock, _ := pem.Decode(pubKeyBytes)
|
||||
if pemBlock == nil {
|
||||
return nil, errors.New("no valid public key found")
|
||||
}
|
||||
|
||||
switch pemBlock.Type {
|
||||
case "CERTIFICATE":
|
||||
cert, err := x509.ParseCertificate(pemBlock.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse provided certificate: %v", err)
|
||||
}
|
||||
err = ValidateCertificate(cert)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid certificate: %v", err)
|
||||
}
|
||||
return CertToKey(cert), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported PEM block type %q, expected certificate", pemBlock.Type)
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateCertificate returns an error if the certificate is not valid for notary
|
||||
// Currently, this is only a time expiry check
|
||||
func ValidateCertificate(c *x509.Certificate) error {
|
||||
if (c.NotBefore).After(c.NotAfter) {
|
||||
return fmt.Errorf("certificate validity window is invalid")
|
||||
}
|
||||
now := time.Now()
|
||||
tomorrow := now.AddDate(0, 0, 1)
|
||||
// Give one day leeway on creation "before" time, check "after" against today
|
||||
if (tomorrow).Before(c.NotBefore) || now.After(c.NotAfter) {
|
||||
return fmt.Errorf("certificate is expired")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateRSAKey generates an RSA private key and returns a TUF PrivateKey
|
||||
func GenerateRSAKey(random io.Reader, bits int) (data.PrivateKey, error) {
|
||||
rsaPrivKey, err := rsa.GenerateKey(random, bits)
|
||||
|
@ -532,3 +571,14 @@ func X509PublicKeyID(certPubKey data.PublicKey) (string, error) {
|
|||
|
||||
return key.ID(), nil
|
||||
}
|
||||
|
||||
// FilterCertsExpiredSha1 can be used as the filter function to cert store
|
||||
// initializers to filter out all expired or SHA-1 certificate that we
|
||||
// shouldn't load.
|
||||
func FilterCertsExpiredSha1(cert *x509.Certificate) bool {
|
||||
return !cert.IsCA &&
|
||||
time.Now().Before(cert.NotAfter) &&
|
||||
cert.SignatureAlgorithm != x509.SHA1WithRSA &&
|
||||
cert.SignatureAlgorithm != x509.DSAWithSHA1 &&
|
||||
cert.SignatureAlgorithm != x509.ECDSAWithSHA1
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ func (c *Client) Update() error {
|
|||
if err != nil {
|
||||
logrus.Debug("Error occurred. Root will be downloaded and another update attempted")
|
||||
if err := c.downloadRoot(); err != nil {
|
||||
logrus.Error("client Update (Root):", err)
|
||||
logrus.Error("Client Update (Root):", err)
|
||||
return err
|
||||
}
|
||||
// If we error again, we now have the latest root and just want to fail
|
||||
|
@ -247,28 +247,27 @@ func (c *Client) downloadTimestamp() error {
|
|||
// We may not have a cached timestamp if this is the first time
|
||||
// we're interacting with the repo. This will result in the
|
||||
// version being 0
|
||||
var download bool
|
||||
old := &data.Signed{}
|
||||
version := 0
|
||||
var (
|
||||
saveToCache bool
|
||||
old *data.Signed
|
||||
version = 0
|
||||
)
|
||||
cachedTS, err := c.cache.GetMeta(role, maxSize)
|
||||
if err == nil {
|
||||
err := json.Unmarshal(cachedTS, old)
|
||||
cached := &data.Signed{}
|
||||
err := json.Unmarshal(cachedTS, cached)
|
||||
if err == nil {
|
||||
ts, err := data.TimestampFromSigned(old)
|
||||
ts, err := data.TimestampFromSigned(cached)
|
||||
if err == nil {
|
||||
version = ts.Signed.Version
|
||||
}
|
||||
} else {
|
||||
old = nil
|
||||
old = cached
|
||||
}
|
||||
}
|
||||
// unlike root, targets and snapshot, always try and download timestamps
|
||||
// from remote, only using the cache one if we couldn't reach remote.
|
||||
raw, s, err := c.downloadSigned(role, maxSize, nil)
|
||||
if err != nil || len(raw) == 0 {
|
||||
if err, ok := err.(store.ErrMetaNotFound); ok {
|
||||
return err
|
||||
}
|
||||
if old == nil {
|
||||
if err == nil {
|
||||
// couldn't retrieve data from server and don't have valid
|
||||
|
@ -277,17 +276,18 @@ func (c *Client) downloadTimestamp() error {
|
|||
}
|
||||
return err
|
||||
}
|
||||
logrus.Debug("using cached timestamp")
|
||||
logrus.Debug(err.Error())
|
||||
logrus.Warn("Error while downloading remote metadata, using cached timestamp - this might not be the latest version available remotely")
|
||||
s = old
|
||||
} else {
|
||||
download = true
|
||||
saveToCache = true
|
||||
}
|
||||
err = signed.Verify(s, role, version, c.keysDB)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Debug("successfully verified timestamp")
|
||||
if download {
|
||||
if saveToCache {
|
||||
c.cache.SetMeta(role, raw)
|
||||
}
|
||||
ts, err := data.TimestampFromSigned(s)
|
||||
|
@ -327,7 +327,7 @@ func (c *Client) downloadSnapshot() error {
|
|||
}
|
||||
err := json.Unmarshal(raw, old)
|
||||
if err == nil {
|
||||
snap, err := data.TimestampFromSigned(old)
|
||||
snap, err := data.SnapshotFromSigned(old)
|
||||
if err == nil {
|
||||
version = snap.Signed.Version
|
||||
} else {
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/agl/ed25519"
|
||||
"github.com/jfrazelle/go/canonical/json"
|
||||
"github.com/docker/go/canonical/json"
|
||||
)
|
||||
|
||||
// PublicKey is the necessary interface for public keys
|
||||
|
|
|
@ -2,6 +2,7 @@ package data
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Sirupsen/logrus"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
@ -109,10 +110,7 @@ func NewRole(name string, threshold int, keyIDs, paths, pathHashPrefixes []strin
|
|||
}
|
||||
if IsDelegation(name) {
|
||||
if len(paths) == 0 && len(pathHashPrefixes) == 0 {
|
||||
return nil, ErrInvalidRole{
|
||||
Role: name,
|
||||
Reason: "roles with no Paths and no PathHashPrefixes will never be able to publish content",
|
||||
}
|
||||
logrus.Debugf("role %s with no Paths and no PathHashPrefixes will never be able to publish content until one or more are added", name)
|
||||
}
|
||||
}
|
||||
if threshold < 1 {
|
||||
|
|
|
@ -3,7 +3,7 @@ package data
|
|||
import (
|
||||
"time"
|
||||
|
||||
"github.com/jfrazelle/go/canonical/json"
|
||||
"github.com/docker/go/canonical/json"
|
||||
)
|
||||
|
||||
// SignedRoot is a fully unpacked root.json
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package data
|
||||
|
||||
import "github.com/jfrazelle/go/canonical/json"
|
||||
import "github.com/docker/go/canonical/json"
|
||||
|
||||
// Serializer is an interface that can marshal and unmarshal TUF data. This
|
||||
// is expected to be a canonical JSON marshaller
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/jfrazelle/go/canonical/json"
|
||||
"github.com/docker/go/canonical/json"
|
||||
)
|
||||
|
||||
// SignedSnapshot is a fully unpacked snapshot.json
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"encoding/hex"
|
||||
"errors"
|
||||
|
||||
"github.com/jfrazelle/go/canonical/json"
|
||||
"github.com/docker/go/canonical/json"
|
||||
)
|
||||
|
||||
// SignedTargets is a fully unpacked targets.json, or target delegation
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"bytes"
|
||||
"time"
|
||||
|
||||
"github.com/jfrazelle/go/canonical/json"
|
||||
"github.com/docker/go/canonical/json"
|
||||
)
|
||||
|
||||
// SignedTimestamp is a fully unpacked timestamp.json
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/jfrazelle/go/canonical/json"
|
||||
"github.com/docker/go/canonical/json"
|
||||
)
|
||||
|
||||
// SigAlgorithm for types of signatures
|
||||
|
|
|
@ -58,6 +58,15 @@ func (db *KeyDB) AddRole(r *data.Role) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// GetAllRoles gets all roles from the database
|
||||
func (db *KeyDB) GetAllRoles() []*data.Role {
|
||||
roles := []*data.Role{}
|
||||
for _, role := range db.roles {
|
||||
roles = append(roles, role)
|
||||
}
|
||||
return roles
|
||||
}
|
||||
|
||||
// GetKey pulls a key out of the database by its ID
|
||||
func (db *KeyDB) GetKey(id string) data.PublicKey {
|
||||
return db.keys[id]
|
||||
|
|
|
@ -6,9 +6,9 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/go/canonical/json"
|
||||
"github.com/docker/notary/tuf/data"
|
||||
"github.com/docker/notary/tuf/keys"
|
||||
"github.com/jfrazelle/go/canonical/json"
|
||||
)
|
||||
|
||||
// Various basic signing errors
|
||||
|
|
|
@ -39,11 +39,14 @@ type FilesystemStore struct {
|
|||
targetsDir string
|
||||
}
|
||||
|
||||
func (f *FilesystemStore) getPath(name string) string {
|
||||
fileName := fmt.Sprintf("%s.%s", name, f.metaExtension)
|
||||
return filepath.Join(f.metaDir, fileName)
|
||||
}
|
||||
|
||||
// GetMeta returns the meta for the given name (a role)
|
||||
func (f *FilesystemStore) GetMeta(name string, size int64) ([]byte, error) {
|
||||
fileName := fmt.Sprintf("%s.%s", name, f.metaExtension)
|
||||
path := filepath.Join(f.metaDir, fileName)
|
||||
meta, err := ioutil.ReadFile(path)
|
||||
meta, err := ioutil.ReadFile(f.getPath(name))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = ErrMetaNotFound{Resource: name}
|
||||
|
@ -66,21 +69,31 @@ func (f *FilesystemStore) SetMultiMeta(metas map[string][]byte) error {
|
|||
|
||||
// SetMeta sets the meta for a single role
|
||||
func (f *FilesystemStore) SetMeta(name string, meta []byte) error {
|
||||
fileName := fmt.Sprintf("%s.%s", name, f.metaExtension)
|
||||
path := filepath.Join(f.metaDir, fileName)
|
||||
fp := f.getPath(name)
|
||||
|
||||
// Ensures the parent directories of the file we are about to write exist
|
||||
err := os.MkdirAll(filepath.Dir(path), 0700)
|
||||
err := os.MkdirAll(filepath.Dir(fp), 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// if something already exists, just delete it and re-write it
|
||||
os.RemoveAll(path)
|
||||
os.RemoveAll(fp)
|
||||
|
||||
// Write the file to disk
|
||||
if err = ioutil.WriteFile(path, meta, 0600); err != nil {
|
||||
if err = ioutil.WriteFile(fp, meta, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveAll clears the existing filestore by removing its base directory
|
||||
func (f *FilesystemStore) RemoveAll() error {
|
||||
return os.RemoveAll(f.baseDir)
|
||||
}
|
||||
|
||||
// RemoveMeta removes the metadata for a single role - if the metadata doesn't
|
||||
// exist, no error is returned
|
||||
func (f *FilesystemStore) RemoveMeta(name string) error {
|
||||
return os.RemoveAll(f.getPath(name)) // RemoveAll succeeds if path doesn't exist
|
||||
}
|
||||
|
|
|
@ -85,6 +85,9 @@ func NewHTTPStore(baseURL, metaPrefix, metaExtension, targetsPrefix, keyExtensio
|
|||
if !base.IsAbs() {
|
||||
return nil, errors.New("HTTPStore requires an absolute baseURL")
|
||||
}
|
||||
if roundTrip == nil {
|
||||
return &OfflineStore{}, nil
|
||||
}
|
||||
return &HTTPStore{
|
||||
baseURL: *base,
|
||||
metaPrefix: metaPrefix,
|
||||
|
@ -182,6 +185,12 @@ func (s HTTPStore) SetMeta(name string, blob []byte) error {
|
|||
return translateStatusToError(resp, "POST "+name)
|
||||
}
|
||||
|
||||
// RemoveMeta always fails, because we should never be able to delete metadata
|
||||
// remotely
|
||||
func (s HTTPStore) RemoveMeta(name string) error {
|
||||
return ErrInvalidOperation{msg: "cannot delete metadata"}
|
||||
}
|
||||
|
||||
// NewMultiPartMetaRequest builds a request with the provided metadata updates
|
||||
// in multipart form
|
||||
func NewMultiPartMetaRequest(url string, metas map[string][]byte) (*http.Request, error) {
|
||||
|
@ -227,6 +236,11 @@ func (s HTTPStore) SetMultiMeta(metas map[string][]byte) error {
|
|||
return translateStatusToError(resp, "POST metadata endpoint")
|
||||
}
|
||||
|
||||
// RemoveAll in the interface is not supported, admins should use the DeleteHandler endpoint directly to delete remote data for a GUN
|
||||
func (s HTTPStore) RemoveAll() error {
|
||||
return errors.New("remove all functionality not supported for HTTPStore")
|
||||
}
|
||||
|
||||
func (s HTTPStore) buildMetaURL(name string) (*url.URL, error) {
|
||||
var filename string
|
||||
if name != "" {
|
||||
|
|
|
@ -14,6 +14,8 @@ type MetadataStore interface {
|
|||
GetMeta(name string, size int64) ([]byte, error)
|
||||
SetMeta(name string, blob []byte) error
|
||||
SetMultiMeta(map[string][]byte) error
|
||||
RemoveAll() error
|
||||
RemoveMeta(name string) error
|
||||
}
|
||||
|
||||
// PublicKeyStore must be implemented by a key service
|
||||
|
|
|
@ -54,6 +54,13 @@ func (m *memoryStore) SetMultiMeta(metas map[string][]byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// RemoveMeta removes the metadata for a single role - if the metadata doesn't
|
||||
// exist, no error is returned
|
||||
func (m *memoryStore) RemoveMeta(name string) error {
|
||||
delete(m.meta, name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) GetTarget(path string) (io.ReadCloser, error) {
|
||||
return &utils.NoopCloser{Reader: bytes.NewReader(m.files[path])}, nil
|
||||
}
|
||||
|
@ -95,3 +102,11 @@ func (m *memoryStore) Commit(map[string][]byte, bool, map[string]data.Hashes) er
|
|||
func (m *memoryStore) GetKey(role string) ([]byte, error) {
|
||||
return nil, fmt.Errorf("GetKey is not implemented for the memoryStore")
|
||||
}
|
||||
|
||||
// Clear this existing memory store by setting this store as new empty one
|
||||
func (m *memoryStore) RemoveAll() error {
|
||||
m.meta = make(map[string][]byte)
|
||||
m.files = make(map[string][]byte)
|
||||
m.keys = make(map[string][]data.PrivateKey)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -14,30 +14,40 @@ func (e ErrOffline) Error() string {
|
|||
var err = ErrOffline{}
|
||||
|
||||
// OfflineStore is to be used as a placeholder for a nil store. It simply
|
||||
// return ErrOffline for every operation
|
||||
// returns ErrOffline for every operation
|
||||
type OfflineStore struct{}
|
||||
|
||||
// GetMeta return ErrOffline
|
||||
// GetMeta returns ErrOffline
|
||||
func (es OfflineStore) GetMeta(name string, size int64) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// SetMeta return ErrOffline
|
||||
// SetMeta returns ErrOffline
|
||||
func (es OfflineStore) SetMeta(name string, blob []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// SetMultiMeta return ErrOffline
|
||||
// SetMultiMeta returns ErrOffline
|
||||
func (es OfflineStore) SetMultiMeta(map[string][]byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// GetKey return ErrOffline
|
||||
// RemoveMeta returns ErrOffline
|
||||
func (es OfflineStore) RemoveMeta(name string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// GetKey returns ErrOffline
|
||||
func (es OfflineStore) GetKey(role string) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GetTarget return ErrOffline
|
||||
// GetTarget returns ErrOffline
|
||||
func (es OfflineStore) GetTarget(path string) (io.ReadCloser, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// RemoveAll return ErrOffline
|
||||
func (es OfflineStore) RemoveAll() error {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -173,6 +173,11 @@ func (tr *Repo) RemoveBaseKeys(role string, keyIDs ...string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// GetAllLoadedRoles returns a list of all role entries loaded in this TUF repo, could be empty
|
||||
func (tr *Repo) GetAllLoadedRoles() []*data.Role {
|
||||
return tr.keysDB.GetAllRoles()
|
||||
}
|
||||
|
||||
// GetDelegation finds the role entry representing the provided
|
||||
// role name or ErrInvalidRole
|
||||
func (tr *Repo) GetDelegation(role string) (*data.Role, error) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче