Example #1
0
// RootHandler returns the handler that routes all the paths from / for the
// server.
func RootHandler(ac auth.AccessController, ctx context.Context, trust signed.CryptoService) http.Handler {
	hand := utils.RootHandlerFactory(ac, ctx, trust)

	r := mux.NewRouter()
	r.Methods("GET").Path("/v2/").Handler(hand(handlers.MainHandler))
	r.Methods("POST").Path("/v2/{imageName:.*}/_trust/tuf/").Handler(
		prometheus.InstrumentHandlerWithOpts(
			prometheusOpts("UpdateTuf"),
			hand(handlers.AtomicUpdateHandler, "push", "pull")))
	r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/{tufRole:root|targets(?:/[^/\\s]+)*|snapshot|timestamp}.{checksum:[a-fA-F0-9]{64}|[a-fA-F0-9]{96}|[a-fA-F0-9]{128}}.json").Handler(
		prometheus.InstrumentHandlerWithOpts(
			prometheusOpts("GetRoleByHash"),
			hand(handlers.GetHandler, "pull")))
	r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/{tufRole:root|targets(?:/[^/\\s]+)*|snapshot|timestamp}.json").Handler(
		prometheus.InstrumentHandlerWithOpts(
			prometheusOpts("GetRole"),
			hand(handlers.GetHandler, "pull")))
	r.Methods("GET").Path(
		"/v2/{imageName:.*}/_trust/tuf/{tufRole:snapshot|timestamp}.key").Handler(
		prometheus.InstrumentHandlerWithOpts(
			prometheusOpts("GetKey"),
			hand(handlers.GetKeyHandler, "push", "pull")))
	r.Methods("DELETE").Path("/v2/{imageName:.*}/_trust/tuf/").Handler(
		prometheus.InstrumentHandlerWithOpts(
			prometheusOpts("DeleteTuf"),
			hand(handlers.DeleteHandler, "push", "pull")))

	r.Methods("GET").Path("/_notary_server/health").HandlerFunc(health.StatusHandler)
	r.Methods("GET").Path("/metrics").Handler(prometheus.Handler())
	r.Methods("GET", "POST", "PUT", "HEAD", "DELETE").Path("/{other:.*}").Handler(
		hand(handlers.NotFoundHandler))

	return r
}
Example #2
0
// RootHandler returns the handler that routes all the paths from / for the
// server.
func RootHandler(ac auth.AccessController, ctx context.Context, trust signed.CryptoService) http.Handler {
	hand := utils.RootHandlerFactory(ac, ctx, trust)

	r := mux.NewRouter()
	r.Methods("GET").Path("/v2/").Handler(hand(handlers.MainHandler))
	r.Methods("POST").Path("/v2/{imageName:.*}/_trust/tuf/").Handler(
		prometheus.InstrumentHandlerWithOpts(
			prometheusOpts("UpdateTuf"),
			hand(handlers.AtomicUpdateHandler, "push", "pull")))
	r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/{tufRole:(root|targets|snapshot)}.json").Handler(
		prometheus.InstrumentHandlerWithOpts(
			prometheusOpts("GetRole"),
			hand(handlers.GetHandler, "pull")))
	r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/timestamp.json").Handler(
		prometheus.InstrumentHandlerWithOpts(
			prometheusOpts("GetTimestamp"),
			hand(handlers.GetTimestampHandler, "pull")))
	r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/timestamp.key").Handler(
		prometheus.InstrumentHandlerWithOpts(
			prometheusOpts("GetTimestampKey"),
			hand(handlers.GetTimestampKeyHandler, "push", "pull")))
	r.Methods("DELETE").Path("/v2/{imageName:.*}/_trust/tuf/").Handler(
		prometheus.InstrumentHandlerWithOpts(
			prometheusOpts("DeleteTuf"),
			hand(handlers.DeleteHandler, "push", "pull")))
	r.Methods("GET").Path("/_notary_server/health").Handler(hand(
		func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
			health.StatusHandler(w, r)
			return nil
		}))
	r.Methods("GET").Path("/_notary_server/metrics").Handler(prometheus.Handler())
	r.Methods("GET", "POST", "PUT", "HEAD", "DELETE").Path("/{other:.*}").Handler(hand(utils.NotFoundHandler))

	return r
}
Example #3
0
func TestMainHandlerGet(t *testing.T) {
	hand := utils.RootHandlerFactory(nil, context.Background(), &signed.Ed25519{})
	handler := hand(MainHandler)
	ts := httptest.NewServer(handler)
	defer ts.Close()

	_, err := http.Get(ts.URL)
	if err != nil {
		t.Fatalf("Received error on GET /: %s", err.Error())
	}
}
Example #4
0
func TestMainHandlerNotGet(t *testing.T) {
	hand := utils.RootHandlerFactory(nil, context.Background(), &signed.Ed25519{})
	handler := hand(MainHandler)
	ts := httptest.NewServer(handler)
	defer ts.Close()

	res, err := http.Head(ts.URL)
	if err != nil {
		t.Fatalf("Received error on GET /: %s", err.Error())
	}
	if res.StatusCode != http.StatusNotFound {
		t.Fatalf("Expected 404, received %d", res.StatusCode)
	}
}
Example #5
0
func testPublish(t *testing.T, rootType data.KeyAlgorithm) {
	// Temporary directory where test files will be created
	tempBaseDir, err := ioutil.TempDir("", "notary-test-")
	defer os.RemoveAll(tempBaseDir)

	assert.NoError(t, err, "failed to create a temporary directory: %s", err)

	gun := "docker.com/notary"

	// Set up server
	ctx := context.WithValue(context.Background(), "metaStore", storage.NewMemStorage())

	// Do not pass one of the const KeyAlgorithms here as the value! Passing a
	// string is in itself good test that we are handling it correctly as we will
	// be receiving a string from the configuration.
	ctx = context.WithValue(ctx, "keyAlgorithm", "ecdsa")

	hand := utils.RootHandlerFactory(nil, ctx,
		cryptoservice.NewCryptoService("", trustmanager.NewKeyMemoryStore(passphraseRetriever)))

	r := mux.NewRouter()
	r.Methods("POST").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/").Handler(hand(handlers.AtomicUpdateHandler, "push", "pull"))
	r.Methods("GET").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/{tufRole:(root|targets|snapshot)}.json").Handler(hand(handlers.GetHandler, "pull"))
	r.Methods("GET").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/timestamp.json").Handler(hand(handlers.GetTimestampHandler, "pull"))
	r.Methods("GET").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/timestamp.key").Handler(hand(handlers.GetTimestampKeyHandler, "push", "pull"))
	//r.Methods("POST").Path("/v2/{imageName:" + server.RepositoryNameRegexp + "}/_trust/tuf/{tufRole:(root|targets|timestamp|snapshot)}.json").Handler(hand(handlers.UpdateHandler, "push", "pull"))
	r.Methods("DELETE").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/").Handler(hand(handlers.DeleteHandler, "push", "pull"))

	ts := httptest.NewServer(r)

	repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, passphraseRetriever)
	assert.NoError(t, err, "error creating repository: %s", err)

	rootKeyID, err := repo.KeyStoreManager.GenRootKey(rootType.String())
	assert.NoError(t, err, "error generating root key: %s", err)

	rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID)
	assert.NoError(t, err, "error retreiving root key: %s", err)

	err = repo.Initialize(rootCryptoService)
	assert.NoError(t, err, "error creating repository: %s", err)

	// Add fixtures/intermediate-ca.crt as a target. There's no particular reason
	// for using this file except that it happens to be available as
	// a fixture.
	latestTarget, err := NewTarget("latest", "../fixtures/intermediate-ca.crt")
	assert.NoError(t, err, "error creating target")
	err = repo.AddTarget(latestTarget)
	assert.NoError(t, err, "error adding target")

	// Look for the changelist file
	changelistDirPath := filepath.Join(tempBaseDir, "tuf", filepath.FromSlash(gun), "changelist")

	changelistDir, err := os.Open(changelistDirPath)
	assert.NoError(t, err, "could not open changelist directory")

	fileInfos, err := changelistDir.Readdir(0)
	assert.NoError(t, err, "could not read changelist directory")

	// Should only be one file in the directory
	assert.Len(t, fileInfos, 1, "wrong number of changelist files found")

	clName := fileInfos[0].Name()
	raw, err := ioutil.ReadFile(filepath.Join(changelistDirPath, clName))
	assert.NoError(t, err, "could not read changelist file %s", clName)

	c := &changelist.TufChange{}
	err = json.Unmarshal(raw, c)
	assert.NoError(t, err, "could not unmarshal changelist file %s", clName)

	assert.EqualValues(t, changelist.ActionCreate, c.Actn)
	assert.Equal(t, "targets", c.Role)
	assert.Equal(t, "target", c.ChangeType)
	assert.Equal(t, "latest", c.ChangePath)
	assert.NotEmpty(t, c.Data)

	changelistDir.Close()

	// Create a second target
	currentTarget, err := NewTarget("current", "../fixtures/intermediate-ca.crt")
	assert.NoError(t, err, "error creating target")
	err = repo.AddTarget(currentTarget)
	assert.NoError(t, err, "error adding target")

	changelistDir, err = os.Open(changelistDirPath)
	assert.NoError(t, err, "could not open changelist directory")

	// There should now be a second file in the directory
	fileInfos, err = changelistDir.Readdir(0)
	assert.NoError(t, err, "could not read changelist directory")

	assert.Len(t, fileInfos, 2, "wrong number of changelist files found")

	newFileFound := false
	for _, fileInfo := range fileInfos {
		if fileInfo.Name() != clName {
			clName2 := fileInfo.Name()
			raw, err := ioutil.ReadFile(filepath.Join(changelistDirPath, clName2))
			assert.NoError(t, err, "could not read changelist file %s", clName2)

			c := &changelist.TufChange{}
			err = json.Unmarshal(raw, c)
			assert.NoError(t, err, "could not unmarshal changelist file %s", clName2)

			assert.EqualValues(t, changelist.ActionCreate, c.Actn)
			assert.Equal(t, "targets", c.Role)
			assert.Equal(t, "target", c.ChangeType)
			assert.Equal(t, "current", c.ChangePath)
			assert.NotEmpty(t, c.Data)

			newFileFound = true
			break
		}
	}

	assert.True(t, newFileFound, "second changelist file not found")

	changelistDir.Close()

	// Now test Publish
	err = repo.Publish()
	assert.NoError(t, err)

	changelistDir, err = os.Open(changelistDirPath)
	assert.NoError(t, err, "could not open changelist directory")
	fileInfos, err = changelistDir.Readdir(0)
	assert.NoError(t, err, "could not read changelist directory")
	// Should only be one file in the directory
	assert.Len(t, fileInfos, 0, "wrong number of changelist files found")

	// Create a new repo and pull from the server
	tempBaseDir2, err := ioutil.TempDir("", "notary-test-")
	defer os.RemoveAll(tempBaseDir2)

	assert.NoError(t, err, "failed to create a temporary directory: %s", err)

	repo2, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, passphraseRetriever)
	assert.NoError(t, err, "error creating repository: %s", err)

	targets, err := repo2.ListTargets()
	assert.NoError(t, err)

	// Should be two targets
	assert.Len(t, targets, 2, "unexpected number of targets returned by ListTargets")

	if targets[0].Name == "latest" {
		assert.Equal(t, latestTarget, targets[0], "latest target does not match")
		assert.Equal(t, currentTarget, targets[1], "current target does not match")
	} else if targets[0].Name == "current" {
		assert.Equal(t, currentTarget, targets[0], "current target does not match")
		assert.Equal(t, latestTarget, targets[1], "latest target does not match")
	} else {
		t.Fatalf("unexpected target name: %s", targets[0].Name)
	}

	// Also test GetTargetByName
	newLatestTarget, err := repo2.GetTargetByName("latest")
	assert.NoError(t, err)
	assert.Equal(t, latestTarget, newLatestTarget, "latest target does not match")

	newCurrentTarget, err := repo2.GetTargetByName("current")
	assert.NoError(t, err)
	assert.Equal(t, currentTarget, newCurrentTarget, "current target does not match")
}
Example #6
0
// Run sets up and starts a TLS server that can be cancelled using the
// given configuration. The context it is passed is the context it should
// use directly for the TLS server, and generate children off for requests
func Run(ctx context.Context, addr, tlsCertFile, tlsKeyFile string, trust signed.CryptoService, authMethod string, authOpts interface{}) error {

	tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		return err
	}
	var lsnr net.Listener
	lsnr, err = net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		return err
	}

	if tlsCertFile != "" && tlsKeyFile != "" {
		keypair, err := tls.LoadX509KeyPair(tlsCertFile, tlsKeyFile)
		if err != nil {
			return err
		}
		tlsConfig := &tls.Config{
			MinVersion:               tls.VersionTLS12,
			PreferServerCipherSuites: true,
			CipherSuites: []uint16{
				tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
				tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
				tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
				tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
				tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
				tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
				tls.TLS_RSA_WITH_AES_128_CBC_SHA,
				tls.TLS_RSA_WITH_AES_256_CBC_SHA,
			},
			Certificates: []tls.Certificate{keypair},
			Rand:         rand.Reader,
		}

		logrus.Info("Enabling TLS")
		lsnr = tls.NewListener(lsnr, tlsConfig)
	} else if tlsCertFile != "" || tlsKeyFile != "" {
		return fmt.Errorf("Partial TLS configuration found. Either include both a cert and key file in the configuration, or include neither to disable TLS.")
	}

	var ac auth.AccessController
	if authMethod == "token" {
		authOptions, ok := authOpts.(map[string]interface{})
		if !ok {
			return fmt.Errorf("auth.options must be a map[string]interface{}")
		}
		ac, err = auth.GetAccessController(authMethod, authOptions)
		if err != nil {
			return err
		}
	}
	hand := utils.RootHandlerFactory(ac, ctx, trust)

	r := mux.NewRouter()
	r.Methods("GET").Path("/v2/").Handler(hand(handlers.MainHandler))
	r.Methods("POST").Path("/v2/{imageName:.*}/_trust/tuf/").Handler(hand(handlers.AtomicUpdateHandler, "push", "pull"))
	r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/{tufRole:(root|targets|snapshot)}.json").Handler(hand(handlers.GetHandler, "pull"))
	r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/timestamp.json").Handler(hand(handlers.GetTimestampHandler, "pull"))
	r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/timestamp.key").Handler(hand(handlers.GetTimestampKeyHandler, "push", "pull"))
	r.Methods("DELETE").Path("/v2/{imageName:.*}/_trust/tuf/").Handler(hand(handlers.DeleteHandler, "push", "pull"))
	r.Methods("GET", "POST", "PUT", "HEAD", "DELETE").Path("/{other:.*}").Handler(hand(utils.NotFoundHandler))
	svr := http.Server{
		Addr:    addr,
		Handler: r,
	}

	logrus.Info("Starting on ", addr)

	err = svr.Serve(lsnr)

	return err
}
Example #7
0
// Run sets up and starts a TLS server that can be cancelled using the
// given configuration. The context it is passed is the context it should
// use directly for the TLS server, and generate children off for requests
func Run(ctx context.Context, addr, tlsCertFile, tlsKeyFile string, trust signed.CryptoService, authMethod string, authOpts interface{}) error {

	tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		return err
	}
	var lsnr net.Listener
	lsnr, err = net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		return err
	}

	keypair, err := tls.LoadX509KeyPair(tlsCertFile, tlsKeyFile)
	if err != nil {
		// must be able to run without certs. In prod, users may
		// want load balancer to terminate TLS
		logrus.Errorf("[Notary Server] Error loading TLS keys %s", err)
	} else {

		tlsConfig := &tls.Config{
			MinVersion:               tls.VersionTLS12,
			PreferServerCipherSuites: true,
			CipherSuites: []uint16{
				tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
				tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
				tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
				tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
				tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
				tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
				tls.TLS_RSA_WITH_AES_128_CBC_SHA,
				tls.TLS_RSA_WITH_AES_256_CBC_SHA,
			},
			Certificates: []tls.Certificate{keypair},
			Rand:         rand.Reader,
		}

		lsnr = tls.NewListener(lsnr, tlsConfig)
	}

	var ac auth.AccessController
	if authMethod == "" {
		ac = nil
	} else {
		authOptions, ok := authOpts.(map[string]interface{})
		if !ok {
			return fmt.Errorf("auth.options must be a map[string]interface{}")
		}
		ac, err = auth.GetAccessController(authMethod, authOptions)
		if err != nil {
			return err
		}
	}
	hand := utils.RootHandlerFactory(ac, ctx, trust)

	r := mux.NewRouter()
	r.Methods("GET").Path("/v2/").Handler(hand(handlers.MainHandler))
	r.Methods("POST").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/").Handler(hand(handlers.AtomicUpdateHandler, "push", "pull"))
	r.Methods("GET").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/{tufRole:(root|targets|snapshot)}.json").Handler(hand(handlers.GetHandler, "pull"))
	r.Methods("GET").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/timestamp.json").Handler(hand(handlers.GetTimestampHandler, "pull"))
	r.Methods("GET").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/timestamp.key").Handler(hand(handlers.GetTimestampKeyHandler, "push", "pull"))
	r.Methods("DELETE").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/").Handler(hand(handlers.DeleteHandler, "push", "pull"))

	svr := http.Server{
		Addr:    addr,
		Handler: r,
	}

	logrus.Info("[Notary Server] : Starting on ", addr)

	err = svr.Serve(lsnr)

	return err
}
Example #8
0
func run(ctx context.Context, addr, tlsCertFile, tlsKeyFile string, trust signed.CryptoService) error {

	keypair, err := tls.LoadX509KeyPair(tlsCertFile, tlsKeyFile)
	if err != nil {
		logrus.Errorf("[Notary Server] Error loading keys %s", err)
		return err
	}

	tlsConfig := &tls.Config{
		MinVersion:               tls.VersionTLS12,
		PreferServerCipherSuites: true,
		CipherSuites: []uint16{
			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
			tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
			tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
			tls.TLS_RSA_WITH_AES_128_CBC_SHA,
			tls.TLS_RSA_WITH_AES_256_CBC_SHA,
		},
		Certificates: []tls.Certificate{keypair},
		Rand:         rand.Reader,
	}

	tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		return err
	}
	lsnr, err := net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		return err
	}
	tlsLsnr := tls.NewListener(lsnr, tlsConfig)

	var ac auth.AccessController = nil
	//ac, err := auth.GetAccessController("token", map[string]interface{}{})
	//if err != nil {
	//	return err
	//}
	hand := utils.RootHandlerFactory(ac, ctx, trust)

	r := mux.NewRouter()
	// TODO (endophage): use correct regexes for image and tag names
	r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/{tufRole:(root|targets|timestamp|snapshot)}.json").Handler(hand(handlers.GetHandler, "pull"))
	r.Methods("POST").Path("/v2/{imageName:.*}/_trust/tuf/{tufRole:(root|targets|timestamp|snapshot)}.json").Handler(hand(handlers.UpdateHandler, "push", "pull"))
	r.Methods("DELETE").Path("/v2/{imageName:.*}/_trust/tuf/").Handler(hand(handlers.DeleteHandler, "push", "pull"))

	svr := NewHTTPServer(
		http.Server{
			Addr:    addr,
			Handler: r,
		},
	)

	logrus.Info("[Notary Server] : Listening on", addr)

	go stopWatcher(ctx, svr, lsnr, tlsLsnr)

	err = svr.Serve(tlsLsnr)

	return err
}
Example #9
0
// RootHandler returns the handler that routes all the paths from / for the
// server.
func RootHandler(ctx context.Context, ac auth.AccessController, trust signed.CryptoService,
	consistent, current utils.CacheControlConfig, repoPrefixes []string) http.Handler {

	authWrapper := utils.RootHandlerFactory(ctx, ac, trust)

	createHandler := func(opts _serverEndpoint) http.Handler {
		var wrapped http.Handler
		wrapped = authWrapper(opts.ServerHandler, opts.PermissionsRequired...)
		if opts.IncludeCacheHeaders {
			wrapped = utils.WrapWithCacheHandler(opts.CacheControlConfig, wrapped)
		}
		wrapped = filterImagePrefixes(repoPrefixes, opts.ErrorIfGUNInvalid, wrapped)
		return prometheus.InstrumentHandlerWithOpts(prometheusOpts(opts.OperationName), wrapped)
	}

	invalidGUNErr := errors.ErrInvalidGUN.WithDetail(fmt.Sprintf("Require GUNs with prefix: %v", repoPrefixes))
	notFoundError := errors.ErrMetadataNotFound.WithDetail(nil)

	r := mux.NewRouter()
	r.Methods("GET").Path("/v2/").Handler(authWrapper(handlers.MainHandler))

	r.Methods("POST").Path("/v2/{imageName:[^*]+}/_trust/tuf/").Handler(createHandler(_serverEndpoint{
		OperationName:       "UpdateTUF",
		ErrorIfGUNInvalid:   invalidGUNErr,
		ServerHandler:       handlers.AtomicUpdateHandler,
		PermissionsRequired: []string{"push", "pull"},
	}))
	r.Methods("GET").Path("/v2/{imageName:[^*]+}/_trust/tuf/{tufRole:root|targets(?:/[^/\\s]+)*|snapshot|timestamp}.{checksum:[a-fA-F0-9]{64}|[a-fA-F0-9]{96}|[a-fA-F0-9]{128}}.json").Handler(createHandler(_serverEndpoint{
		OperationName:       "GetRoleByHash",
		ErrorIfGUNInvalid:   notFoundError,
		IncludeCacheHeaders: true,
		CacheControlConfig:  consistent,
		ServerHandler:       handlers.GetHandler,
		PermissionsRequired: []string{"pull"},
	}))
	r.Methods("GET").Path("/v2/{imageName:[^*]+}/_trust/tuf/{tufRole:root|targets(?:/[^/\\s]+)*|snapshot|timestamp}.json").Handler(createHandler(_serverEndpoint{
		OperationName:       "GetRole",
		ErrorIfGUNInvalid:   notFoundError,
		IncludeCacheHeaders: true,
		CacheControlConfig:  current,
		ServerHandler:       handlers.GetHandler,
		PermissionsRequired: []string{"pull"},
	}))
	r.Methods("GET").Path(
		"/v2/{imageName:[^*]+}/_trust/tuf/{tufRole:snapshot|timestamp}.key").Handler(createHandler(_serverEndpoint{
		OperationName:       "GetKey",
		ErrorIfGUNInvalid:   notFoundError,
		ServerHandler:       handlers.GetKeyHandler,
		PermissionsRequired: []string{"push", "pull"},
	}))
	r.Methods("POST").Path(
		"/v2/{imageName:[^*]+}/_trust/tuf/{tufRole:snapshot|timestamp}.key").Handler(createHandler(_serverEndpoint{
		OperationName:       "RotateKey",
		ErrorIfGUNInvalid:   notFoundError,
		ServerHandler:       handlers.RotateKeyHandler,
		PermissionsRequired: []string{"*"},
	}))
	r.Methods("DELETE").Path("/v2/{imageName:[^*]+}/_trust/tuf/").Handler(createHandler(_serverEndpoint{
		OperationName:       "DeleteTUF",
		ErrorIfGUNInvalid:   notFoundError,
		ServerHandler:       handlers.DeleteHandler,
		PermissionsRequired: []string{"*"},
	}))
	r.Methods("GET").Path("/v2/{imageName:[^*]+}/_trust/changefeed").Handler(createHandler(_serverEndpoint{
		OperationName:       "Changefeed",
		ErrorIfGUNInvalid:   notFoundError,
		ServerHandler:       handlers.Changefeed,
		PermissionsRequired: []string{"pull"},
	}))
	r.Methods("GET").Path("/v2/_trust/changefeed").Handler(createHandler(_serverEndpoint{
		OperationName:       "Changefeed",
		ServerHandler:       handlers.Changefeed,
		PermissionsRequired: []string{"*"},
	}))

	r.Methods("GET").Path("/_notary_server/health").HandlerFunc(health.StatusHandler)
	r.Methods("GET").Path("/metrics").Handler(prometheus.Handler())
	r.Methods("GET", "POST", "PUT", "HEAD", "DELETE").Path("/{other:.*}").Handler(
		authWrapper(handlers.NotFoundHandler))

	return r
}
Example #10
0
func TestRotate(t *testing.T) {
	// Temporary directory where test files will be created
	tempBaseDir, err := ioutil.TempDir("", "notary-test-")
	defer os.RemoveAll(tempBaseDir)

	assert.NoError(t, err, "failed to create a temporary directory: %s", err)

	gun := "docker.com/notary"

	// Set up server
	ctx := context.WithValue(context.Background(), "metaStore", storage.NewMemStorage())

	// Do not pass one of the const KeyAlgorithms here as the value! Passing a
	// string is in itself good test that we are handling it correctly as we will
	// be receiving a string from the configuration.
	ctx = context.WithValue(ctx, "keyAlgorithm", "ecdsa")

	hand := utils.RootHandlerFactory(nil, ctx,
		cryptoservice.NewCryptoService("", trustmanager.NewKeyMemoryStore(passphraseRetriever)))

	r := mux.NewRouter()
	r.Methods("POST").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/").Handler(hand(handlers.AtomicUpdateHandler, "push", "pull"))
	r.Methods("GET").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/{tufRole:(root|targets|snapshot)}.json").Handler(hand(handlers.GetHandler, "pull"))
	r.Methods("GET").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/timestamp.json").Handler(hand(handlers.GetTimestampHandler, "pull"))
	r.Methods("GET").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/timestamp.key").Handler(hand(handlers.GetTimestampKeyHandler, "push", "pull"))
	//r.Methods("POST").Path("/v2/{imageName:" + server.RepositoryNameRegexp + "}/_trust/tuf/{tufRole:(root|targets|timestamp|snapshot)}.json").Handler(hand(handlers.UpdateHandler, "push", "pull"))
	r.Methods("DELETE").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/").Handler(hand(handlers.DeleteHandler, "push", "pull"))

	ts := httptest.NewServer(r)

	repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, passphraseRetriever)
	assert.NoError(t, err, "error creating repository: %s", err)

	rootKeyID, err := repo.KeyStoreManager.GenRootKey(data.ECDSAKey)
	assert.NoError(t, err, "error generating root key: %s", err)

	err = repo.Initialize(rootKeyID)
	assert.NoError(t, err, "error creating repository: %s", err)

	// Add fixtures/intermediate-ca.crt as a target. There's no particular reason
	// for using this file except that it happens to be available as
	// a fixture.
	// Adding a target will allow us to confirm the repository is still valid after
	// rotating the keys.
	latestTarget, err := NewTarget("latest", "../fixtures/intermediate-ca.crt")
	assert.NoError(t, err, "error creating target")
	err = repo.AddTarget(latestTarget)
	assert.NoError(t, err, "error adding target")

	// Publish
	err = repo.Publish()
	assert.NoError(t, err)

	// Get root.json and capture targets + snapshot key IDs
	repo.GetTargetByName("latest") // force a pull
	targetsKeyIDs := repo.tufRepo.Root.Signed.Roles["targets"].KeyIDs
	snapshotKeyIDs := repo.tufRepo.Root.Signed.Roles["snapshot"].KeyIDs
	assert.Len(t, targetsKeyIDs, 1)
	assert.Len(t, snapshotKeyIDs, 1)

	// Do rotation
	repo.RotateKeys()

	// Publish
	err = repo.Publish()
	assert.NoError(t, err)

	// Get root.json. Check targets + snapshot keys have changed
	// and that they match those found in the changelist.
	_, err = repo.GetTargetByName("latest") // force a pull
	assert.NoError(t, err)
	newTargetsKeyIDs := repo.tufRepo.Root.Signed.Roles["targets"].KeyIDs
	newSnapshotKeyIDs := repo.tufRepo.Root.Signed.Roles["snapshot"].KeyIDs
	assert.Len(t, newTargetsKeyIDs, 1)
	assert.Len(t, newSnapshotKeyIDs, 1)
	assert.NotEqual(t, targetsKeyIDs[0], newTargetsKeyIDs[0])
	assert.NotEqual(t, snapshotKeyIDs[0], newSnapshotKeyIDs[0])

	// Confirm changelist dir empty after publishing changes
	// Look for the changelist file
	changelistDirPath := filepath.Join(tempBaseDir, "tuf", filepath.FromSlash(gun), "changelist")
	changelistDir, err := os.Open(changelistDirPath)
	assert.NoError(t, err, "could not open changelist directory")
	fileInfos, err := changelistDir.Readdir(0)
	assert.NoError(t, err, "could not read changelist directory")
	// Should only be one file in the directory
	assert.Len(t, fileInfos, 0, "wrong number of changelist files found")
}
Example #11
0
// Run sets up and starts a TLS server that can be cancelled using the
// given configuration. The context it is passed is the context it should
// use directly for the TLS server, and generate children off for requests
func Run(ctx context.Context, addr, tlsCertFile, tlsKeyFile string, trust signed.CryptoService) error {

	tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		return err
	}
	var lsnr net.Listener
	lsnr, err = net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		return err
	}

	keypair, err := tls.LoadX509KeyPair(tlsCertFile, tlsKeyFile)
	if err != nil {
		// must be able to run without certs. In prod, users may
		// want load balancer to terminate TLS
		logrus.Errorf("[Notary Server] Error loading TLS keys %s", err)
	} else {

		tlsConfig := &tls.Config{
			MinVersion:               tls.VersionTLS12,
			PreferServerCipherSuites: true,
			CipherSuites: []uint16{
				tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
				tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
				tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
				tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
				tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
				tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
				tls.TLS_RSA_WITH_AES_128_CBC_SHA,
				tls.TLS_RSA_WITH_AES_256_CBC_SHA,
			},
			Certificates: []tls.Certificate{keypair},
			Rand:         rand.Reader,
		}

		lsnr = tls.NewListener(lsnr, tlsConfig)
	}

	var ac auth.AccessController = nil
	hand := utils.RootHandlerFactory(ac, ctx, trust)

	r := mux.NewRouter()
	// TODO (endophage): use correct regexes for image and tag names
	r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/{tufRole:(root|targets|snapshot)}.json").Handler(hand(handlers.GetHandler, "pull"))
	r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/timestamp.json").Handler(hand(handlers.GetTimestampHandler, "pull"))
	r.Methods("GET").Path("/v2/{imageName:.*}/_trust/tuf/timestamp.key").Handler(hand(handlers.GetTimestampKeyHandler, "push", "pull"))
	r.Methods("POST").Path("/v2/{imageName:.*}/_trust/tuf/{tufRole:(root|targets|timestamp|snapshot)}.json").Handler(hand(handlers.UpdateHandler, "push", "pull"))
	r.Methods("DELETE").Path("/v2/{imageName:.*}/_trust/tuf/").Handler(hand(handlers.DeleteHandler, "push", "pull"))

	svr := http.Server{
		Addr:    addr,
		Handler: r,
	}

	logrus.Error("[Notary Server] : Starting on ", addr)

	err = svr.Serve(lsnr)

	return err
}