From bf1c82bd597ee1af560b4a5a6ade842051df728f Mon Sep 17 00:00:00 2001 From: Chris Marchesi Date: Wed, 14 Dec 2016 23:26:38 -0800 Subject: [PATCH] Reverse proxy support This update adds full reverse proxy support, namely path-based reverse proxy. This is controlled by the -web.proxy-address flag, or the NOMAD_PROXY_ADDRESS environment variable. Set this to the full path of what the proxy looks like on the front end, ie: https://example.com/nomad/. --- .gitignore | 1 + README.md | 17 +++++++++-------- backend/main.go | 25 +++++++++++++++++++++++-- frontend/index.html.ejs | 16 +++++----------- frontend/src/main.js | 6 +++++- frontend/webpack-base.config.js | 2 +- frontend/webpack.config.js | 1 + 7 files changed, 45 insertions(+), 23 deletions(-) diff --git a/.gitignore b/.gitignore index c1cd3b9a..f871944e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ frontend/node_modules backend/vendor +backend/backend .tern-port .sass-cache diff --git a/README.md b/README.md index eb7ee8c8..1358c73f 100644 --- a/README.md +++ b/README.md @@ -43,14 +43,15 @@ both hostname and port. hashi-ui can be controlled by both ENV or CLI flags as described below -| Environment | CLI (`--flag`) | Default | Description | -|------------------- |----------------------- |------------------------- |------------------------------------------------------------------------------------------------------ | -| `NOMAD_ADDR` | `nomad.address` | `http://127.0.0.1:4646` | Must point to the correct location of your Nomad server. | -| `NOMAD_READ_ONLY` | `nomad.read-only` | `false` | Should hash-ui allowed to modify nomad state (stop/start jobs and so forth) | -| `NOMAD_PORT_http` | `web.listen-address` | `0.0.0.0:3000` | The IP + PORT to listen on | -| `NOMAD_LOG_LEVEL` | `log.level` | `info` | Log level to use while running the hashi-ui server - (`critical`, `error`, `warning`, `notice`, `info`, `debug`) | -| `NEWRELIC_APP_NAME` | `newrelic.app_name` | `hashi-ui` | (optional) NewRelic application name | -| `NEWRELIC_LICENSE` | `newrelic.license` | '' | (optional) NewRelic license key | +| Environment | CLI (`--flag`) | Default | Description | +|-----------------------|-------------------------|---------------------------|--------------------------------------------------------------------------------------------------------| +| `NOMAD_ADDR` | `nomad.address` | `http://127.0.0.1:4646` | Must point to the correct location of your Nomad server. | +| `NOMAD_READ_ONLY` | `nomad.read-only` | `false` | Should hash-ui allowed to modify nomad state (stop/start jobs and so forth) | +| `NOMAD_PORT_http` | `web.listen-address` | `0.0.0.0:3000` | The IP + PORT to listen on | +| `NOMAD_PROXY_ADDRESS` | `web.proxy-address` | `` | (optional) The base URL of the UI when running behind a reverse proxy (ie: example.com/nomad/) | +| `NOMAD_LOG_LEVEL` | `log.level` | `info` | Log level to use while running the hashi-ui server - (`critical`, `error`, `warning`, `notice`, `info`, `debug`) | +| `NEWRELIC_APP_NAME` | `newrelic.app_name` | `hashi-ui` | (optional) NewRelic application name | +| `NEWRELIC_LICENSE` | `newrelic.license` | '' | (optional) NewRelic license key | # Try diff --git a/backend/main.go b/backend/main.go index 55bf7ef1..83353399 100644 --- a/backend/main.go +++ b/backend/main.go @@ -43,6 +43,7 @@ type Config struct { ReadOnly bool Address string ListenAddress string + ProxyAddress string LogLevel string NewRelicAppName string NewRelicLicense string @@ -85,6 +86,9 @@ var ( flagListenAddress = flag.String("web.listen-address", "", "The address on which to expose the web interface. "+flagDefault(defaultConfig.ListenAddress)) + flagProxyAddress = flag.String("web.proxy-address", "", + "The address used on an external proxy (exmaple: example.com/nomad) "+flagDefault(defaultConfig.ProxyAddress)) + flagLogLevel = flag.String("log.level", "", "The log level for hashi-ui to run under. "+flagDefault(defaultConfig.LogLevel)) @@ -115,6 +119,11 @@ func (c *Config) Parse() { c.ListenAddress = fmt.Sprintf("0.0.0.0:%s", listenPort) } + proxyAddress, ok := syscall.Getenv("NOMAD_PROXY_ADDRESS") + if ok { + c.ProxyAddress = proxyAddress + } + logLevel, ok := syscall.Getenv("NOMAD_LOG_LEVEL") if ok { c.LogLevel = logLevel @@ -144,6 +153,10 @@ func (c *Config) Parse() { c.ListenAddress = *flagListenAddress } + if *flagProxyAddress != "" { + c.ProxyAddress = *flagProxyAddress + } + if *flagLogLevel != "" { c.LogLevel = *flagLogLevel } @@ -188,6 +201,7 @@ func main() { logger.Infof("| nomad.address : %-50s |", cfg.Address) logger.Infof("| web.listen-address : http://%-43s |", cfg.ListenAddress) + logger.Infof("| web.proxy-address : %-50s |", cfg.ProxyAddress) logger.Infof("| log.level : %-50s |", cfg.LogLevel) if cfg.NewRelicAppName != "" && cfg.NewRelicLicense != "" { @@ -231,17 +245,24 @@ func main() { router := mux.NewRouter() router.HandleFunc(newrelic.WrapHandleFunc(app, "/ws", hub.Handler)) router.HandleFunc(newrelic.WrapHandleFunc(app, "/download/{path:.*}", nomad.downloadFile)) - router.PathPrefix("/static").Handler(http.FileServer(myAssetFS)) router.HandleFunc(newrelic.WrapHandleFunc(app, "/config.js", func(w http.ResponseWriter, r *http.Request) { response := make([]string, 0) response = append(response, fmt.Sprintf("window.NOMAD_READ_ONLY=%s", strconv.FormatBool(cfg.ReadOnly))) response = append(response, fmt.Sprintf("window.NOMAD_ADDR=\"%s\"", cfg.Address)) response = append(response, fmt.Sprintf("window.NOMAD_LOG_LEVEL=\"%s\"", cfg.LogLevel)) + var endpointURL string + if cfg.ProxyAddress != "" { + endpointURL = cfg.ProxyAddress + } else { + endpointURL = cfg.ListenAddress + } + response = append(response, fmt.Sprintf("window.NOMAD_ENDPOINT=\"%s\"", strings.TrimSuffix(endpointURL, "/"))) + w.Header().Set("Content-Type", "application/javascript") w.Write([]byte(strings.Join(response, "\n"))) })) - + router.PathPrefix("/static").Handler(http.FileServer(myAssetFS)) router.PathPrefix("/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if bs, err := myAssetFS.Open("/index.html"); err != nil { logger.Infof("%s", err) diff --git a/frontend/index.html.ejs b/frontend/index.html.ejs index 0ba3ddb9..78d73907 100644 --- a/frontend/index.html.ejs +++ b/frontend/index.html.ejs @@ -37,17 +37,11 @@ <% } %> - + <% if (htmlWebpackPlugin.options.window) { %> + + <% } else { %> + + <% } %>
diff --git a/frontend/src/main.js b/frontend/src/main.js index ee349eee..2bfb3f72 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -1,6 +1,10 @@ import React from 'react' import ReactDOM from 'react-dom' -import { browserHistory } from 'react-router' +import { createHistory, useBasename } from 'history'; +const browserHistory = useBasename(createHistory)({ + basename: window['NOMAD_ENDPOINT'].replace(/^[.a-zA-Z0-9]+:?[0-9]*/, '') +}); + import { Provider } from 'react-redux' import injectTapEventPlugin from 'react-tap-event-plugin' diff --git a/frontend/webpack-base.config.js b/frontend/webpack-base.config.js index 12d5cd8e..2ceb1dcf 100644 --- a/frontend/webpack-base.config.js +++ b/frontend/webpack-base.config.js @@ -4,7 +4,7 @@ const autoprefixer = require('autoprefixer'); module.exports = { output: { path: path.join(__dirname, 'build/'), - publicPath: '/' + publicPath: '' }, resolve: { extensions: ['', '.js', '.jsx', '.css', '.scss'] diff --git a/frontend/webpack.config.js b/frontend/webpack.config.js index 6ba5d0a8..38c92628 100644 --- a/frontend/webpack.config.js +++ b/frontend/webpack.config.js @@ -26,6 +26,7 @@ webpackConfig = merge(webpackConfig, { favicon: './assets/img/favicon.png', appMountId: 'app', window: { + NOMAD_ENDPOINT: process.env.GO_HOST || '127.0.0.1', NOMAD_ENDPOINT_PORT: process.env.GO_PORT || 3000 } }),