mirror of
https://github.com/distribution/distribution.git
synced 2025-08-29 03:41:27 +00:00
Use selected fields from app in context to avoid secrets
The app struct includes sensitive data that the handlers don't necessarily need access to, so remove it from the context, and don't pass the app in place of contexts. Where handlers do need configuration data from the app, store it in the handler's own struct instead of relying on it being in context. Signed-off-by: James Hewitt <james.hewitt@uk.ibm.com>
This commit is contained in:
parent
f22dd61860
commit
402ac1fcb6
@ -104,7 +104,6 @@ func SetDefaultLogger(logger Logger) {
|
|||||||
// required.
|
// required.
|
||||||
func getLogrusLogger(ctx context.Context, keys ...interface{}) *logrus.Entry {
|
func getLogrusLogger(ctx context.Context, keys ...interface{}) *logrus.Entry {
|
||||||
var logger *logrus.Entry
|
var logger *logrus.Entry
|
||||||
|
|
||||||
// Get a logger, if it is present.
|
// Get a logger, if it is present.
|
||||||
loggerInterface := ctx.Value(loggerKey{})
|
loggerInterface := ctx.Value(loggerKey{})
|
||||||
if loggerInterface != nil {
|
if loggerInterface != nil {
|
||||||
|
@ -59,7 +59,7 @@ const defaultCheckInterval = 10 * time.Second
|
|||||||
// on this object that will be accessible from all requests. Any writable
|
// on this object that will be accessible from all requests. Any writable
|
||||||
// fields should be protected.
|
// fields should be protected.
|
||||||
type App struct {
|
type App struct {
|
||||||
context.Context
|
Context context.Context
|
||||||
|
|
||||||
Config *configuration.Configuration
|
Config *configuration.Configuration
|
||||||
|
|
||||||
@ -92,6 +92,8 @@ type App struct {
|
|||||||
// requests. The app only implements ServeHTTP and can be wrapped in other
|
// requests. The app only implements ServeHTTP and can be wrapped in other
|
||||||
// handlers accordingly.
|
// handlers accordingly.
|
||||||
func NewApp(ctx context.Context, config *configuration.Configuration) *App {
|
func NewApp(ctx context.Context, config *configuration.Configuration) *App {
|
||||||
|
logger := dcontext.GetLogger(ctx)
|
||||||
|
|
||||||
app := &App{
|
app := &App{
|
||||||
Config: config,
|
Config: config,
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
@ -100,7 +102,7 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Register the handler dispatchers.
|
// Register the handler dispatchers.
|
||||||
app.register(v2.RouteNameBase, func(ctx *Context, r *http.Request) http.Handler {
|
app.register(v2.RouteNameBase, func(ctx *Context, app *App, r *http.Request) http.Handler {
|
||||||
return http.HandlerFunc(apiBase)
|
return http.HandlerFunc(apiBase)
|
||||||
})
|
})
|
||||||
app.register(v2.RouteNameManifest, manifestDispatcher)
|
app.register(v2.RouteNameManifest, manifestDispatcher)
|
||||||
@ -120,7 +122,7 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
app.driver, err = factory.Create(app, config.Storage.Type(), storageParams)
|
app.driver, err = factory.Create(ctx, config.Storage.Type(), storageParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO(stevvooe): Move the creation of a service into a protected
|
// TODO(stevvooe): Move the creation of a service into a protected
|
||||||
// method, where this is created lazily. Its status can be queried via
|
// method, where this is created lazily. Its status can be queried via
|
||||||
@ -150,9 +152,9 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
startUploadPurger(app, app.driver, dcontext.GetLogger(app), purgeConfig)
|
startUploadPurger(app.Context, app.driver, logger, purgeConfig)
|
||||||
|
|
||||||
app.driver, err = applyStorageMiddleware(app, app.driver, config.Middleware["storage"])
|
app.driver, err = applyStorageMiddleware(ctx, app.driver, config.Middleware["storage"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -217,7 +219,7 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if redirectDisabled {
|
if redirectDisabled {
|
||||||
dcontext.GetLogger(app).Infof("backend redirection disabled")
|
logger.Infof("backend redirection disabled")
|
||||||
} else {
|
} else {
|
||||||
options = append(options, storage.EnableRedirect)
|
options = append(options, storage.EnableRedirect)
|
||||||
}
|
}
|
||||||
@ -288,15 +290,15 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App {
|
|||||||
panic("redis configuration required to use for layerinfo cache")
|
panic("redis configuration required to use for layerinfo cache")
|
||||||
}
|
}
|
||||||
if _, ok := cc["blobdescriptorsize"]; ok {
|
if _, ok := cc["blobdescriptorsize"]; ok {
|
||||||
dcontext.GetLogger(app).Warnf("blobdescriptorsize parameter is not supported with redis cache")
|
logger.Warnf("blobdescriptorsize parameter is not supported with redis cache")
|
||||||
}
|
}
|
||||||
cacheProvider := rediscache.NewRedisBlobDescriptorCacheProvider(app.redis)
|
cacheProvider := rediscache.NewRedisBlobDescriptorCacheProvider(app.redis)
|
||||||
localOptions := append(options, storage.BlobDescriptorCacheProvider(cacheProvider))
|
localOptions := append(options, storage.BlobDescriptorCacheProvider(cacheProvider))
|
||||||
app.registry, err = storage.NewRegistry(app, app.driver, localOptions...)
|
app.registry, err = storage.NewRegistry(ctx, app.driver, localOptions...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("could not create registry: " + err.Error())
|
panic("could not create registry: " + err.Error())
|
||||||
}
|
}
|
||||||
dcontext.GetLogger(app).Infof("using redis blob descriptor cache")
|
logger.Infof("using redis blob descriptor cache")
|
||||||
case "inmemory":
|
case "inmemory":
|
||||||
blobDescriptorSize := memorycache.DefaultSize
|
blobDescriptorSize := memorycache.DefaultSize
|
||||||
configuredSize, ok := cc["blobdescriptorsize"]
|
configuredSize, ok := cc["blobdescriptorsize"]
|
||||||
@ -310,14 +312,14 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App {
|
|||||||
|
|
||||||
cacheProvider := memorycache.NewInMemoryBlobDescriptorCacheProvider(blobDescriptorSize)
|
cacheProvider := memorycache.NewInMemoryBlobDescriptorCacheProvider(blobDescriptorSize)
|
||||||
localOptions := append(options, storage.BlobDescriptorCacheProvider(cacheProvider))
|
localOptions := append(options, storage.BlobDescriptorCacheProvider(cacheProvider))
|
||||||
app.registry, err = storage.NewRegistry(app, app.driver, localOptions...)
|
app.registry, err = storage.NewRegistry(ctx, app.driver, localOptions...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("could not create registry: " + err.Error())
|
panic("could not create registry: " + err.Error())
|
||||||
}
|
}
|
||||||
dcontext.GetLogger(app).Infof("using inmemory blob descriptor cache")
|
logger.Infof("using inmemory blob descriptor cache")
|
||||||
default:
|
default:
|
||||||
if v != "" {
|
if v != "" {
|
||||||
dcontext.GetLogger(app).Warnf("unknown cache type %q, caching disabled", config.Storage["cache"])
|
logger.Warnf("unknown cache type %q, caching disabled", config.Storage["cache"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -330,7 +332,7 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
app.registry, err = applyRegistryMiddleware(app, app.registry, app.driver, config.Middleware["registry"])
|
app.registry, err = applyRegistryMiddleware(ctx, app.registry, app.driver, config.Middleware["registry"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -343,7 +345,7 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App {
|
|||||||
panic(fmt.Sprintf("unable to configure authorization (%s): %v", authType, err))
|
panic(fmt.Sprintf("unable to configure authorization (%s): %v", authType, err))
|
||||||
}
|
}
|
||||||
app.accessController = accessController
|
app.accessController = accessController
|
||||||
dcontext.GetLogger(app).Debugf("configured %q access controller", authType)
|
logger.Debugf("configured %q access controller", authType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// configure as a pull through cache
|
// configure as a pull through cache
|
||||||
@ -353,12 +355,12 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App {
|
|||||||
panic(err.Error())
|
panic(err.Error())
|
||||||
}
|
}
|
||||||
app.isCache = true
|
app.isCache = true
|
||||||
dcontext.GetLogger(app).Info("Registry configured as a proxy cache to ", config.Proxy.RemoteURL)
|
logger.Info("Registry configured as a proxy cache to ", config.Proxy.RemoteURL)
|
||||||
}
|
}
|
||||||
var ok bool
|
var ok bool
|
||||||
app.repoRemover, ok = app.registry.(distribution.RepositoryRemover)
|
app.repoRemover, ok = app.registry.(distribution.RepositoryRemover)
|
||||||
if !ok {
|
if !ok {
|
||||||
dcontext.GetLogger(app).Warnf("Registry does not implement RepositoryRemover. Will not be able to delete repos and tags")
|
logger.Warnf("Registry does not implement RepositoryRemover. Will not be able to delete repos and tags")
|
||||||
}
|
}
|
||||||
|
|
||||||
return app
|
return app
|
||||||
@ -399,7 +401,7 @@ func (app *App) RegisterHealthChecks(healthRegistries ...*health.Registry) {
|
|||||||
|
|
||||||
updater := health.NewThresholdStatusUpdater(app.Config.Health.StorageDriver.Threshold)
|
updater := health.NewThresholdStatusUpdater(app.Config.Health.StorageDriver.Threshold)
|
||||||
healthRegistry.Register("storagedriver_"+app.Config.Storage.Type(), updater)
|
healthRegistry.Register("storagedriver_"+app.Config.Storage.Type(), updater)
|
||||||
go health.Poll(app, updater, storageDriverCheck, interval)
|
go health.Poll(app.Context, updater, storageDriverCheck, interval)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, fileChecker := range app.Config.Health.FileCheckers {
|
for _, fileChecker := range app.Config.Health.FileCheckers {
|
||||||
@ -407,10 +409,10 @@ func (app *App) RegisterHealthChecks(healthRegistries ...*health.Registry) {
|
|||||||
if interval == 0 {
|
if interval == 0 {
|
||||||
interval = defaultCheckInterval
|
interval = defaultCheckInterval
|
||||||
}
|
}
|
||||||
dcontext.GetLogger(app).Infof("configuring file health check path=%s, interval=%d", fileChecker.File, interval/time.Second)
|
dcontext.GetLogger(app.Context).Infof("configuring file health check path=%s, interval=%d", fileChecker.File, interval/time.Second)
|
||||||
u := health.NewStatusUpdater()
|
u := health.NewStatusUpdater()
|
||||||
healthRegistry.Register(fileChecker.File, u)
|
healthRegistry.Register(fileChecker.File, u)
|
||||||
go health.Poll(app, u, checks.FileChecker(fileChecker.File), interval)
|
go health.Poll(app.Context, u, checks.FileChecker(fileChecker.File), interval)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, httpChecker := range app.Config.Health.HTTPCheckers {
|
for _, httpChecker := range app.Config.Health.HTTPCheckers {
|
||||||
@ -426,10 +428,10 @@ func (app *App) RegisterHealthChecks(healthRegistries ...*health.Registry) {
|
|||||||
|
|
||||||
checker := checks.HTTPChecker(httpChecker.URI, statusCode, httpChecker.Timeout, httpChecker.Headers)
|
checker := checks.HTTPChecker(httpChecker.URI, statusCode, httpChecker.Timeout, httpChecker.Headers)
|
||||||
|
|
||||||
dcontext.GetLogger(app).Infof("configuring HTTP health check uri=%s, interval=%d, threshold=%d", httpChecker.URI, interval/time.Second, httpChecker.Threshold)
|
dcontext.GetLogger(app.Context).Infof("configuring HTTP health check uri=%s, interval=%d, threshold=%d", httpChecker.URI, interval/time.Second, httpChecker.Threshold)
|
||||||
updater := health.NewThresholdStatusUpdater(httpChecker.Threshold)
|
updater := health.NewThresholdStatusUpdater(httpChecker.Threshold)
|
||||||
healthRegistry.Register(httpChecker.URI, updater)
|
healthRegistry.Register(httpChecker.URI, updater)
|
||||||
go health.Poll(app, updater, checker, interval)
|
go health.Poll(app.Context, updater, checker, interval)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tcpChecker := range app.Config.Health.TCPCheckers {
|
for _, tcpChecker := range app.Config.Health.TCPCheckers {
|
||||||
@ -440,10 +442,10 @@ func (app *App) RegisterHealthChecks(healthRegistries ...*health.Registry) {
|
|||||||
|
|
||||||
checker := checks.TCPChecker(tcpChecker.Addr, tcpChecker.Timeout)
|
checker := checks.TCPChecker(tcpChecker.Addr, tcpChecker.Timeout)
|
||||||
|
|
||||||
dcontext.GetLogger(app).Infof("configuring TCP health check addr=%s, interval=%d, threshold=%d", tcpChecker.Addr, interval/time.Second, tcpChecker.Threshold)
|
dcontext.GetLogger(app.Context).Infof("configuring TCP health check addr=%s, interval=%d, threshold=%d", tcpChecker.Addr, interval/time.Second, tcpChecker.Threshold)
|
||||||
updater := health.NewThresholdStatusUpdater(tcpChecker.Threshold)
|
updater := health.NewThresholdStatusUpdater(tcpChecker.Threshold)
|
||||||
healthRegistry.Register(tcpChecker.Addr, updater)
|
healthRegistry.Register(tcpChecker.Addr, updater)
|
||||||
go health.Poll(app, updater, checker, interval)
|
go health.Poll(app.Context, updater, checker, interval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -489,11 +491,11 @@ func (app *App) configureEvents(configuration *configuration.Configuration) {
|
|||||||
var sinks []events.Sink
|
var sinks []events.Sink
|
||||||
for _, endpoint := range configuration.Notifications.Endpoints {
|
for _, endpoint := range configuration.Notifications.Endpoints {
|
||||||
if endpoint.Disabled {
|
if endpoint.Disabled {
|
||||||
dcontext.GetLogger(app).Infof("endpoint %s disabled, skipping", endpoint.Name)
|
dcontext.GetLogger(app.Context).Infof("endpoint %s disabled, skipping", endpoint.Name)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
dcontext.GetLogger(app).Infof("configuring endpoint %v (%v), timeout=%s, headers=%v", endpoint.Name, endpoint.URL, endpoint.Timeout, endpoint.Headers)
|
dcontext.GetLogger(app.Context).Infof("configuring endpoint %v (%v), timeout=%s, headers=%v", endpoint.Name, endpoint.URL, endpoint.Timeout, endpoint.Headers)
|
||||||
endpoint := notifications.NewEndpoint(endpoint.Name, endpoint.URL, notifications.EndpointConfig{
|
endpoint := notifications.NewEndpoint(endpoint.Name, endpoint.URL, notifications.EndpointConfig{
|
||||||
Timeout: endpoint.Timeout,
|
Timeout: endpoint.Timeout,
|
||||||
Threshold: endpoint.Threshold,
|
Threshold: endpoint.Threshold,
|
||||||
@ -526,13 +528,13 @@ func (app *App) configureEvents(configuration *configuration.Configuration) {
|
|||||||
|
|
||||||
app.events.source = notifications.SourceRecord{
|
app.events.source = notifications.SourceRecord{
|
||||||
Addr: hostname,
|
Addr: hostname,
|
||||||
InstanceID: dcontext.GetStringValue(app, "instance.id"),
|
InstanceID: dcontext.GetStringValue(app.Context, "instance.id"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *App) configureRedis(cfg *configuration.Configuration) {
|
func (app *App) configureRedis(cfg *configuration.Configuration) {
|
||||||
if len(cfg.Redis.Options.Addrs) == 0 {
|
if len(cfg.Redis.Options.Addrs) == 0 {
|
||||||
dcontext.GetLogger(app).Infof("redis not configured")
|
dcontext.GetLogger(app.Context).Infof("redis not configured")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -569,7 +571,7 @@ func (app *App) configureRedis(cfg *configuration.Configuration) {
|
|||||||
|
|
||||||
// Enable metrics instrumentation.
|
// Enable metrics instrumentation.
|
||||||
if err := redisotel.InstrumentMetrics(app.redis); err != nil {
|
if err := redisotel.InstrumentMetrics(app.redis); err != nil {
|
||||||
dcontext.GetLogger(app).Errorf("failed to instrument metrics on redis: %v", err)
|
dcontext.GetLogger(app.Context).Errorf("failed to instrument metrics on redis: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup expvar
|
// setup expvar
|
||||||
@ -597,7 +599,7 @@ func (app *App) createPool(cfg redis.UniversalOptions) redis.UniversalClient {
|
|||||||
|
|
||||||
// configureLogHook prepares logging hook parameters.
|
// configureLogHook prepares logging hook parameters.
|
||||||
func (app *App) configureLogHook(configuration *configuration.Configuration) {
|
func (app *App) configureLogHook(configuration *configuration.Configuration) {
|
||||||
entry, ok := dcontext.GetLogger(app).(*logrus.Entry)
|
entry, ok := dcontext.GetLogger(app.Context).(*logrus.Entry)
|
||||||
if !ok {
|
if !ok {
|
||||||
// somehow, we are not using logrus
|
// somehow, we are not using logrus
|
||||||
return
|
return
|
||||||
@ -635,7 +637,7 @@ func (app *App) configureSecret(configuration *configuration.Configuration) {
|
|||||||
panic(fmt.Sprintf("could not generate random bytes for HTTP secret: %v", err))
|
panic(fmt.Sprintf("could not generate random bytes for HTTP secret: %v", err))
|
||||||
}
|
}
|
||||||
configuration.HTTP.Secret = string(secretBytes[:])
|
configuration.HTTP.Secret = string(secretBytes[:])
|
||||||
dcontext.GetLogger(app).Warn("No HTTP secret provided - generated random secret. This may cause problems with uploads if multiple registries are behind a load-balancer. To provide a shared secret, fill in http.secret in the configuration file or set the REGISTRY_HTTP_SECRET environment variable.")
|
dcontext.GetLogger(app.Context).Warn("No HTTP secret provided - generated random secret. This may cause problems with uploads if multiple registries are behind a load-balancer. To provide a shared secret, fill in http.secret in the configuration file or set the REGISTRY_HTTP_SECRET environment variable.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -656,7 +658,7 @@ func (app *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
// for the route. The dispatcher will use this to dynamically create request
|
// for the route. The dispatcher will use this to dynamically create request
|
||||||
// specific handlers for each endpoint without creating a new router for each
|
// specific handlers for each endpoint without creating a new router for each
|
||||||
// request.
|
// request.
|
||||||
type dispatchFunc func(ctx *Context, r *http.Request) http.Handler
|
type dispatchFunc func(ctx *Context, app *App, r *http.Request) http.Handler
|
||||||
|
|
||||||
// TODO(stevvooe): dispatchers should probably have some validation error
|
// TODO(stevvooe): dispatchers should probably have some validation error
|
||||||
// chain with proper error reporting.
|
// chain with proper error reporting.
|
||||||
@ -729,12 +731,12 @@ func (app *App) dispatcher(dispatch dispatchFunc) http.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// assign and decorate the authorized repository with an event bridge.
|
// assign and decorate the authorized repository with an event bridge.
|
||||||
context.Repository, context.RepositoryRemover = notifications.Listen(
|
context.Repository, _ = notifications.Listen(
|
||||||
repository,
|
repository,
|
||||||
context.App.repoRemover,
|
app.repoRemover,
|
||||||
app.eventBridge(context, r))
|
app.eventBridge(context, r))
|
||||||
|
|
||||||
context.Repository, err = applyRepoMiddleware(app, context.Repository, app.Config.Middleware["repository"])
|
context.Repository, err = applyRepoMiddleware(app.Context, context.Repository, app.Config.Middleware["repository"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dcontext.GetLogger(context).Errorf("error initializing repository middleware: %v", err)
|
dcontext.GetLogger(context).Errorf("error initializing repository middleware: %v", err)
|
||||||
context.Errors = append(context.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
|
context.Errors = append(context.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
|
||||||
@ -746,7 +748,7 @@ func (app *App) dispatcher(dispatch dispatchFunc) http.Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(context, r).ServeHTTP(w, r)
|
dispatch(context, app, r).ServeHTTP(w, r)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -800,7 +802,6 @@ func (app *App) context(w http.ResponseWriter, r *http.Request) *Context {
|
|||||||
"vars.uuid"))
|
"vars.uuid"))
|
||||||
|
|
||||||
context := &Context{
|
context := &Context{
|
||||||
App: app,
|
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ func TestAppDispatcher(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
varCheckingDispatcher := func(expectedVars map[string]string) dispatchFunc {
|
varCheckingDispatcher := func(expectedVars map[string]string) dispatchFunc {
|
||||||
return func(ctx *Context, r *http.Request) http.Handler {
|
return func(ctx *Context, app *App, r *http.Request) http.Handler {
|
||||||
// Always checks the same name context
|
// Always checks the same name context
|
||||||
if ctx.Repository.Named().Name() != getName(ctx) {
|
if ctx.Repository.Named().Name() != getName(ctx) {
|
||||||
t.Fatalf("unexpected name: %q != %q", ctx.Repository.Named().Name(), "foo/bar")
|
t.Fatalf("unexpected name: %q != %q", ctx.Repository.Named().Name(), "foo/bar")
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// blobDispatcher uses the request context to build a blobHandler.
|
// blobDispatcher uses the request context to build a blobHandler.
|
||||||
func blobDispatcher(ctx *Context, r *http.Request) http.Handler {
|
func blobDispatcher(ctx *Context, app *App, r *http.Request) http.Handler {
|
||||||
dgst, err := getDigest(ctx)
|
dgst, err := getDigest(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ func blobDispatcher(ctx *Context, r *http.Request) http.Handler {
|
|||||||
http.MethodHead: http.HandlerFunc(blobHandler.GetBlob),
|
http.MethodHead: http.HandlerFunc(blobHandler.GetBlob),
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ctx.readOnly {
|
if !app.readOnly {
|
||||||
mhandler[http.MethodDelete] = http.HandlerFunc(blobHandler.DeleteBlob)
|
mhandler[http.MethodDelete] = http.HandlerFunc(blobHandler.DeleteBlob)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/distribution/distribution/v3"
|
"github.com/distribution/distribution/v3"
|
||||||
|
"github.com/distribution/distribution/v3/configuration"
|
||||||
"github.com/distribution/distribution/v3/internal/dcontext"
|
"github.com/distribution/distribution/v3/internal/dcontext"
|
||||||
"github.com/distribution/distribution/v3/registry/api/errcode"
|
"github.com/distribution/distribution/v3/registry/api/errcode"
|
||||||
"github.com/distribution/distribution/v3/registry/storage"
|
"github.com/distribution/distribution/v3/registry/storage"
|
||||||
@ -17,9 +18,10 @@ import (
|
|||||||
|
|
||||||
// blobUploadDispatcher constructs and returns the blob upload handler for the
|
// blobUploadDispatcher constructs and returns the blob upload handler for the
|
||||||
// given request context.
|
// given request context.
|
||||||
func blobUploadDispatcher(ctx *Context, r *http.Request) http.Handler {
|
func blobUploadDispatcher(ctx *Context, app *App, r *http.Request) http.Handler {
|
||||||
buh := &blobUploadHandler{
|
buh := &blobUploadHandler{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
|
Config: app.Config,
|
||||||
UUID: getUploadUUID(ctx),
|
UUID: getUploadUUID(ctx),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,7 +30,7 @@ func blobUploadDispatcher(ctx *Context, r *http.Request) http.Handler {
|
|||||||
http.MethodHead: http.HandlerFunc(buh.GetUploadStatus),
|
http.MethodHead: http.HandlerFunc(buh.GetUploadStatus),
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ctx.readOnly {
|
if !app.readOnly {
|
||||||
handler[http.MethodPost] = http.HandlerFunc(buh.StartBlobUpload)
|
handler[http.MethodPost] = http.HandlerFunc(buh.StartBlobUpload)
|
||||||
handler[http.MethodPatch] = http.HandlerFunc(buh.PatchBlobData)
|
handler[http.MethodPatch] = http.HandlerFunc(buh.PatchBlobData)
|
||||||
handler[http.MethodPut] = http.HandlerFunc(buh.PutBlobUploadComplete)
|
handler[http.MethodPut] = http.HandlerFunc(buh.PutBlobUploadComplete)
|
||||||
@ -52,6 +54,8 @@ func blobUploadDispatcher(ctx *Context, r *http.Request) http.Handler {
|
|||||||
type blobUploadHandler struct {
|
type blobUploadHandler struct {
|
||||||
*Context
|
*Context
|
||||||
|
|
||||||
|
Config *configuration.Configuration
|
||||||
|
|
||||||
// UUID identifies the upload instance for the current request. Using UUID
|
// UUID identifies the upload instance for the current request. Using UUID
|
||||||
// to key blob writers since this implementation uses UUIDs.
|
// to key blob writers since this implementation uses UUIDs.
|
||||||
UUID string
|
UUID string
|
||||||
@ -270,7 +274,7 @@ func (buh *blobUploadHandler) CancelBlobUpload(w http.ResponseWriter, r *http.Re
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (buh *blobUploadHandler) ResumeBlobUpload(ctx *Context, r *http.Request) http.Handler {
|
func (buh *blobUploadHandler) ResumeBlobUpload(ctx *Context, r *http.Request) http.Handler {
|
||||||
state, err := hmacKey(ctx.Config.HTTP.Secret).unpackUploadState(r.FormValue("_state"))
|
state, err := hmacKey(buh.Config.HTTP.Secret).unpackUploadState(r.FormValue("_state"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
dcontext.GetLogger(ctx).Infof("error resolving upload: %v", err)
|
dcontext.GetLogger(ctx).Infof("error resolving upload: %v", err)
|
||||||
|
@ -8,6 +8,8 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/distribution/distribution/v3"
|
||||||
|
"github.com/distribution/distribution/v3/configuration"
|
||||||
"github.com/distribution/distribution/v3/registry/api/errcode"
|
"github.com/distribution/distribution/v3/registry/api/errcode"
|
||||||
"github.com/distribution/distribution/v3/registry/storage/driver"
|
"github.com/distribution/distribution/v3/registry/storage/driver"
|
||||||
"github.com/gorilla/handlers"
|
"github.com/gorilla/handlers"
|
||||||
@ -15,9 +17,11 @@ import (
|
|||||||
|
|
||||||
const defaultReturnedEntries = 100
|
const defaultReturnedEntries = 100
|
||||||
|
|
||||||
func catalogDispatcher(ctx *Context, r *http.Request) http.Handler {
|
func catalogDispatcher(ctx *Context, app *App, r *http.Request) http.Handler {
|
||||||
catalogHandler := &catalogHandler{
|
catalogHandler := &catalogHandler{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
|
Config: app.Config,
|
||||||
|
registry: app.registry,
|
||||||
}
|
}
|
||||||
|
|
||||||
return handlers.MethodHandler{
|
return handlers.MethodHandler{
|
||||||
@ -27,6 +31,8 @@ func catalogDispatcher(ctx *Context, r *http.Request) http.Handler {
|
|||||||
|
|
||||||
type catalogHandler struct {
|
type catalogHandler struct {
|
||||||
*Context
|
*Context
|
||||||
|
Config *configuration.Configuration
|
||||||
|
registry distribution.Namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
type catalogAPIResponse struct {
|
type catalogAPIResponse struct {
|
||||||
@ -40,7 +46,7 @@ func (ch *catalogHandler) GetCatalog(w http.ResponseWriter, r *http.Request) {
|
|||||||
lastEntry := q.Get("last")
|
lastEntry := q.Get("last")
|
||||||
|
|
||||||
entries := defaultReturnedEntries
|
entries := defaultReturnedEntries
|
||||||
maximumConfiguredEntries := ch.App.Config.Catalog.MaxEntries
|
maximumConfiguredEntries := ch.Config.Catalog.MaxEntries
|
||||||
|
|
||||||
// parse n, if n is negative abort with an error
|
// parse n, if n is negative abort with an error
|
||||||
if n := q.Get("n"); n != "" {
|
if n := q.Get("n"); n != "" {
|
||||||
@ -71,7 +77,7 @@ func (ch *catalogHandler) GetCatalog(w http.ResponseWriter, r *http.Request) {
|
|||||||
if entries == 0 {
|
if entries == 0 {
|
||||||
moreEntries = false
|
moreEntries = false
|
||||||
} else {
|
} else {
|
||||||
returnedRepositories, err := ch.App.registry.Repositories(ch.Context, repos, lastEntry)
|
returnedRepositories, err := ch.registry.Repositories(ch.Context, repos, lastEntry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_, pathNotFound := err.(driver.PathNotFoundError)
|
_, pathNotFound := err.(driver.PathNotFoundError)
|
||||||
if err != io.EOF && !pathNotFound {
|
if err != io.EOF && !pathNotFound {
|
||||||
|
@ -17,17 +17,12 @@ import (
|
|||||||
// handlers. Resources that don't need to be shared across handlers should not
|
// handlers. Resources that don't need to be shared across handlers should not
|
||||||
// be on this object.
|
// be on this object.
|
||||||
type Context struct {
|
type Context struct {
|
||||||
// App points to the application structure that created this context.
|
|
||||||
*App
|
|
||||||
context.Context
|
context.Context
|
||||||
|
|
||||||
// Repository is the repository for the current request. All requests
|
// Repository is the repository for the current request. All requests
|
||||||
// should be scoped to a single repository. This field may be nil.
|
// should be scoped to a single repository. This field may be nil.
|
||||||
Repository distribution.Repository
|
Repository distribution.Repository
|
||||||
|
|
||||||
// RepositoryRemover provides method to delete a repository
|
|
||||||
RepositoryRemover distribution.RepositoryRemover
|
|
||||||
|
|
||||||
// Errors is a collection of errors encountered during the request to be
|
// Errors is a collection of errors encountered during the request to be
|
||||||
// returned to the client API. If errors are added to the collection, the
|
// returned to the client API. If errors are added to the collection, the
|
||||||
// handler *must not* start the response via http.ResponseWriter.
|
// handler *must not* start the response via http.ResponseWriter.
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/distribution/distribution/v3"
|
"github.com/distribution/distribution/v3"
|
||||||
|
"github.com/distribution/distribution/v3/configuration"
|
||||||
"github.com/distribution/distribution/v3/internal/dcontext"
|
"github.com/distribution/distribution/v3/internal/dcontext"
|
||||||
"github.com/distribution/distribution/v3/manifest/manifestlist"
|
"github.com/distribution/distribution/v3/manifest/manifestlist"
|
||||||
"github.com/distribution/distribution/v3/manifest/ocischema"
|
"github.com/distribution/distribution/v3/manifest/ocischema"
|
||||||
@ -42,9 +43,11 @@ const (
|
|||||||
|
|
||||||
// manifestDispatcher takes the request context and builds the
|
// manifestDispatcher takes the request context and builds the
|
||||||
// appropriate handler for handling manifest requests.
|
// appropriate handler for handling manifest requests.
|
||||||
func manifestDispatcher(ctx *Context, r *http.Request) http.Handler {
|
func manifestDispatcher(ctx *Context, app *App, r *http.Request) http.Handler {
|
||||||
manifestHandler := &manifestHandler{
|
manifestHandler := &manifestHandler{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
|
isCache: app.isCache,
|
||||||
|
Config: app.Config,
|
||||||
}
|
}
|
||||||
ref := getReference(ctx)
|
ref := getReference(ctx)
|
||||||
dgst, err := digest.Parse(ref)
|
dgst, err := digest.Parse(ref)
|
||||||
@ -60,7 +63,7 @@ func manifestDispatcher(ctx *Context, r *http.Request) http.Handler {
|
|||||||
http.MethodHead: http.HandlerFunc(manifestHandler.GetManifest),
|
http.MethodHead: http.HandlerFunc(manifestHandler.GetManifest),
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ctx.readOnly {
|
if !app.readOnly {
|
||||||
mhandler[http.MethodPut] = http.HandlerFunc(manifestHandler.PutManifest)
|
mhandler[http.MethodPut] = http.HandlerFunc(manifestHandler.PutManifest)
|
||||||
mhandler[http.MethodDelete] = http.HandlerFunc(manifestHandler.DeleteManifest)
|
mhandler[http.MethodDelete] = http.HandlerFunc(manifestHandler.DeleteManifest)
|
||||||
}
|
}
|
||||||
@ -72,6 +75,11 @@ func manifestDispatcher(ctx *Context, r *http.Request) http.Handler {
|
|||||||
type manifestHandler struct {
|
type manifestHandler struct {
|
||||||
*Context
|
*Context
|
||||||
|
|
||||||
|
Config *configuration.Configuration
|
||||||
|
|
||||||
|
// isCache is true if this registry is configured as a pull through cache
|
||||||
|
isCache bool
|
||||||
|
|
||||||
// One of tag or digest gets set, depending on what is present in context.
|
// One of tag or digest gets set, depending on what is present in context.
|
||||||
Tag string
|
Tag string
|
||||||
Digest digest.Digest
|
Digest digest.Digest
|
||||||
@ -362,7 +370,7 @@ func (imh *manifestHandler) PutManifest(w http.ResponseWriter, r *http.Request)
|
|||||||
// applyResourcePolicy checks whether the resource class matches what has
|
// applyResourcePolicy checks whether the resource class matches what has
|
||||||
// been authorized and allowed by the policy configuration.
|
// been authorized and allowed by the policy configuration.
|
||||||
func (imh *manifestHandler) applyResourcePolicy(manifest distribution.Manifest) error {
|
func (imh *manifestHandler) applyResourcePolicy(manifest distribution.Manifest) error {
|
||||||
allowedClasses := imh.App.Config.Policy.Repository.Classes
|
allowedClasses := imh.Config.Policy.Repository.Classes
|
||||||
if len(allowedClasses) == 0 {
|
if len(allowedClasses) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -431,7 +439,7 @@ func (imh *manifestHandler) applyResourcePolicy(manifest distribution.Manifest)
|
|||||||
func (imh *manifestHandler) DeleteManifest(w http.ResponseWriter, r *http.Request) {
|
func (imh *manifestHandler) DeleteManifest(w http.ResponseWriter, r *http.Request) {
|
||||||
dcontext.GetLogger(imh).Debug("DeleteImageManifest")
|
dcontext.GetLogger(imh).Debug("DeleteImageManifest")
|
||||||
|
|
||||||
if imh.App.isCache {
|
if imh.isCache {
|
||||||
imh.Errors = append(imh.Errors, errcode.ErrorCodeUnsupported)
|
imh.Errors = append(imh.Errors, errcode.ErrorCodeUnsupported)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// tagsDispatcher constructs the tags handler api endpoint.
|
// tagsDispatcher constructs the tags handler api endpoint.
|
||||||
func tagsDispatcher(ctx *Context, r *http.Request) http.Handler {
|
func tagsDispatcher(ctx *Context, app *App, r *http.Request) http.Handler {
|
||||||
tagsHandler := &tagsHandler{
|
tagsHandler := &tagsHandler{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
}
|
}
|
||||||
|
@ -223,6 +223,7 @@ func setDirectoryURL(directoryurl string) *acme.Client {
|
|||||||
// ListenAndServe runs the registry's HTTP server.
|
// ListenAndServe runs the registry's HTTP server.
|
||||||
func (registry *Registry) ListenAndServe() error {
|
func (registry *Registry) ListenAndServe() error {
|
||||||
config := registry.config
|
config := registry.config
|
||||||
|
logger := dcontext.GetLogger(registry.app.Context)
|
||||||
|
|
||||||
ln, err := listener.NewListener(config.HTTP.Net, config.HTTP.Addr)
|
ln, err := listener.NewListener(config.HTTP.Net, config.HTTP.Addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -237,19 +238,19 @@ func (registry *Registry) ListenAndServe() error {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("unknown minimum TLS level '%s' specified for http.tls.minimumtls", config.HTTP.TLS.MinimumTLS)
|
return fmt.Errorf("unknown minimum TLS level '%s' specified for http.tls.minimumtls", config.HTTP.TLS.MinimumTLS)
|
||||||
}
|
}
|
||||||
dcontext.GetLogger(registry.app).Infof("restricting TLS version to %s or higher", config.HTTP.TLS.MinimumTLS)
|
logger.Infof("restricting TLS version to %s or higher", config.HTTP.TLS.MinimumTLS)
|
||||||
|
|
||||||
var tlsCipherSuites []uint16
|
var tlsCipherSuites []uint16
|
||||||
// configuring cipher suites are no longer supported after the tls1.3.
|
// configuring cipher suites are no longer supported after the tls1.3.
|
||||||
// (https://go.dev/blog/tls-cipher-suites)
|
// (https://go.dev/blog/tls-cipher-suites)
|
||||||
if tlsMinVersion > tls.VersionTLS12 {
|
if tlsMinVersion > tls.VersionTLS12 {
|
||||||
dcontext.GetLogger(registry.app).Warnf("restricting TLS cipher suites to empty. Because configuring cipher suites is no longer supported in %s", config.HTTP.TLS.MinimumTLS)
|
logger.Warnf("restricting TLS cipher suites to empty. Because configuring cipher suites is no longer supported in %s", config.HTTP.TLS.MinimumTLS)
|
||||||
} else {
|
} else {
|
||||||
tlsCipherSuites, err = getCipherSuites(config.HTTP.TLS.CipherSuites)
|
tlsCipherSuites, err = getCipherSuites(config.HTTP.TLS.CipherSuites)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
dcontext.GetLogger(registry.app).Infof("restricting TLS cipher suites to: %s", strings.Join(getCipherSuiteNames(tlsCipherSuites), ","))
|
logger.Infof("restricting TLS cipher suites to: %s", strings.Join(getCipherSuiteNames(tlsCipherSuites), ","))
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsConf := &tls.Config{
|
tlsConf := &tls.Config{
|
||||||
@ -295,7 +296,7 @@ func (registry *Registry) ListenAndServe() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, subj := range pool.Subjects() { //nolint:staticcheck // FIXME(thaJeztah): ignore SA1019: ac.(*accessController).rootCerts.Subjects has been deprecated since Go 1.18: if s was returned by SystemCertPool, Subjects will not include the system roots. (staticcheck)
|
for _, subj := range pool.Subjects() { //nolint:staticcheck // FIXME(thaJeztah): ignore SA1019: ac.(*accessController).rootCerts.Subjects has been deprecated since Go 1.18: if s was returned by SystemCertPool, Subjects will not include the system roots. (staticcheck)
|
||||||
dcontext.GetLogger(registry.app).Debugf("CA Subject: %s", string(subj))
|
logger.Debugf("CA Subject: %s", string(subj))
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsConf.ClientAuth = tls.RequireAndVerifyClientCert
|
tlsConf.ClientAuth = tls.RequireAndVerifyClientCert
|
||||||
@ -303,9 +304,9 @@ func (registry *Registry) ListenAndServe() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ln = tls.NewListener(ln, tlsConf)
|
ln = tls.NewListener(ln, tlsConf)
|
||||||
dcontext.GetLogger(registry.app).Infof("listening on %v, tls", ln.Addr())
|
logger.Infof("listening on %v, tls", ln.Addr())
|
||||||
} else {
|
} else {
|
||||||
dcontext.GetLogger(registry.app).Infof("listening on %v", ln.Addr())
|
logger.Infof("listening on %v", ln.Addr())
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.HTTP.DrainTimeout == 0 {
|
if config.HTTP.DrainTimeout == 0 {
|
||||||
@ -325,7 +326,7 @@ func (registry *Registry) ListenAndServe() error {
|
|||||||
case err := <-serveErr:
|
case err := <-serveErr:
|
||||||
return err
|
return err
|
||||||
case <-registry.quit:
|
case <-registry.quit:
|
||||||
dcontext.GetLogger(registry.app).Info("stopping server gracefully. Draining connections for ", config.HTTP.DrainTimeout)
|
logger.Info("stopping server gracefully. Draining connections for ", config.HTTP.DrainTimeout)
|
||||||
// shutdown the server with a grace period of configured timeout
|
// shutdown the server with a grace period of configured timeout
|
||||||
c, cancel := context.WithTimeout(context.Background(), config.HTTP.DrainTimeout)
|
c, cancel := context.WithTimeout(context.Background(), config.HTTP.DrainTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
Loading…
Reference in New Issue
Block a user