remove HOSTURL variable and instead get host from header

This commit is contained in:
HF 2020-01-09 15:54:55 +01:00
parent cfbae41817
commit ba144c3b8a
12 changed files with 72 additions and 42 deletions

View File

@ -65,8 +65,6 @@ Configuration takes place in the environment variables that are defined in ecosy
| Variable | Description | Example | | Variable | Description | Example |
|----------------|:-------------------------|------------------------:| |----------------|:-------------------------|------------------------:|
| HOSTURL | URL of the canvas | "http://localhost" |
| ASSET_SERVER | URL for assets | "http://localhost" |
| PORT | Port | 80 | | PORT | Port | 80 |
| REDIS_URL | URL:PORT of redis server | "http://localhost:6379" | | REDIS_URL | URL:PORT of redis server | "http://localhost:6379" |
| MYSQL_HOST | MySql Host | "localhost" | | MYSQL_HOST | MySql Host | "localhost" |
@ -76,16 +74,18 @@ Configuration takes place in the environment variables that are defined in ecosy
#### Optional Configuration #### Optional Configuration
| Variable | Description | Example | | Variable | Description | Example |
|-------------------|:--------------------------------------|-------------| |-------------------|:--------------------------------------|--------------------|
| USE_PROXYCHECK | Check users for Proxies | 0 | | ASSET_SERVER | URL for assets | "http://localhost" |
| APISOCKET_KEY | Key for API Socket for SpecialAccess™ | "SDfasife3" | | USE_PROXYCHECK | Check users for Proxies | 0 |
| ADMIN_IDS | Ids of users with Admin rights | "1,12,3" | | APISOCKET_KEY | Key for API Socket for SpecialAccess™ | "SDfasife3" |
| RECAPTCHA_SECRET | reCaptcha secret key | "asdieewff" | | ADMIN_IDS | Ids of users with Admin rights | "1,12,3" |
| RECAPTCHA_SITEKEY | reCaptcha site key | "23ksdfssd" | | RECAPTCHA_SECRET | reCaptcha secret key | "asdieewff" |
| RECAPTCHA_TIME | time in minutes between captchas | 30 | | RECAPTCHA_SITEKEY | reCaptcha site key | "23ksdfssd" |
| SESSION_SECRET | random sting for expression sessions | "ayylmao" | | RECAPTCHA_TIME | time in minutes between captchas | 30 |
| LOG_MYSQL | if sql queries should get logged | 0 | | SESSION_SECRET | random sting for expression sessions | "ayylmao" |
| LOG_MYSQL | if sql queries should get logged | 0 |
| USE_XREALIP | see cloudflare section | 1 |
Notes: Notes:
@ -156,10 +156,12 @@ pm2 log flush
pm2 stop web pm2 stop web
``` ```
### If using Cloudflare ### If using Cloudflare / Reverse Proxy
In order to get the real IP and not use the cloudflare Proxy IP for placing pixels, we filter those out. The cloudflare IPs are in src/utils/cloudflareip.js and used in src/utils/ip.js. If for some reason cloudflare ads more IPs to it, you can see them at https://www.cloudflare.com/ips/ and add them. In order to get the real IP and not use the cloudflare Proxy IP for placing pixels, we filter those out. The cloudflare IPs are in src/utils/cloudflareip.js and used in src/utils/ip.js. If for some reason cloudflare ads more IPs to it, you can see them at https://www.cloudflare.com/ips/ and add them.
If you use any other Reverse Proxy, you can define it's IPs there too. If you use any other Reverse Proxy, you can define it's IPs there too.
If USE\_XREALIP is set, we take the IP from the X-Real-Ip header without checking for cloudflare IPs. Use this if you have pixelplanet running behind nginx use the nginx set\_realip module to give us the client ip on the X-Real-Ip header. And be sure to also forward X-Forwarded-Port and set X-Forwarded-Proto.
### Auto-Start ### Auto-Start
To have the canvas with all it's components autostart at systemstart, To have the canvas with all it's components autostart at systemstart,
enable mysql, redis (and probably nginx if you use it) according to your system (`systemctl enable ...`) enable mysql, redis (and probably nginx if you use it) according to your system (`systemctl enable ...`)

