mirror of https://github.com/docker/docs.git
Merge pull request #19760 from cyli/re-vendor-notary
Re-vendor Notary and docker/go
This commit is contained in:
commit
c39c7e6edf
|
@ -167,7 +167,7 @@ RUN set -x \
|
||||||
&& rm -rf "$GOPATH"
|
&& rm -rf "$GOPATH"
|
||||||
|
|
||||||
# Install notary server
|
# Install notary server
|
||||||
ENV NOTARY_VERSION docker-v1.10-3
|
ENV NOTARY_VERSION docker-v1.10-4
|
||||||
RUN set -x \
|
RUN set -x \
|
||||||
&& export GOPATH="$(mktemp -d)" \
|
&& export GOPATH="$(mktemp -d)" \
|
||||||
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
||||||
|
|
|
@ -110,11 +110,11 @@ RUN set -x \
|
||||||
&& rm -rf "$GOPATH"
|
&& rm -rf "$GOPATH"
|
||||||
|
|
||||||
# Install notary server
|
# Install notary server
|
||||||
ENV NOTARY_COMMIT 0c11a970826e62479379ccc75a45184460b9200f
|
ENV NOTARY_VERSION docker-v1.10-4
|
||||||
RUN set -x \
|
RUN set -x \
|
||||||
&& export GOPATH="$(mktemp -d)" \
|
&& export GOPATH="$(mktemp -d)" \
|
||||||
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
&& 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" \
|
&& 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 \
|
go build -o /usr/local/bin/notary-server github.com/docker/notary/cmd/notary-server \
|
||||||
&& rm -rf "$GOPATH"
|
&& rm -rf "$GOPATH"
|
||||||
|
|
|
@ -144,7 +144,7 @@ RUN set -x \
|
||||||
&& rm -rf "$GOPATH"
|
&& rm -rf "$GOPATH"
|
||||||
|
|
||||||
# Install notary server
|
# Install notary server
|
||||||
ENV NOTARY_VERSION docker-v1.10-2
|
ENV NOTARY_VERSION docker-v1.10-4
|
||||||
RUN set -x \
|
RUN set -x \
|
||||||
&& export GOPATH="$(mktemp -d)" \
|
&& export GOPATH="$(mktemp -d)" \
|
||||||
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
&& 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+
|
# TODO update this when we upgrade to Go 1.5.1+
|
||||||
# Install notary server
|
# Install notary server
|
||||||
#ENV NOTARY_COMMIT 8e8122eb5528f621afcd4e2854c47302f17392f7
|
#ENV NOTARY_VERSION docker-v1.10-4
|
||||||
#RUN set -x \
|
#RUN set -x \
|
||||||
# && export GOPATH="$(mktemp -d)" \
|
# && export GOPATH="$(mktemp -d)" \
|
||||||
# && git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
# && 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" \
|
# && 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 \
|
# go build -o /usr/local/bin/notary-server github.com/docker/notary/cmd/notary-server \
|
||||||
# && rm -rf "$GOPATH"
|
# && rm -rf "$GOPATH"
|
||||||
|
|
|
@ -116,11 +116,11 @@ RUN set -x \
|
||||||
&& rm -rf "$GOPATH"
|
&& rm -rf "$GOPATH"
|
||||||
|
|
||||||
# Install notary server
|
# Install notary server
|
||||||
ENV NOTARY_COMMIT 8e8122eb5528f621afcd4e2854c47302f17392f7
|
ENV NOTARY_VERSION docker-v1.10-4
|
||||||
RUN set -x \
|
RUN set -x \
|
||||||
&& export GOPATH="$(mktemp -d)" \
|
&& export GOPATH="$(mktemp -d)" \
|
||||||
&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
|
&& 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" \
|
&& 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 \
|
go build -o /usr/local/bin/notary-server github.com/docker/notary/cmd/notary-server \
|
||||||
&& rm -rf "$GOPATH"
|
&& rm -rf "$GOPATH"
|
||||||
|
|
|
@ -50,11 +50,11 @@ clone git github.com/docker/distribution c301f8ab27f4913c968b8d73a38e5dda79b9d3d
|
||||||
clone git github.com/vbatts/tar-split v0.9.11
|
clone git github.com/vbatts/tar-split v0.9.11
|
||||||
|
|
||||||
# get desired notary commit, might also need to be updated in Dockerfile
|
# 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 google.golang.org/grpc 174192fc93efcb188fc8f46ca447f0da606b6885 https://github.com/grpc/grpc-go.git
|
||||||
clone git github.com/miekg/pkcs11 80f102b5cac759de406949c47f0928b99bd64cdf
|
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/agl/ed25519 d2b94fd789ea21d12fac1a4443dd3a3f79cda72c
|
||||||
|
|
||||||
clone git github.com/opencontainers/runc 3d8a20bb772defc28c355534d83486416d1719b4 # libcontainer
|
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) {
|
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
|
// tag the image and upload it to the private registry
|
||||||
dockerCmd(c, "tag", "busybox", repoName)
|
dockerCmd(c, "tag", "busybox", withTagName)
|
||||||
dockerCmd(c, "push", repoName)
|
dockerCmd(c, "push", withTagName)
|
||||||
dockerCmd(c, "rmi", repoName)
|
dockerCmd(c, "rmi", withTagName)
|
||||||
|
|
||||||
// Try trusted create on untrusted tag
|
// Try trusted create on untrusted tag
|
||||||
createCmd := exec.Command(dockerBinary, "create", repoName)
|
createCmd := exec.Command(dockerBinary, "create", withTagName)
|
||||||
s.trustedCmd(createCmd)
|
s.trustedCmd(createCmd)
|
||||||
out, _, err := runCommandWithOutput(createCmd)
|
out, _, err := runCommandWithOutput(createCmd)
|
||||||
c.Assert(err, check.Not(check.IsNil))
|
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) {
|
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
|
// tag the image and upload it to the private registry
|
||||||
dockerCmd(c, "tag", "busybox", repoName)
|
dockerCmd(c, "tag", "busybox", repoName)
|
||||||
dockerCmd(c, "push", repoName)
|
dockerCmd(c, "push", repoName)
|
||||||
|
|
|
@ -215,7 +215,7 @@ func (s *DockerSchema1RegistrySuite) TestCrossRepositoryLayerPushNotSupported(c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerTrustSuite) TestTrustedPush(c *check.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
|
// tag the image and upload it to the private registry
|
||||||
dockerCmd(c, "tag", "busybox", repoName)
|
dockerCmd(c, "tag", "busybox", repoName)
|
||||||
|
|
||||||
|
@ -267,7 +267,7 @@ func (s *DockerTrustSuite) TestTrustedPushWithDeprecatedEnvPasswords(c *check.C)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerTrustSuite) TestTrustedPushWithFailingServer(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
|
// tag the image and upload it to the private registry
|
||||||
dockerCmd(c, "tag", "busybox", repoName)
|
dockerCmd(c, "tag", "busybox", repoName)
|
||||||
|
|
||||||
|
@ -279,7 +279,7 @@ func (s *DockerTrustSuite) TestTrustedPushWithFailingServer(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerTrustSuite) TestTrustedPushWithoutServerAndUntrusted(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
|
// tag the image and upload it to the private registry
|
||||||
dockerCmd(c, "tag", "busybox", repoName)
|
dockerCmd(c, "tag", "busybox", repoName)
|
||||||
|
|
||||||
|
|
|
@ -3179,7 +3179,7 @@ func (s *DockerTrustSuite) TestTrustedRun(c *check.C) {
|
||||||
func (s *DockerTrustSuite) TestUntrustedRun(c *check.C) {
|
func (s *DockerTrustSuite) TestUntrustedRun(c *check.C) {
|
||||||
// Windows does not support this functionality
|
// Windows does not support this functionality
|
||||||
testRequires(c, DaemonIsLinux)
|
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
|
// tag the image and upload it to the private registry
|
||||||
dockerCmd(c, "tag", "busybox", repoName)
|
dockerCmd(c, "tag", "busybox", repoName)
|
||||||
dockerCmd(c, "push", repoName)
|
dockerCmd(c, "push", repoName)
|
||||||
|
|
|
@ -118,12 +118,13 @@ protos:
|
||||||
# be run first
|
# be run first
|
||||||
|
|
||||||
define gocover
|
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
|
endef
|
||||||
|
|
||||||
gen-cover: go_version
|
gen-cover: go_version
|
||||||
@mkdir -p "$(COVERDIR)"
|
@mkdir -p "$(COVERDIR)"
|
||||||
$(foreach PKG,$(PKGS),$(call gocover,$(PKG)))
|
$(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
|
# Generates the cover binaries and runs them all in serial, so this can be used
|
||||||
# run all tests with a yubikey without any problems
|
# run all tests with a yubikey without any problems
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
@ -13,14 +12,6 @@ import (
|
||||||
"github.com/docker/notary/tuf/signed"
|
"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
|
// ErrValidationFail is returned when there is no valid trusted certificates
|
||||||
// being served inside of the roots.json
|
// being served inside of the roots.json
|
||||||
type ErrValidationFail struct {
|
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)
|
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
|
ValidateRoot receives a new root, validates its correctness and attempts to
|
||||||
do root key rotation if needed.
|
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
|
have a list of trusted certificates for it. In this case, we use this list of
|
||||||
certificates to attempt to validate this root file.
|
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
|
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
|
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
|
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.
|
adding an extra layer of security over the normal (SSH style) trust model.
|
||||||
We shall call this: TOFUS.
|
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)
|
logrus.Debugf("entered ValidateRoot with dns: %s", gun)
|
||||||
signedRoot, err := data.RootFromSigned(root)
|
signedRoot, err := data.RootFromSigned(root)
|
||||||
if err != nil {
|
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
|
// Retrieve all the trusted certificates that match this gun
|
||||||
certsForCN, err := m.trustedCertificateStore.GetCertificatesByCN(gun)
|
certsForCN, err := certStore.GetCertificatesByCN(gun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If the error that we get back is different than ErrNoCertificatesFound
|
// If the error that we get back is different than ErrNoCertificatesFound
|
||||||
// we couldn't check if there are any certificates with this CN already
|
// 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
|
// 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)
|
// First we add all the new certificates (even if they already exist)
|
||||||
for _, cert := range allValidCerts {
|
for _, cert := range allValidCerts {
|
||||||
err := m.trustedCertificateStore.AddCert(cert)
|
err := certStore.AddCert(cert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If the error is already exists we don't fail the rotation
|
// If the error is already exists we don't fail the rotation
|
||||||
if _, ok := err.(*trustmanager.ErrCertExists); ok {
|
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
|
// Now we delete old certificates that aren't present in the new root
|
||||||
for certID, cert := range certsToRemove(certsForCN, allValidCerts) {
|
for certID, cert := range certsToRemove(certsForCN, allValidCerts) {
|
||||||
logrus.Debugf("removing certificate with certID: %s", certID)
|
logrus.Debugf("removing certificate with certID: %s", certID)
|
||||||
err = m.trustedCertificateStore.RemoveCert(cert)
|
err = certStore.RemoveCert(cert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Debugf("failed to remove trusted certificate with keyID: %s, %v", certID, err)
|
logrus.Debugf("failed to remove trusted certificate with keyID: %s, %v", certID, err)
|
||||||
return &ErrRootRotationFail{Reason: "failed to rotate root keys"}
|
return &ErrRootRotationFail{Reason: "failed to rotate root keys"}
|
||||||
|
@ -208,7 +142,7 @@ func (m *Manager) ValidateRoot(root *data.Signed, gun string) error {
|
||||||
return nil
|
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
|
// Common-Names match the provided GUN
|
||||||
func validRootLeafCerts(root *data.SignedRoot, gun string) ([]*x509.Certificate, error) {
|
func validRootLeafCerts(root *data.SignedRoot, gun string) ([]*x509.Certificate, error) {
|
||||||
// Get a list of all of the leaf certificates present in root
|
// 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 {
|
for _, cert := range allLeafCerts {
|
||||||
// Validate that this leaf certificate has a CN that matches the exact gun
|
// Validate that this leaf certificate has a CN that matches the exact gun
|
||||||
if cert.Subject.CommonName != 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
|
continue
|
||||||
}
|
}
|
||||||
// Make sure the certificate is not expired
|
// Make sure the certificate is not expired
|
|
@ -18,6 +18,8 @@ machine:
|
||||||
CIRCLE_PAIN: "mode: set"
|
CIRCLE_PAIN: "mode: set"
|
||||||
# Put the coverage profile somewhere codecov's script can find it
|
# Put the coverage profile somewhere codecov's script can find it
|
||||||
COVERPROFILE: coverage.out
|
COVERPROFILE: coverage.out
|
||||||
|
# Set the pull request number so codecov can figure it out
|
||||||
|
PULL_REQUEST: ${CI_PULL_REQUEST##*/}
|
||||||
|
|
||||||
hosts:
|
hosts:
|
||||||
# Not used yet
|
# Not used yet
|
||||||
|
@ -40,8 +42,7 @@ dependencies:
|
||||||
# For the stable go version, additionally install linting tools
|
# For the stable go version, additionally install linting tools
|
||||||
- >
|
- >
|
||||||
gvm use stable &&
|
gvm use stable &&
|
||||||
go get github.com/golang/lint/golint github.com/wadey/gocovmerge &&
|
go get github.com/golang/lint/golint
|
||||||
go install github.com/wadey/gocovmerge
|
|
||||||
test:
|
test:
|
||||||
pre:
|
pre:
|
||||||
# Output the go versions we are going to test
|
# Output the go versions we are going to test
|
||||||
|
@ -72,11 +73,6 @@ test:
|
||||||
pwd: $BASE_STABLE
|
pwd: $BASE_STABLE
|
||||||
|
|
||||||
post:
|
post:
|
||||||
- gvm use stable && make covmerge:
|
|
||||||
timeout: 600
|
|
||||||
parallel: true
|
|
||||||
pwd: $BASE_STABLE
|
|
||||||
|
|
||||||
# Report to codecov.io
|
# Report to codecov.io
|
||||||
- bash <(curl -s https://codecov.io/bash):
|
- bash <(curl -s https://codecov.io/bash):
|
||||||
parallel: true
|
parallel: true
|
||||||
|
|
|
@ -9,10 +9,10 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/notary"
|
||||||
"github.com/docker/notary/certs"
|
"github.com/docker/notary/certs"
|
||||||
"github.com/docker/notary/client/changelist"
|
"github.com/docker/notary/client/changelist"
|
||||||
"github.com/docker/notary/cryptoservice"
|
"github.com/docker/notary/cryptoservice"
|
||||||
|
@ -53,9 +53,9 @@ type ErrInvalidRemoteRole struct {
|
||||||
Role string
|
Role string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e ErrInvalidRemoteRole) Error() string {
|
func (err ErrInvalidRemoteRole) Error() string {
|
||||||
return fmt.Sprintf(
|
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
|
// ErrRepositoryNotExist is returned when an action is taken on a remote
|
||||||
|
@ -84,7 +84,7 @@ type NotaryRepository struct {
|
||||||
CryptoService signed.CryptoService
|
CryptoService signed.CryptoService
|
||||||
tufRepo *tuf.Repo
|
tufRepo *tuf.Repo
|
||||||
roundTrip http.RoundTripper
|
roundTrip http.RoundTripper
|
||||||
CertManager *certs.Manager
|
CertStore trustmanager.X509Store
|
||||||
}
|
}
|
||||||
|
|
||||||
// repositoryFromKeystores is a helper function for NewNotaryRepository that
|
// repositoryFromKeystores is a helper function for NewNotaryRepository that
|
||||||
|
@ -93,7 +93,11 @@ type NotaryRepository struct {
|
||||||
func repositoryFromKeystores(baseDir, gun, baseURL string, rt http.RoundTripper,
|
func repositoryFromKeystores(baseDir, gun, baseURL string, rt http.RoundTripper,
|
||||||
keyStores []trustmanager.KeyStore) (*NotaryRepository, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -107,7 +111,7 @@ func repositoryFromKeystores(baseDir, gun, baseURL string, rt http.RoundTripper,
|
||||||
tufRepoPath: filepath.Join(baseDir, tufDir, filepath.FromSlash(gun)),
|
tufRepoPath: filepath.Join(baseDir, tufDir, filepath.FromSlash(gun)),
|
||||||
CryptoService: cryptoService,
|
CryptoService: cryptoService,
|
||||||
roundTrip: rt,
|
roundTrip: rt,
|
||||||
CertManager: certManager,
|
CertStore: certStore,
|
||||||
}
|
}
|
||||||
|
|
||||||
fileStore, err := store.NewFilesystemStore(
|
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
|
// currently we only support server managing timestamps and snapshots, and
|
||||||
// nothing else - timestamps are always managed by the server, and implicit
|
// nothing else - timestamps are always managed by the server, and implicit
|
||||||
// (do not have to be passed in as part of `serverManagedRoles`, so that
|
// (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
|
var serverManagesSnapshot bool
|
||||||
locallyManagedKeys := []string{
|
locallyManagedKeys := []string{
|
||||||
data.CanonicalTargetsRole,
|
data.CanonicalTargetsRole,
|
||||||
|
@ -197,7 +201,7 @@ func (r *NotaryRepository) Initialize(rootKeyID string, serverManagedRoles ...st
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
r.CertManager.AddTrustedCert(rootCert)
|
r.CertStore.AddCert(rootCert)
|
||||||
|
|
||||||
// The root key gets stored in the TUF metadata X509 encoded, linking
|
// The root key gets stored in the TUF metadata X509 encoded, linking
|
||||||
// the tuf root.json to our X509 PKI.
|
// 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
|
var changes []changelist.Change
|
||||||
for _, role := range roles {
|
for _, role := range roles {
|
||||||
role = strings.ToLower(role)
|
|
||||||
|
|
||||||
// Ensure we can only add targets to the CanonicalTargetsRole,
|
// Ensure we can only add targets to the CanonicalTargetsRole,
|
||||||
// or a Delegation role (which is <CanonicalTargetsRole>/something else)
|
// or a Delegation role (which is <CanonicalTargetsRole>/something else)
|
||||||
if role != data.CanonicalTargetsRole && !data.IsDelegation(role) {
|
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.
|
// the repository when the changelist gets applied at publish time.
|
||||||
// This does not validate that the delegation exists, since one might exist
|
// This does not validate that the delegation exists, since one might exist
|
||||||
// after applying all changes.
|
// 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) {
|
if !data.IsDelegation(name) {
|
||||||
return data.ErrInvalidRole{Role: name, Reason: "invalid delegation role 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()
|
defer cl.Close()
|
||||||
|
|
||||||
logrus.Debugf(`Removing delegation "%s"\n`, name)
|
logrus.Debugf(`Removing delegation "%s"\n`, name)
|
||||||
|
var template *changelist.TufChange
|
||||||
|
|
||||||
template := changelist.NewTufChange(
|
// 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,
|
changelist.ActionDelete,
|
||||||
name,
|
name,
|
||||||
changelist.TypeTargetsDelegation,
|
changelist.TypeTargetsDelegation,
|
||||||
"", // no path
|
"", // no path
|
||||||
nil,
|
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)
|
return addChange(cl, template, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTarget creates new changelist entries to add a target to the given roles
|
// 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".
|
// If roles are unspecified, the default role is "targets".
|
||||||
func (r *NotaryRepository) AddTarget(target *Target, roles ...string) error {
|
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 {
|
for _, role := range roles {
|
||||||
// we don't need to do anything special with removing role from
|
// we don't need to do anything special with removing role from
|
||||||
// roles because listSubtree always processes role and only excludes
|
// 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...)
|
r.listSubtree(targets, role, roles...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,6 +532,92 @@ func (r *NotaryRepository) GetChangelist() (changelist.Changelist, error) {
|
||||||
return cl, nil
|
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
|
// Publish pushes the local changes in signed material to the remote notary-server
|
||||||
// Conceptually it performs an operation similar to a `git rebase`
|
// Conceptually it performs an operation similar to a `git rebase`
|
||||||
func (r *NotaryRepository) Publish() error {
|
func (r *NotaryRepository) Publish() error {
|
||||||
|
@ -837,7 +946,7 @@ func (r *NotaryRepository) validateRoot(rootJSON []byte) (*data.SignedRoot, erro
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = r.CertManager.ValidateRoot(root, r.gun)
|
err = certs.ValidateRoot(r.CertStore, root, r.gun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -904,3 +1013,27 @@ func (r *NotaryRepository) rootFileKeyChange(role, action string, key data.Publi
|
||||||
}
|
}
|
||||||
return nil
|
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"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
@ -85,13 +86,13 @@ func changeTargetsDelegation(repo *tuf.Repo, c changelist.Change) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// role existed
|
// role existed, attempt to merge paths and keys
|
||||||
return data.ErrInvalidRole{
|
if err := r.AddPaths(td.AddPaths); err != nil {
|
||||||
Role: c.Scope(),
|
return err
|
||||||
Reason: "cannot create a role that already exists",
|
|
||||||
}
|
}
|
||||||
|
return repo.UpdateDelegations(r, td.AddKeys)
|
||||||
}
|
}
|
||||||
// role doesn't exist, create brand new
|
// create brand new role
|
||||||
r, err = td.ToNewRole(c.Scope())
|
r, err = td.ToNewRole(c.Scope())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -107,7 +108,12 @@ func changeTargetsDelegation(repo *tuf.Repo, c changelist.Change) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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 {
|
if err := r.AddPaths(td.AddPaths); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,14 @@ package notary
|
||||||
|
|
||||||
// application wide constants
|
// application wide constants
|
||||||
const (
|
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
|
PrivKeyPerms = 0700
|
||||||
|
// PubCertPerms are the file permissions to use when writing public certificates to disk
|
||||||
PubCertPerms = 0755
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to add key to filestore: %v", err)
|
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.
|
// 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
|
// GetIntermediateCerts parses a list of x509 Certificates and returns all of the
|
||||||
// ones marked as a CA, to be used as intermediates
|
// 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 {
|
for _, cert := range certs {
|
||||||
if cert.IsCA {
|
if cert.IsCA {
|
||||||
intCerts = append(intCerts, cert)
|
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
|
// GenerateRSAKey generates an RSA private key and returns a TUF PrivateKey
|
||||||
func GenerateRSAKey(random io.Reader, bits int) (data.PrivateKey, error) {
|
func GenerateRSAKey(random io.Reader, bits int) (data.PrivateKey, error) {
|
||||||
rsaPrivKey, err := rsa.GenerateKey(random, bits)
|
rsaPrivKey, err := rsa.GenerateKey(random, bits)
|
||||||
|
@ -532,3 +571,14 @@ func X509PublicKeyID(certPubKey data.PublicKey) (string, error) {
|
||||||
|
|
||||||
return key.ID(), nil
|
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 {
|
if err != nil {
|
||||||
logrus.Debug("Error occurred. Root will be downloaded and another update attempted")
|
logrus.Debug("Error occurred. Root will be downloaded and another update attempted")
|
||||||
if err := c.downloadRoot(); err != nil {
|
if err := c.downloadRoot(); err != nil {
|
||||||
logrus.Error("client Update (Root):", err)
|
logrus.Error("Client Update (Root):", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// If we error again, we now have the latest root and just want to fail
|
// 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 may not have a cached timestamp if this is the first time
|
||||||
// we're interacting with the repo. This will result in the
|
// we're interacting with the repo. This will result in the
|
||||||
// version being 0
|
// version being 0
|
||||||
var download bool
|
var (
|
||||||
old := &data.Signed{}
|
saveToCache bool
|
||||||
version := 0
|
old *data.Signed
|
||||||
|
version = 0
|
||||||
|
)
|
||||||
cachedTS, err := c.cache.GetMeta(role, maxSize)
|
cachedTS, err := c.cache.GetMeta(role, maxSize)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err := json.Unmarshal(cachedTS, old)
|
cached := &data.Signed{}
|
||||||
|
err := json.Unmarshal(cachedTS, cached)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
ts, err := data.TimestampFromSigned(old)
|
ts, err := data.TimestampFromSigned(cached)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
version = ts.Signed.Version
|
version = ts.Signed.Version
|
||||||
}
|
}
|
||||||
} else {
|
old = cached
|
||||||
old = nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// unlike root, targets and snapshot, always try and download timestamps
|
// unlike root, targets and snapshot, always try and download timestamps
|
||||||
// from remote, only using the cache one if we couldn't reach remote.
|
// from remote, only using the cache one if we couldn't reach remote.
|
||||||
raw, s, err := c.downloadSigned(role, maxSize, nil)
|
raw, s, err := c.downloadSigned(role, maxSize, nil)
|
||||||
if err != nil || len(raw) == 0 {
|
if err != nil || len(raw) == 0 {
|
||||||
if err, ok := err.(store.ErrMetaNotFound); ok {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if old == nil {
|
if old == nil {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// couldn't retrieve data from server and don't have valid
|
// couldn't retrieve data from server and don't have valid
|
||||||
|
@ -277,17 +276,18 @@ func (c *Client) downloadTimestamp() error {
|
||||||
}
|
}
|
||||||
return err
|
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
|
s = old
|
||||||
} else {
|
} else {
|
||||||
download = true
|
saveToCache = true
|
||||||
}
|
}
|
||||||
err = signed.Verify(s, role, version, c.keysDB)
|
err = signed.Verify(s, role, version, c.keysDB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logrus.Debug("successfully verified timestamp")
|
logrus.Debug("successfully verified timestamp")
|
||||||
if download {
|
if saveToCache {
|
||||||
c.cache.SetMeta(role, raw)
|
c.cache.SetMeta(role, raw)
|
||||||
}
|
}
|
||||||
ts, err := data.TimestampFromSigned(s)
|
ts, err := data.TimestampFromSigned(s)
|
||||||
|
@ -327,7 +327,7 @@ func (c *Client) downloadSnapshot() error {
|
||||||
}
|
}
|
||||||
err := json.Unmarshal(raw, old)
|
err := json.Unmarshal(raw, old)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
snap, err := data.TimestampFromSigned(old)
|
snap, err := data.SnapshotFromSigned(old)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
version = snap.Signed.Version
|
version = snap.Signed.Version
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/agl/ed25519"
|
"github.com/agl/ed25519"
|
||||||
"github.com/jfrazelle/go/canonical/json"
|
"github.com/docker/go/canonical/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PublicKey is the necessary interface for public keys
|
// PublicKey is the necessary interface for public keys
|
||||||
|
|
|
@ -2,6 +2,7 @@ package data
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -109,10 +110,7 @@ func NewRole(name string, threshold int, keyIDs, paths, pathHashPrefixes []strin
|
||||||
}
|
}
|
||||||
if IsDelegation(name) {
|
if IsDelegation(name) {
|
||||||
if len(paths) == 0 && len(pathHashPrefixes) == 0 {
|
if len(paths) == 0 && len(pathHashPrefixes) == 0 {
|
||||||
return nil, ErrInvalidRole{
|
logrus.Debugf("role %s with no Paths and no PathHashPrefixes will never be able to publish content until one or more are added", name)
|
||||||
Role: name,
|
|
||||||
Reason: "roles with no Paths and no PathHashPrefixes will never be able to publish content",
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if threshold < 1 {
|
if threshold < 1 {
|
||||||
|
|
|
@ -3,7 +3,7 @@ package data
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jfrazelle/go/canonical/json"
|
"github.com/docker/go/canonical/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SignedRoot is a fully unpacked root.json
|
// SignedRoot is a fully unpacked root.json
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package data
|
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
|
// Serializer is an interface that can marshal and unmarshal TUF data. This
|
||||||
// is expected to be a canonical JSON marshaller
|
// is expected to be a canonical JSON marshaller
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/jfrazelle/go/canonical/json"
|
"github.com/docker/go/canonical/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SignedSnapshot is a fully unpacked snapshot.json
|
// SignedSnapshot is a fully unpacked snapshot.json
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/jfrazelle/go/canonical/json"
|
"github.com/docker/go/canonical/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SignedTargets is a fully unpacked targets.json, or target delegation
|
// SignedTargets is a fully unpacked targets.json, or target delegation
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jfrazelle/go/canonical/json"
|
"github.com/docker/go/canonical/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SignedTimestamp is a fully unpacked timestamp.json
|
// SignedTimestamp is a fully unpacked timestamp.json
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/jfrazelle/go/canonical/json"
|
"github.com/docker/go/canonical/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SigAlgorithm for types of signatures
|
// SigAlgorithm for types of signatures
|
||||||
|
|
|
@ -58,6 +58,15 @@ func (db *KeyDB) AddRole(r *data.Role) error {
|
||||||
return nil
|
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
|
// GetKey pulls a key out of the database by its ID
|
||||||
func (db *KeyDB) GetKey(id string) data.PublicKey {
|
func (db *KeyDB) GetKey(id string) data.PublicKey {
|
||||||
return db.keys[id]
|
return db.keys[id]
|
||||||
|
|
|
@ -6,9 +6,9 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/go/canonical/json"
|
||||||
"github.com/docker/notary/tuf/data"
|
"github.com/docker/notary/tuf/data"
|
||||||
"github.com/docker/notary/tuf/keys"
|
"github.com/docker/notary/tuf/keys"
|
||||||
"github.com/jfrazelle/go/canonical/json"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Various basic signing errors
|
// Various basic signing errors
|
||||||
|
|
|
@ -39,11 +39,14 @@ type FilesystemStore struct {
|
||||||
targetsDir string
|
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)
|
// GetMeta returns the meta for the given name (a role)
|
||||||
func (f *FilesystemStore) GetMeta(name string, size int64) ([]byte, error) {
|
func (f *FilesystemStore) GetMeta(name string, size int64) ([]byte, error) {
|
||||||
fileName := fmt.Sprintf("%s.%s", name, f.metaExtension)
|
meta, err := ioutil.ReadFile(f.getPath(name))
|
||||||
path := filepath.Join(f.metaDir, fileName)
|
|
||||||
meta, err := ioutil.ReadFile(path)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
err = ErrMetaNotFound{Resource: name}
|
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
|
// SetMeta sets the meta for a single role
|
||||||
func (f *FilesystemStore) SetMeta(name string, meta []byte) error {
|
func (f *FilesystemStore) SetMeta(name string, meta []byte) error {
|
||||||
fileName := fmt.Sprintf("%s.%s", name, f.metaExtension)
|
fp := f.getPath(name)
|
||||||
path := filepath.Join(f.metaDir, fileName)
|
|
||||||
|
|
||||||
// Ensures the parent directories of the file we are about to write exist
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// if something already exists, just delete it and re-write it
|
// if something already exists, just delete it and re-write it
|
||||||
os.RemoveAll(path)
|
os.RemoveAll(fp)
|
||||||
|
|
||||||
// Write the file to disk
|
// 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 err
|
||||||
}
|
}
|
||||||
return nil
|
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() {
|
if !base.IsAbs() {
|
||||||
return nil, errors.New("HTTPStore requires an absolute baseURL")
|
return nil, errors.New("HTTPStore requires an absolute baseURL")
|
||||||
}
|
}
|
||||||
|
if roundTrip == nil {
|
||||||
|
return &OfflineStore{}, nil
|
||||||
|
}
|
||||||
return &HTTPStore{
|
return &HTTPStore{
|
||||||
baseURL: *base,
|
baseURL: *base,
|
||||||
metaPrefix: metaPrefix,
|
metaPrefix: metaPrefix,
|
||||||
|
@ -182,6 +185,12 @@ func (s HTTPStore) SetMeta(name string, blob []byte) error {
|
||||||
return translateStatusToError(resp, "POST "+name)
|
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
|
// NewMultiPartMetaRequest builds a request with the provided metadata updates
|
||||||
// in multipart form
|
// in multipart form
|
||||||
func NewMultiPartMetaRequest(url string, metas map[string][]byte) (*http.Request, error) {
|
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")
|
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) {
|
func (s HTTPStore) buildMetaURL(name string) (*url.URL, error) {
|
||||||
var filename string
|
var filename string
|
||||||
if name != "" {
|
if name != "" {
|
||||||
|
|
|
@ -14,6 +14,8 @@ type MetadataStore interface {
|
||||||
GetMeta(name string, size int64) ([]byte, error)
|
GetMeta(name string, size int64) ([]byte, error)
|
||||||
SetMeta(name string, blob []byte) error
|
SetMeta(name string, blob []byte) error
|
||||||
SetMultiMeta(map[string][]byte) error
|
SetMultiMeta(map[string][]byte) error
|
||||||
|
RemoveAll() error
|
||||||
|
RemoveMeta(name string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublicKeyStore must be implemented by a key service
|
// PublicKeyStore must be implemented by a key service
|
||||||
|
|
|
@ -54,6 +54,13 @@ func (m *memoryStore) SetMultiMeta(metas map[string][]byte) error {
|
||||||
return nil
|
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) {
|
func (m *memoryStore) GetTarget(path string) (io.ReadCloser, error) {
|
||||||
return &utils.NoopCloser{Reader: bytes.NewReader(m.files[path])}, nil
|
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) {
|
func (m *memoryStore) GetKey(role string) ([]byte, error) {
|
||||||
return nil, fmt.Errorf("GetKey is not implemented for the memoryStore")
|
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{}
|
var err = ErrOffline{}
|
||||||
|
|
||||||
// OfflineStore is to be used as a placeholder for a nil store. It simply
|
// 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{}
|
type OfflineStore struct{}
|
||||||
|
|
||||||
// GetMeta return ErrOffline
|
// GetMeta returns ErrOffline
|
||||||
func (es OfflineStore) GetMeta(name string, size int64) ([]byte, error) {
|
func (es OfflineStore) GetMeta(name string, size int64) ([]byte, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMeta return ErrOffline
|
// SetMeta returns ErrOffline
|
||||||
func (es OfflineStore) SetMeta(name string, blob []byte) error {
|
func (es OfflineStore) SetMeta(name string, blob []byte) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMultiMeta return ErrOffline
|
// SetMultiMeta returns ErrOffline
|
||||||
func (es OfflineStore) SetMultiMeta(map[string][]byte) error {
|
func (es OfflineStore) SetMultiMeta(map[string][]byte) error {
|
||||||
return err
|
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) {
|
func (es OfflineStore) GetKey(role string) ([]byte, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTarget return ErrOffline
|
// GetTarget returns ErrOffline
|
||||||
func (es OfflineStore) GetTarget(path string) (io.ReadCloser, error) {
|
func (es OfflineStore) GetTarget(path string) (io.ReadCloser, error) {
|
||||||
return nil, err
|
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
|
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
|
// GetDelegation finds the role entry representing the provided
|
||||||
// role name or ErrInvalidRole
|
// role name or ErrInvalidRole
|
||||||
func (tr *Repo) GetDelegation(role string) (*data.Role, error) {
|
func (tr *Repo) GetDelegation(role string) (*data.Role, error) {
|
||||||
|
|
Loading…
Reference in New Issue