diff --git a/configuration/configuration.go b/configuration/configuration.go index 6712f3d82..3dff32f84 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -64,6 +64,10 @@ type Configuration struct { // Net specifies the net portion of the bind address. A default empty value means tcp. Net string `yaml:"net,omitempty"` + // Host specifies an externally-reachable address for the registry, as a fully + // qualified URL. + Host string `yaml:"host,omitempty"` + Prefix string `yaml:"prefix,omitempty"` // Secret specifies the secret key which HMAC tokens are created with. diff --git a/configuration/configuration_test.go b/configuration/configuration_test.go index 58cdc1a9f..ced321a6e 100644 --- a/configuration/configuration_test.go +++ b/configuration/configuration_test.go @@ -65,6 +65,7 @@ var configStruct = Configuration{ HTTP: struct { Addr string `yaml:"addr,omitempty"` Net string `yaml:"net,omitempty"` + Host string `yaml:"host,omitempty"` Prefix string `yaml:"prefix,omitempty"` Secret string `yaml:"secret,omitempty"` TLS struct { diff --git a/docs/configuration.md b/docs/configuration.md index b4080cb07..55717c42e 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -158,6 +158,7 @@ information about each option that appears later in this page. http: addr: localhost:5000 prefix: /my/nested/registry/ + host: https://myregistryaddress.org:5000 secret: asecretforlocaldevelopment tls: certificate: /path/to/x509/public @@ -1189,6 +1190,7 @@ configuration may contain both. addr: localhost:5000 net: tcp prefix: /my/nested/registry/ + host: https://myregistryaddress.org:5000 secret: asecretforlocaldevelopment tls: certificate: /path/to/x509/public @@ -1233,7 +1235,7 @@ The `http` option details the configuration for the HTTP server that hosts the r The default empty value means tcp. -
prefix
v2
. It
should have both preceding and trailing slashes, for example /path/
.
host
+ secret
diff --git a/registry/api/v2/urls_test.go b/registry/api/v2/urls_test.go
index 1113a7dde..61d415474 100644
--- a/registry/api/v2/urls_test.go
+++ b/registry/api/v2/urls_test.go
@@ -158,8 +158,9 @@ func TestBuilderFromRequest(t *testing.T) {
forwardedHostHeader2.Set("X-Forwarded-Host", "first.example.com, proxy1.example.com")
testRequests := []struct {
- request *http.Request
- base string
+ request *http.Request
+ base string
+ configHost url.URL
}{
{
request: &http.Request{URL: u, Host: u.Host},
@@ -177,10 +178,23 @@ func TestBuilderFromRequest(t *testing.T) {
request: &http.Request{URL: u, Host: u.Host, Header: forwardedHostHeader2},
base: "http://first.example.com",
},
+ {
+ request: &http.Request{URL: u, Host: u.Host, Header: forwardedHostHeader2},
+ base: "https://third.example.com:5000",
+ configHost: url.URL{
+ Scheme: "https",
+ Host: "third.example.com:5000",
+ },
+ },
}
for _, tr := range testRequests {
- builder := NewURLBuilderFromRequest(tr.request)
+ var builder *URLBuilder
+ if tr.configHost.Scheme != "" && tr.configHost.Host != "" {
+ builder = NewURLBuilder(&tr.configHost)
+ } else {
+ builder = NewURLBuilderFromRequest(tr.request)
+ }
for _, testCase := range makeURLBuilderTestCases(builder) {
url, err := testCase.build()
@@ -207,8 +221,9 @@ func TestBuilderFromRequestWithPrefix(t *testing.T) {
forwardedProtoHeader.Set("X-Forwarded-Proto", "https")
testRequests := []struct {
- request *http.Request
- base string
+ request *http.Request
+ base string
+ configHost url.URL
}{
{
request: &http.Request{URL: u, Host: u.Host},
@@ -218,10 +233,23 @@ func TestBuilderFromRequestWithPrefix(t *testing.T) {
request: &http.Request{URL: u, Host: u.Host, Header: forwardedProtoHeader},
base: "https://example.com/prefix/",
},
+ {
+ request: &http.Request{URL: u, Host: u.Host, Header: forwardedProtoHeader},
+ base: "https://subdomain.example.com/prefix/",
+ configHost: url.URL{
+ Scheme: "https",
+ Host: "subdomain.example.com/prefix",
+ },
+ },
}
for _, tr := range testRequests {
- builder := NewURLBuilderFromRequest(tr.request)
+ var builder *URLBuilder
+ if tr.configHost.Scheme != "" && tr.configHost.Host != "" {
+ builder = NewURLBuilder(&tr.configHost)
+ } else {
+ builder = NewURLBuilderFromRequest(tr.request)
+ }
for _, testCase := range makeURLBuilderTestCases(builder) {
url, err := testCase.build()
diff --git a/registry/handlers/app.go b/registry/handlers/app.go
index 5103c5fbe..f2f6ad9d7 100644
--- a/registry/handlers/app.go
+++ b/registry/handlers/app.go
@@ -7,6 +7,7 @@ import (
"math/rand"
"net"
"net/http"
+ "net/url"
"os"
"time"
@@ -54,6 +55,10 @@ type App struct {
registry distribution.Namespace // registry is the primary registry backend for the app instance.
accessController auth.AccessController // main access controller for application
+ // httpHost is a parsed representation of the http.host parameter from
+ // the configuration. Only the Scheme and Host fields are used.
+ httpHost url.URL
+
// events contains notification related configuration.
events struct {
sink notifications.Sink
@@ -120,6 +125,14 @@ func NewApp(ctx context.Context, configuration *configuration.Configuration) *Ap
app.configureRedis(configuration)
app.configureLogHook(configuration)
+ if configuration.HTTP.Host != "" {
+ u, err := url.Parse(configuration.HTTP.Host)
+ if err != nil {
+ panic(fmt.Sprintf(`could not parse http "host" parameter: %v`, err))
+ }
+ app.httpHost = *u
+ }
+
options := []storage.RegistryOption{}
if app.isCache {
@@ -639,9 +652,17 @@ func (app *App) context(w http.ResponseWriter, r *http.Request) *Context {
"vars.uuid"))
context := &Context{
- App: app,
- Context: ctx,
- urlBuilder: v2.NewURLBuilderFromRequest(r),
+ App: app,
+ Context: ctx,
+ }
+
+ if app.httpHost.Scheme != "" && app.httpHost.Host != "" {
+ // A "host" item in the configuration takes precedence over
+ // X-Forwarded-Proto and X-Forwarded-Host headers, and the
+ // hostname in the request.
+ context.urlBuilder = v2.NewURLBuilder(&app.httpHost)
+ } else {
+ context.urlBuilder = v2.NewURLBuilderFromRequest(r)
}
return context