Add http.host parameter
This allows the administrator to specify an externally-reachable URL for the registry. It takes precedence over the X-Forwarded-Proto and X-Forwarded-Host headers, and the hostname in the request. Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>master
							parent
							
								
									f8109a78f9
								
							
						
					
					
						commit
						0a6988195e
					
				|  | @ -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.
 | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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. | ||||
|     </td> | ||||
|   </tr> | ||||
|     <tr> | ||||
|   <tr> | ||||
|     <td> | ||||
|       <code>prefix</code> | ||||
|     </td> | ||||
|  | @ -1246,6 +1248,19 @@ prefix. The root path is the section before <code>v2</code>. It | |||
| should have both preceding and trailing slashes, for example <code>/path/</code>. | ||||
|     </td> | ||||
|   </tr> | ||||
|   <tr> | ||||
|     <td> | ||||
|       <code>host</code> | ||||
|     </td> | ||||
|     <td> | ||||
|       no | ||||
|     </td> | ||||
|     <td> | ||||
| This parameter specifies an externally-reachable address for the registry, as a | ||||
| fully qualified URL. If present, it is used when creating generated URLs. | ||||
| Otherwise, these URLs are derived from client requests. | ||||
|     </td> | ||||
|   </tr> | ||||
|   <tr> | ||||
|     <td> | ||||
|       <code>secret</code> | ||||
|  |  | |||
|  | @ -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() | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue