redirect requests to right host
This commit is contained in:
parent
22fe37d38a
commit
85d0ae7268
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Reference in a new issue