17

We're running an instance of Metabase on a App Engine Flexible Custom Runtime with a Dockerfile based on openjdk:8. Currently it allows access on http://[metabase-project].appspot.com/ and https://[metabase-project].appspot.com/. I'd like to force SSL by having all http traffic redirected to https.

The Dockerfile looks something like this:

FROM openjdk:8 ADD https://dl.google.com/cloudsql/cloud_sql_proxy.linux.amd64 ./cloud_sql_proxy ADD http://downloads.metabase.com/v0.21.1/metabase.jar ./metabase.jar CMD ./cloud_sql_proxy -instances=$INSTANCE=tcp:$MB_DB_PORT -dir=/cloudsql & java -jar ./metabase.jar 

Our app.yaml looks like:

service: metabase runtime: custom env: flex 

In a normal App Engine app.yaml file, I'd want to add:

handlers: - url: [something] secure: always 

But in the custom runtime we don't have access to handlers like this. Is there a way to configure the Flexible runtime to perform the redirect for all traffic?

3
  • 1
    Does your domain provider offer domain forwarding? You can forward all http: to https: Commented Jan 30, 2017 at 20:47
  • 1
    @GAEfan this would only apply to a custom domain, and the project appspot.com subdomain would allow http without forwarding, right? Commented Jan 30, 2017 at 21:59
  • 1
    Correct, it would only work for custom domain. I am not aware of a built-in for the appspot domain. You may need to test the request header for https, and force your own redirect for non-https requests. Commented Jan 30, 2017 at 22:44

6 Answers 6

9

Late to answer, but I had to struggle a lot in order to do this.

I followed various links which mentioned the following code,

app.use(function(req, res, next) { if(!req.secure) { return res.redirect(['https://', req.get('Host'), req.url].join('')); } next(); }); 

This might work in other cloud vendors.

But in GCP as rightly mentioned by @zengabor, our app will be running behind an nginx reverse proxy which terminates the SSL connection, we need to check the X-FORWARDED-PROTO which can be done by the following code,

app.use(function(req, res, next) { if(req.headers['x-forwarded-proto'] && req.headers['x-forwarded-proto'] === "http") { return res.redirect(['https://', req.get('Host'), req.url].join('')); } next(); }); 

Just adding my answer as after reading @zengabor's code I had to search again on how to achieve it. So above is the readymade code which will work.

Sign up to request clarification or add additional context in comments.

1 Comment

It is important to use the status code 307 to work with methods other than GET: res.redirect(307, `https://${req.get('Host')}${req.url}`);
8

App Engine Flex doesn't support handlers, at all: https://cloud.google.com/appengine/docs/flexible/java/upgrading#appyaml_changes

If you need https:// redirects, you need to do it from within your application. Sorry!

2 Comments

Is that only with Java apps? Or with everything?
I did that in the app following this stackoverflow.com/questions/10697660/force-ssl-with-expressjs-3, but I am getting "redirected you too many times.". I think the https is terminating on nginx itself and hence it always gives me protocol as http and secure as false.
7

Since your app (env: flex in app.yaml) is running behind an nginx reverse proxy which terminates the SSL connection, you need to check the X-FORWARDED-PROTO header which will be either http or https. If it’s http then you can do the redirect.

3 Comments

I am under the impression that I don't have the ability to modify the nginx config (stackoverflow.com/questions/40853799/…). I'm using Metabase, which has explicitly decided that this should be supported by the hosting platform and not their tools. They have a config for EB that sets up the redirect correctly (github.com/metabase/metabase/issues/1356), but it appears that there's no way to do with on appengine flex.
Since Metabase seems to be open source it is technically possible to extend it. :)
Confirm that "X-Forwarded-Proto" header is present today and either "http" or "https".
4

This is what worked for me. In my case using Loopback based NodeJS application running in Cloud Sites App Engine flexible environment.

  1. Create a middleware, for example server/middleware/https-redirect.js with the following code:

    /** * Create a middleware to redirect http requests to https * @param {Object} options Options * @returns {Function} The express middleware handler */ module.exports = function(options) { options = options || {}; var httpsPort = options.httpsPort || 443; return function(req, res, next) { if (req.protocol != 'https' && process.env.NODE_ENV !== 'development') { var parts = req.get('host').split(':'); var host = parts[0] || '127.0.0.1'; return res.redirect('https://' + host + ':' + httpsPort + req.url); } next(); }; }; 

    (based on the step 8 in the post http://www.jonxie.com/blog/2014/11/12/setting-up-loopback-to-use-https-and-ssl-certificates/ but modified to use req.protocol instead of req.secure, also will only redirect if not running in development mode)

  2. Modify the file server/server.js to request:

    var httpsRedirect = require('./middleware/https-redirect'); 
  3. An then, after the boot line:

    var httpsPort = app.get('https-port'); app.use(httpsRedirect({httpsPort: httpsPort})); app.set('trust proxy', true) 

Setting app.set('trust proxy', true) will let the req.protocol read the X-Forwarded-Proto header.

References:

Comments

0

Use the following code

app.use (function (req, res, next) { var schema = (req.headers['x-forwarded-proto'] || '').toLowerCase(); if (schema === 'https') { next(); } else { res.redirect('https://' + req.headers.host + req.url); } }); 

Comments

0

Here's the Node.js Express code I use:

// set the env variable REQUIRE_HTTPS=1 to enable this middleware .use(function(req, res, next) { // Redirect HTTP to HTTPS if (!process.env.REQUIRE_HTTPS) return next(); if (req.headers["x-forwarded-proto"] === "https") return next(); if (req.protocol === "https") return next(); res.redirect(301, `https://${req.hostname}${req.url}`); }) 

Put it as the first middleware in your Express app.

This code assumes that you're using standard ports for http/https, as is the case when you're on AppEngine.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.