Google App Engine - Redirect HTTP to HTTPS - google-app-engine

I'm new to app engine and I'm trying to set it up so that any http requests get redirected to https.
My app.yaml file looks like this. I have script: None in there because if I don't have it there I get some parsing error, but that's not the problem.
env: flex
runtime: nodejs
handlers:
- url: /.*
script: None
secure: always
So right now, if I go to http :// mysite.org it stays on the http version and just shows 'mysite.net' in the url bar. If I go to https :// mysite.org it shows the secured version. If I go to the appspot url that google gave me, the http redirects to the https version just fine. Is there something I'm missing in the app.yaml? This isnt in a custom runtime

Use helmet, secure setting under handlers in app.yaml is depricated in the Google App Engine Latest Release.
https://helmetjs.github.io/docs/hsts/
https://expressjs.com/en/advanced/best-practice-security.html
// Forcing HTTPS connections on Gooogle App Engine Flexible Environment sample app.js
'use strict';
const express = require('express');
const helmet = require('helmet');
const app = express();
const port = process.env.PORT || 8080;
app.disable('x-powered-by');
app.enable('trust proxy');
app.use(helmet.hsts({
maxAge: 31536000,
includeSubDomains: true,
preload: true,
setIf: function (req, res) {
return req.secure;
}
}));
app.get('/', (req, res) => {
if (!req.secure) {
res.redirect(301, "https://" + req.headers.host + req.originalUrl);
}
res.status(200).send("hello, world\n").end();
});
app.listen(port, () => {
console.log(`App listening on port ${port}`);
console.log('Press Ctrl+C to quit.');
});
Upgrading to the App Engine Latest Release
The secure setting under handlers is now deprecated for the App Engine flexible environment. If you need SSL redirection, you can update your application code and use the X-Forwarded-Proto header to redirect http traffic.
https://cloud.google.com/appengine/docs/flexible/php/upgrading#appyaml_changes
Forcing HTTPS connections
For security reasons, all applications should encourage clients to connect over https. You can use the Strict-Transport-Security header to instruct the browser to prefer https over http for a given page or an entire domain, for example:
Strict-Transport-Security: max-age=31536000; includeSubDomains
https://cloud.google.com/appengine/docs/flexible/php/how-requests-are-handled
HTTPS and forwarding proxies
With Express.js, use the trust proxy setting
app.set('trust proxy', true);
https://cloud.google.com/appengine/docs/flexible/nodejs/runtime#https_and_forwarding_proxies

Adding the code snippet below to web.xml works just fine on Flexible environment
<security-constraint>
<web-resource-collection>
<web-resource-name>Entire Application</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>

Related

CORS request did not succeed, when deploying MERN stack app in digitalocean

I have two servers running. The react frontend is running on port 3000 and the express backend is running on port 8000. The frontend makes request to the backend, but the request returns CORS error.This problem only occurs on the digital ocean server.
The request from the react app:
await Axios.get("http://localhost:8000/api").then(
(response) =>{
setListOfArticles(response.data);
setIsLoading(false)
},
)
Express backend:
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*"); // update to match the domain you will make the request from
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next(); });
I have read multiple answers on this topic and tried to change localhost:8000 with 0.0.0.0:8000, but with no success.
When adding:
app.use(express.static(path.join(__dirname, '../front/build')))
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '../front/build'))
})
to the express backend and opening it in the browser the same error occurs.
Do you know why this happens? Note that the app is running flawlessly on my pc.
I don't understand why you are running your front end server on port 3000 unless you do server side rendering. If you are just having a traditional Single page application, you have to build and serve the files through Nginx or even express.
Serve your express app and react app using Nginx. Following link could be helpful.
https://javascript.plainenglish.io/how-to-deploy-a-react-app-with-expressjs-and-nginx-29abeef08c67
And you cannot hardcode localhost:8000 into your react app.
await axios.get("/api").then(
(response) =>{
setListOfArticles(response.data);
setIsLoading(false)
},
)

My react app and express server are on different ports