View File

@ -7,20 +7,20 @@ import React from 'react';
import ReactDOM from 'react-dom/server'; import ReactDOM from 'react-dom/server';
import Html from './Html'; import Html from './Html';
const RedirectionPage = ({ text }) => ( const RedirectionPage = ({ text, host }) => (
<div> <div>
<h3>{text}</h3> <h3>{text}</h3>
<p>You will be automatically redirected after 5s</p> <p>You will be automatically redirected after 5s</p>
<p>Or <a href="https://pixelplanet.fun">Click here</a> to go back to pixelplanet</p> <p>Or <a href={host}>Click here</a> to go back to pixelplanet</p>
</div> </div>
); );
export function getHtml(description, text) { export function getHtml(description, text, host) {
const data = { const data = {
title: 'PixelPlanet.fun Accounts', title: 'PixelPlanet.fun Accounts',
description, description,
body: <RedirectionPage text={text} />, body: <RedirectionPage text={text} host ={host} />,
code: 'window.setTimeout(function(){window.location.href="https://pixelplanet.fun";},4000)', code: `window.setTimeout(function(){window.location.href="${host}";},4000)`,
}; };
const index = `<!doctype html>${ReactDOM.renderToStaticMarkup(<Html {...data} />)}`; const index = `<!doctype html>${ReactDOM.renderToStaticMarkup(<Html {...data} />)}`;
return index; return index;

View File

@ -7,8 +7,6 @@ if (process.env.BROWSER) {
throw new Error('Do not import `config.js` from inside the client-side code.'); throw new Error('Do not import `config.js` from inside the client-side code.');
} }
export const HOSTURL = process.env.HOSTURL || 'https://pixelplanet.fun';
export const PORT = process.env.PORT || 80; export const PORT = process.env.PORT || 80;
const TILE_FOLDER_REL = process.env.TILE_FOLDER || 'tiles'; const TILE_FOLDER_REL = process.env.TILE_FOLDER || 'tiles';
@ -16,6 +14,8 @@ export const TILE_FOLDER = path.join(__dirname, `./${TILE_FOLDER_REL}`);
export const ASSET_SERVER = process.env.ASSET_SERVER || '.'; export const ASSET_SERVER = process.env.ASSET_SERVER || '.';
export const USE_XREALIP = process.env.USE_XREALIP || false;
// Proxycheck // Proxycheck
export const USE_PROXYCHECK = parseInt(process.env.USE_PROXYCHECK, 10) || false; export const USE_PROXYCHECK = parseInt(process.env.USE_PROXYCHECK, 10) || false;

View File

@ -8,7 +8,6 @@ import nodemailer from 'nodemailer';
import logger from './logger'; import logger from './logger';
import { HOUR, MINUTE } from './constants'; import { HOUR, MINUTE } from './constants';
import { HOSTURL } from './config';
import { DailyCron, HourlyCron } from '../utils/cron'; import { DailyCron, HourlyCron } from '../utils/cron';
import RegUser from '../data/models/RegUser'; import RegUser from '../data/models/RegUser';
@ -37,7 +36,7 @@ class MailProvider {
DailyCron.hook(MailProvider.cleanUsers); DailyCron.hook(MailProvider.cleanUsers);
} }
sendVerifyMail(to, name) { sendVerifyMail(to, name, host) {
const pastMail = this.verifyCodes[to]; const pastMail = this.verifyCodes[to];
if (pastMail) { if (pastMail) {
const minLeft = Math.floor( const minLeft = Math.floor(
@ -53,7 +52,7 @@ class MailProvider {
} }
logger.info(`Sending verification mail to ${to} / ${name}`); logger.info(`Sending verification mail to ${to} / ${name}`);
const code = this.setCode(to); const code = this.setCode(to);
const verifyUrl = `${HOSTURL}/api/auth/verify?token=${code}`; const verifyUrl = `${host}/api/auth/verify?token=${code}`;
transporter.sendMail({ transporter.sendMail({
from: 'donotreply@pixelplanet.fun', from: 'donotreply@pixelplanet.fun',
to, to,
@ -70,7 +69,7 @@ class MailProvider {
return null; return null;
} }
async sendPasswdResetMail(to, ip) { async sendPasswdResetMail(to, ip, host) {
const pastMail = this.verifyCodes[to]; const pastMail = this.verifyCodes[to];
if (pastMail) { if (pastMail) {
if (Date.now() < pastMail.timestamp + 15 * MINUTE) { if (Date.now() < pastMail.timestamp + 15 * MINUTE) {
@ -100,7 +99,7 @@ class MailProvider {
logger.info(`Sending Password reset mail to ${to}`); logger.info(`Sending Password reset mail to ${to}`);
const code = this.setCode(to); const code = this.setCode(to);
const restoreUrl = `${HOSTURL}/reset_password?token=${code}`; const restoreUrl = `${host}/reset_password?token=${code}`;
transporter.sendMail({ transporter.sendMail({
from: 'donotreply@pixelplanet.fun', from: 'donotreply@pixelplanet.fun',
to, to,

View File

@ -16,7 +16,7 @@ import { sanitizeName } from '../utils/validation';
import logger from './logger'; import logger from './logger';
import { User, RegUser } from '../data/models'; import { User, RegUser } from '../data/models';
import { auth, HOSTURL } from './config'; import { auth } from './config';
import { compareToHash } from '../utils/hash'; import { compareToHash } from '../utils/hash';
@ -105,7 +105,8 @@ async function oauth_login(email, name, discordid = null) {
*/ */
passport.use(new FacebookStrategy({ passport.use(new FacebookStrategy({
...auth.facebook, ...auth.facebook,
callbackURL: `${HOSTURL}/api/auth/facebook/return`, callbackURL: `/api/auth/facebook/return`,
proxy: true,
profileFields: ['displayName', 'email'], profileFields: ['displayName', 'email'],
}, async (req, accessToken, refreshToken, profile, done) => { }, async (req, accessToken, refreshToken, profile, done) => {
const { displayName: name, emails } = profile; const { displayName: name, emails } = profile;
@ -119,7 +120,8 @@ passport.use(new FacebookStrategy({
*/ */
passport.use(new DiscordStrategy({ passport.use(new DiscordStrategy({
...auth.discord, ...auth.discord,
callbackURL: `${HOSTURL}/api/auth/discord/return`, callbackURL: `/api/auth/discord/return`,
proxy: true,
}, async (accessToken, refreshToken, profile, done) => { }, async (accessToken, refreshToken, profile, done) => {
// TODO get discord id // TODO get discord id
console.log({ profile, refreshToken, accessToken }); console.log({ profile, refreshToken, accessToken });
@ -133,7 +135,8 @@ passport.use(new DiscordStrategy({
*/ */
passport.use(new GoogleStrategy({ passport.use(new GoogleStrategy({
...auth.google, ...auth.google,
callbackURL: `${HOSTURL}/api/auth/google/return`, callbackURL: `/api/auth/google/return`,
proxy: true,
}, async (accessToken, refreshToken, profile, done) => { }, async (accessToken, refreshToken, profile, done) => {
const { displayName: name, emails } = profile; const { displayName: name, emails } = profile;
const email = emails[0].value; const email = emails[0].value;
@ -146,7 +149,8 @@ passport.use(new GoogleStrategy({
*/ */
passport.use(new RedditStrategy({ passport.use(new RedditStrategy({
...auth.reddit, ...auth.reddit,
callbackURL: `${HOSTURL}/api/auth/reddit/return`, callbackURL: `/api/auth/reddit/return`,
proxy: true,
}, async (accessToken, refreshToken, profile, done) => { }, async (accessToken, refreshToken, profile, done) => {
console.log({ profile, refreshToken, accessToken }); console.log({ profile, refreshToken, accessToken });
const redditid = profile.id; const redditid = profile.id;
@ -177,7 +181,8 @@ passport.use(new RedditStrategy({
*/ */
passport.use(new VkontakteStrategy({ passport.use(new VkontakteStrategy({
...auth.vk, ...auth.vk,
callbackURL: `${HOSTURL}/api/auth/vk/return`, callbackURL: `/api/auth/vk/return`,
proxy: true,
scope: ['email'], scope: ['email'],
profileFields: ['displayName', 'email'], profileFields: ['displayName', 'email'],
}, async (accessToken, refreshToken, params, profile, done) => { }, async (accessToken, refreshToken, params, profile, done) => {

View File

@ -8,6 +8,7 @@ import type { Request, Response } from 'express';
import mailProvider from '../../../core/mail'; import mailProvider from '../../../core/mail';
import { validatePassword, validateEMail } from '../../../utils/validation'; import { validatePassword, validateEMail } from '../../../utils/validation';
import { getHostFromRequest } from '../../../utils/ip';
import { compareToHash } from '../../../utils/hash'; import { compareToHash } from '../../../utils/hash';
function validate(email, password) { function validate(email, password) {
@ -55,7 +56,8 @@ export default async (req: Request, res: Response) => {
mailVerified: false, mailVerified: false,
}); });
mailProvider.sendVerifyMail(email, user.regUser.name); const host = getHostFromRequest(req);
mailProvider.sendVerifyMail(email, user.regUser.name, host);
res.json({ res.json({
success: true, success: true,

View File

@ -6,6 +6,7 @@
import express from 'express'; import express from 'express';
import logger from '../../../core/logger'; import logger from '../../../core/logger';
import { getHostFromRequest } from '../../../utils/ip';
import register from './register'; import register from './register';
import verify from './verify'; import verify from './verify';
@ -67,7 +68,8 @@ export default (passport) => {
res.set({ res.set({
'Content-Type': 'text/html', 'Content-Type': 'text/html',
}); });
const index = getHtml('OAuth Authentification', 'LogIn failed :(, please try again later or register a new account with Mail.'); const host = getHostFromRequest(req);
const index = getHtml('OAuth Authentification', 'LogIn failed :(, please try again later or register a new account with Mail.', host);
res.status(200).send(index); res.status(200).send(index);
}); });

View File

@ -10,6 +10,7 @@ import Sequelize from 'sequelize';
import { RegUser } from '../../../data/models'; import { RegUser } from '../../../data/models';
import mailProvider from '../../../core/mail'; import mailProvider from '../../../core/mail';
import getMe from '../../../core/me'; import getMe from '../../../core/me';
import { getHostFromRequest } from '../../../utils/ip';
import { import {
validateEMail, validateEMail,
validateName, validateName,
@ -73,7 +74,8 @@ export default async (req: Request, res: Response) => {
}); });
return; return;
} }
mailProvider.sendVerifyMail(email, name); const host = getHostFromRequest(req);
mailProvider.sendVerifyMail(email, name, host);
res.status(200); res.status(200);
res.json({ res.json({
success: true, success: true,

View File

@ -7,6 +7,7 @@
import type { Request, Response } from 'express'; import type { Request, Response } from 'express';
import mailProvider from '../../../core/mail'; import mailProvider from '../../../core/mail';
import { getHostFromRequest } from '../../../utils/ip';
export default async (req: Request, res: Response) => { export default async (req: Request, res: Response) => {
const { user } = req; const { user } = req;
@ -27,7 +28,9 @@ export default async (req: Request, res: Response) => {
return; return;
} }
const error = mailProvider.sendVerifyMail(email, name); const host = getHostFromRequest(req);
const error = mailProvider.sendVerifyMail(email, name, host);
if (error) { if (error) {
res.status(400); res.status(400);
res.json({ res.json({

View File

@ -8,6 +8,7 @@ import type { Request, Response } from 'express';
import mailProvider from '../../../core/mail'; import mailProvider from '../../../core/mail';
import { validateEMail } from '../../../utils/validation'; import { validateEMail } from '../../../utils/validation';
import { getHostFromRequest } from '../../../utils/ip';
async function validate(email) { async function validate(email) {
const errors = []; const errors = [];
@ -29,7 +30,8 @@ export default async (req: Request, res: Response) => {
}); });
return; return;
} }
const error = await mailProvider.sendPasswdResetMail(email, ip); const host = getHostFromRequest(req);
const error = await mailProvider.sendPasswdResetMail(email, ip, host);
if (error) { if (error) {
res.status(400); res.status(400);
res.json({ res.json({

View File

@ -6,17 +6,19 @@
import type { Request, Response } from 'express'; import type { Request, Response } from 'express';
import { getHtml } from '../../../components/RedirectionPage'; import { getHtml } from '../../../components/RedirectionPage';
import { getHostFromRequest } from '../../../utils/ip';
import mailProvider from '../../../core/mail'; import mailProvider from '../../../core/mail';
export default async (req: Request, res: Response) => { export default async (req: Request, res: Response) => {
const { token } = req.query; const { token } = req.query;
const success = await mailProvider.verify(token); const success = await mailProvider.verify(token);
const host = getHostFromRequest(req);
if (success) { if (success) {
const index = getHtml('Mail verification', 'You are now verified :)'); const index = getHtml('Mail verification', 'You are now verified :)', host);
res.status(200).send(index); res.status(200).send(index);
} else { } else {
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
const index = getHtml('Mail verification', 'Your mail verification code is invalid or already expired :(, please request a new one.'); const index = getHtml('Mail verification', 'Your mail verification code is invalid or already expired :(, please request a new one.', host);
res.status(400).send(index); res.status(400).send(index);
} }
}; };

View File

@ -7,6 +7,8 @@ import isCloudflareIp from './cloudflareip';
import logger from '../core/logger'; import logger from '../core/logger';
import { USE_XREALIP } from '../core/config';
function isTrustedProxy(ip: string): boolean { function isTrustedProxy(ip: string): boolean {
if (ip === '::ffff:127.0.0.1' || ip === '127.0.0.1' || isCloudflareIp(ip)) { if (ip === '::ffff:127.0.0.1' || ip === '127.0.0.1' || isCloudflareIp(ip)) {
@ -15,15 +17,24 @@ function isTrustedProxy(ip: string): boolean {
return false; return false;
} }
/** export function getHostFromRequest(req): ?string {
* Note: nginx should handle that, const { headers } = req;
* it's not neccessary to do that ourself const host = headers['x-forwarded-host'] || headers['host'];
*/ const proto = headers['x-forwarded-proto'] || 'http';
return `${proto}://${host}`;
}
export async function getIPFromRequest(req): ?string { export async function getIPFromRequest(req): ?string {
const { socket, connection, headers } = req; const { socket, connection, headers } = req;
const conip = (connection ? connection.remoteAddress : socket.remoteAddress); const conip = (connection ? connection.remoteAddress : socket.remoteAddress);
if (USE_XREALIP) {
const ip = headers['x-real-ip'];
return ip || conip;
}
if (!headers['x-forwarded-for'] || !isTrustedProxy(conip)) { if (!headers['x-forwarded-for'] || !isTrustedProxy(conip)) {
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
logger.warn(`Connection not going through nginx and cloudflare! IP: ${conip}`, headers); logger.warn(`Connection not going through nginx and cloudflare! IP: ${conip}`, headers);