redirect requests to right host

This commit is contained in:
Izalia Mae 2020-01-21 00:58:24 -05:00
parent 22fe37d38a
commit 85d0ae7268
5 changed files with 56 additions and 45 deletions

View file

@ -6,8 +6,6 @@ Web proxy for Mastodon that puts public profiles behind an auth layer.
PAWS sits between Mastodon and your front-facing web proxy to intercept incoming requests. If a profile, toot, or any related json is requested, it will be blocked unless authenticated. If authenticated fetches on mastodon are disabled, PAWS will check signatures instead
Note: Still very much a WIP. Currently it's just simple http auth, but I plan on adding the ability to login via oauth
## Installation
Python 3.6.0+ (3.8.0 recommended)
@ -21,6 +19,7 @@ data/production.env:
```
# Path to mastodon instance. Defaults to current working dir
MASTOPATH=/home/mastodon/glitch-soc
MASTOHOST=localhost:3000
# Listen address and port for PAWS. Can safely be ignored if running on same host as web server
PAWS_HOST=127.0.0.1

View file

@ -66,8 +66,9 @@ else:
PAWSCONFIG = {
'host': env.get('PAWS_HOST', '127.0.0.1'),
'port': int(env.get('PAWS_PORT', 3001)),
'domain': env.get('PAWS_DOMAIN', 'bappypaws.example.com'),
'mastopath': env.get('MASTOPATH', os.getcwd()),
'domain': env.get('PAWS_DOMAIN', 'bappypaws.example.com')
'mastohost': env.get('MASTOHOST', 'localhost:3000')
}

View file

@ -133,6 +133,20 @@ def dig(domain):
logging.info(f'Failed to resolve IP: {e}')
def distill_query(querydata):
rawquery = ''
if len(querydata) > 0:
for var in querydata:
if rawquery == '':
rawquery += f'{var}={querydata[var]}'
else:
rawquery += f'&{var}={querydata[var]}'
return rawquery if rawquery != '?' else ''
def css_ts():
from .config import script_path

View file

@ -16,7 +16,7 @@ from aiohttp.client_exceptions import *
from Crypto.PublicKey import RSA
from .signature import validate, pass_hash, sign_headers
from .functions import error, user_check, domain_check, parse_sig, parse_ua, dig
from .functions import error, user_check, domain_check, parse_sig, parse_ua, dig, distill_query
from .config import MASTOCONFIG, PAWSCONFIG, VERSION, script_path
from .database import pawsdb, query, trans, ban_check, user_ban_check, banned_user_check, wl_check, keys, user_domain_ban_check
@ -49,20 +49,28 @@ auth_paths = [
]
async def passthrough(request, headers, urlquery=None, is_json=False):
QUERY = urlquery if urlquery else ''
async def passthrough(request, headers):
mastohost = PAWSCONFIG['mastohost']
data = await request.read() if request.body_exists else None
is_json = True if request.get('reqtype') == 'json' else False
try:
# I don't think I need this, but I'll leave it here just in case
#async with aiohttp.request(reqtype, f'https://{MASTOCONFIG["domain"]}/{path}{query}', headers=headers, data=post) as resp:
async with aiohttp.request(request.method, f'http://localhost:3000{request.path}?{urlquery}', headers=headers) as resp:
async with aiohttp.request(request.method, f'http://{mastohost}{request.path}?{distill_query(request.query)}', headers=headers, data=data) as resp:
data = await resp.read()
if resp.status != 200:
logging.warning(data)
logging.warning(f'Recieved error {resp.status} from Mastodon')
return error(resp.status, f'Failed to forward request. Recieved error {resp.status} from Mastodon')
if resp.status not in [200, 202]:
if data in [b'Request not signed', 'Request not signed']:
err_msg = 'Missing signature'
logging.debug(err_msg)
else:
err_msg = f'Recieved error {resp.status} from Mastodon'
logging.debug(err_msg)
return error(resp.status, f'Failed to forward request. {err_msg}')
return aiohttp.web.HTTPOk(body=data, content_type=resp.content_type)
@ -86,6 +94,10 @@ async def http_filter(app, handler):
# add logged in user data to the request for the frontend
request['user'] = user_data
logging.warn(request.host)
if request.path not in ['/paws/actor', '/paws/inbox', '/.well-known/webfinger'] and request.host == paws_host:
return aiohttp.web.HTTPFound(f'http://{masto_host}{request.path}?{distill_query(request.query)}')
# try to find the domain for the request
if not domain:
@ -140,42 +152,28 @@ async def http_filter(app, handler):
if user_ban_check(user.lower(), (user_data['handle'].lower(), user_data['instance'])) or user_domain_ban_check(user.lower(), user_data['instance']):
return http_error(request, 403, 'Access Denied')
querydata = request.query
rawquery = ''
if not signature and real_ip == ua_ip and wl_check(domain) and request.method == 'GET':
logging.info(f'Signing fetch for whitelisted instance: {domain}')
if len(querydata) > 0:
for var in querydata:
if rawquery == '':
rawquery += f'{var}={querydata[var]}'
HEADERS = {
'(request-target)': f'get {request.path}',
'Accept': 'application/json',
'User-Agent': f'PAWS/{VERSION}; https://{masto_host}',
'Host': masto_host,
'X-Forwarded-For': request.headers.get('X-Forwarded-For', request.remote),
'X-Forwarded-Port': '443',
'X-Forwarded-Proto': 'https',
'X-Real-Ip': request.headers.get('X-Real-Ip', request.remote)
}
HEADERS['signature'] = sign_headers(HEADERS, 'default', f'https://{paws_host}/paws/actor#main-key')
HEADERS.pop('(request-target)')
else:
rawquery += f'&{var}={querydata[var]}'
else:
HEADERS = request.headers
urlquery = rawquery if rawquery != '?' else None
if not signature and real_ip == ua_ip and wl_check(domain) and request.method == 'GET':
logging.info(f'Signing fetch for whitelisted instance: {domain}')
HEADERS = {
'(request-target)': f'get {request.path}',
'Accept': 'application/json',
'User-Agent': f'PAWS/{VERSION}; https://{masto_host}',
'Host': masto_host,
'X-Forwarded-For': request.headers.get('X-Forwarded-For', request.remote),
'X-Forwarded-Port': '443',
'X-Forwarded-Proto': 'https',
'X-Real-Ip': request.headers.get('X-Real-Ip', request.remote)
}
HEADERS['signature'] = sign_headers(HEADERS, 'default', f'https://{paws_host}/paws/actor#main-key')
HEADERS.pop('(request-target)')
else:
HEADERS = request.headers
is_json = True if request.get('reqtype') == 'json' else False
get_data = await passthrough(request, HEADERS, urlquery=urlquery, is_json=is_json)
return get_data
if not request.path.startswith('/paws'):
return_data = await passthrough(request, HEADERS)
return return_data
return (await handler(request))
return http_filter_handler

View file

@ -24,7 +24,6 @@ def webserver():
web.on_response_prepare.append(middleware.http_server_header)
web.add_routes([
aiohttp.web.route('GET', '/', views.get_home),
aiohttp.web.route('GET', '/paws', views.get_paws),
aiohttp.web.route('GET', '/paws/imgay', views.get_gay),
aiohttp.web.route('POST', '/paws/action/{action}', views.post_paws),