Problem/error:
I am running my react app on port 3000 and express on 3001.
error: Access to fetch at 'http://localhost:3001/api' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
//react code
//this function gets called when a button get pressed
...
post = () => {
const client_data = {info: this.state.input};
const options = {
method: "POST",
headers: {
"Content-Type": "application/json
},
body: JSON.stringify(client_data)
}
fetch("http://localhost:3001/api", options);
}
...
Server is pretty simple
var express = require('express');
var app = express();
app.post('api', (request, response)=>{console.log(request})
(when I add "app.listen(3000)" react stops working)
You have to proxy your requests from react to express. The best way is to let it do your WebPack devServer, having this configuration:
(I assume, your express server is on 3000 and your React app on 3001)
devServer: {
...
...
proxy: {
'/api': {
target: 'http://127.0.0.1:3000/',
},
},
This says, that whichever request to path /api will be redirected to port 3001. Then you fetch to
fetch("/api", options);
Client automatically requests localhost:3001 (port on which the application is running), but the proxy redirects to 3000.
Brief explanation:
When developing react app, react creates a live developing enviroment that runs on -p 3000. However, if I spin up a node backend on -p 3000, the ports collide and the live enviroment crashes.
Solution:
Spin the node backend on a different port (4000)
and make a proxy in the live app
SERVER:
app.listen(4000, () => console.log("server is up on -p 4000")
CLIENT:
In the react app package.json file insert this key/value:
{
...
"proxy": "http://localhost:4000",
...
}

Can I access app engine service by using http protocol?

I deployed a nodejs web server to GAE flexible environment.
When I access my web server, it using https protocol.
I try to use http protocol, but it seems can not be accessed.
Does GAE support http?
I expect both http and https should work fine.
update 1
app.yaml
runtime: nodejs8
env_variables:
NODE_ENV: production
PORT: 8080
app.flex.yaml
service: get-started-flex
runtime: nodejs
env: flex
env_variables:
NODE_ENV: production
manual_scaling:
instances: 1
resources:
cpu: 1
memory_gb: 0.5
disk_size_gb: 10
server.js
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const package = require('./package.json');
console.log('version: ', package.version);
console.log('process.env.NODE_ENV: ', process.env.NODE_ENV);
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.get('/', (req, res) => {
res.send(`Hello from App Engine! version:${package.version}`);
});
app.get('/submit', (req, res) => {
res.sendFile(path.join(__dirname, '/views/form.html'));
});
app.post('/submit', (req, res) => {
console.log({
name: req.body.name,
message: req.body.message
});
res.send('Thanks for your message!');
});
// Listen to the App Engine-specified port, or 8080 otherwise
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}...`);
});
In App Engine Standard you have the option to state the security of each handler by using the secure tag, however, in Flexible this is not possible.
Besides, in Flexible you can do http and http requests to the handlers but with some restrictions. Note that the application knows what protocol to use depending on the X-Forwarded-Proto header of the request, as you can see in this documentation.
However, I believe you can change this behaviour on the application level of your app. My guess is that you are enforcing https in some part of your application, for example, if you have the line require("https") in your application.
Also, I have found this answer that could help in your case. If you disable SSL security checks in your handler, you might be able to process the requests even if the connection is not secure, but this will make your application insecure.

Force SSL on App Engine Flexible Environment Custom Runtime

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?
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.
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!
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.
This is what worked for me. In my case using Loopback based NodeJS application running in Cloud Sites App Engine flexible environment.
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)
Modify the file server/server.js to request:
var httpsRedirect = require('./middleware/https-redirect');
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:
http://expressjs.com/es/api.html#req.protocol
http://expressjs.com/en/guide/behind-proxies.html
http://www.jonxie.com/blog/2014/11/12/setting-up-loopback-to-use-https-and-ssl-certificates/
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);
}
});
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.

Rendering AngularJS HTML page on server side NodeJS

I have NodeJS server + AnglarJS page.
All works.
Do working examples or frameworks that allow the download page on the server NodeJS and fill it and return to the client a static page. It is necessary for Browsers IE 5-8 (90% clients).
Yes, take a look at Express for Node.js - you can easily serve up a single static page and then use a link to redirect users to your Angular app (using a 'login' page, for example).
Here is an example of a very basic Express app.
var express = require('express')
var app = express()
app.get('/', function (req, res) {
res.send('Hello World!')
})
var server = app.listen(3000, function () {
var host = server.address().address
var port = server.address().port
console.log('Example app listening at http://%s:%s', host, port)
})
The req (request) and res (response) are the exact same objects that Node provides, so you can invoke req.pipe(), req.on('data', callback) and anything else you would do without Express involved.
The app starts a server and listens on port 3000 for connection. It will respond with "Hello World!" for requests to the homepage. For every other path, it will respond with a 404 Not Found.
Save the code in a file named app.js and run it with the following command.
$ node app.js
Then, load http://localhost:3000/ in a browser to see the output.

Resources