feat: Initial code drop
All checks were successful
Build Docker / Build Docker (push) Successful in 41s
All checks were successful
Build Docker / Build Docker (push) Successful in 41s
This commit is contained in:
parent
f011e2fd2d
commit
f129e2d722
3
.env.example
Normal file
3
.env.example
Normal file
@ -0,0 +1,3 @@
|
||||
PROXY=http://nathanwoodburn:5000/
|
||||
TLD=woodburn
|
||||
RESTRICTED=["admin"]
|
38
.gitea/workflows/build.yml
Normal file
38
.gitea/workflows/build.yml
Normal file
@ -0,0 +1,38 @@
|
||||
name: Build Docker
|
||||
run-name: Build Docker Images
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
Build Docker:
|
||||
runs-on: [ubuntu-latest, amd] # Add amd to require amd64
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Install Docker
|
||||
run : |
|
||||
echo "Updating apt sources"
|
||||
echo "deb http://ftp.au.debian.org/debian buster main" > /etc/apt/sources.list
|
||||
apt-get update --allow-unauthenticated --allow-insecure-repositories
|
||||
apt-get install docker.io -y
|
||||
- name: Build Docker image
|
||||
run : |
|
||||
echo "${{ secrets.DOCKERGIT_TOKEN }}" | docker login git.woodburn.au -u nathanwoodburn --password-stdin
|
||||
echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}"
|
||||
tag=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}
|
||||
tag=${tag//\//-}
|
||||
tag_num=${GITHUB_RUN_NUMBER}
|
||||
echo "tag_num=$tag_num"
|
||||
|
||||
if [[ "$tag" == "main" ]]; then
|
||||
tag="latest"
|
||||
else
|
||||
tag_num="${tag}-${tag_num}"
|
||||
fi
|
||||
|
||||
|
||||
docker build -t tld_restricted_proxy:$tag_num .
|
||||
docker tag tld_restricted_proxy:$tag_num git.woodburn.au/nathanwoodburn/tld_restricted_proxy:$tag_num
|
||||
docker push git.woodburn.au/nathanwoodburn/tld_restricted_proxy:$tag_num
|
||||
docker tag tld_restricted_proxy:$tag_num git.woodburn.au/nathanwoodburn/tld_restricted_proxy:$tag
|
||||
docker push git.woodburn.au/nathanwoodburn/tld_restricted_proxy:$tag
|
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
.env
|
||||
|
||||
__pycache__/
|
||||
|
||||
cookies.json
|
17
Dockerfile
Normal file
17
Dockerfile
Normal file
@ -0,0 +1,17 @@
|
||||
FROM --platform=$BUILDPLATFORM python:3.10-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY requirements.txt /app
|
||||
RUN --mount=type=cache,target=/root/.cache/pip \
|
||||
pip3 install -r requirements.txt
|
||||
|
||||
COPY . /app
|
||||
|
||||
# Add mount point for data volume
|
||||
# VOLUME /data
|
||||
|
||||
ENTRYPOINT ["python3"]
|
||||
CMD ["server.py"]
|
||||
|
||||
FROM builder as dev-envs
|
@ -1 +1,7 @@
|
||||
# g-user-proxy
|
||||
# TLD Restricted Proxy
|
||||
|
||||
Environment variables:
|
||||
- `PROXY` - The proxy to use for restricted TLDs. Example: `http://localhost:3128`
|
||||
- `TLD` - The TLD to restrict. Example: `g`
|
||||
- `RESTRICTED` - List of restricted paths. Example: `["path1", "path2"]` will require the user to have a .g domain to access `path1/*`, `path2/*`
|
||||
|
||||
|
130
main.py
Normal file
130
main.py
Normal file
@ -0,0 +1,130 @@
|
||||
from flask import Flask, make_response, redirect, request, jsonify, render_template, send_from_directory, render_template_string
|
||||
import os
|
||||
import dotenv
|
||||
import requests
|
||||
import datetime
|
||||
import json
|
||||
import secrets
|
||||
import threading
|
||||
|
||||
app = Flask(__name__)
|
||||
dotenv.load_dotenv()
|
||||
URL = os.getenv('PROXY')
|
||||
RESTRICTED = os.getenv('RESTRICTED')
|
||||
RESTRICTED = json.loads(RESTRICTED)
|
||||
RESTRICTED = [f'{i.lower()}/' for i in RESTRICTED]
|
||||
TLD = os.getenv('TLD')
|
||||
|
||||
# Load cookies
|
||||
cookies = []
|
||||
|
||||
if os.path.isfile('cookies.json'):
|
||||
with open('cookies.json') as file:
|
||||
cookies = json.load(file)
|
||||
else:
|
||||
with open('cookies.json', 'w') as file:
|
||||
json.dump(cookies, file)
|
||||
|
||||
|
||||
# region Auth
|
||||
@app.route('/auth', methods=['POST'])
|
||||
def auth():
|
||||
global cookies
|
||||
auth = login(request)
|
||||
if auth == False:
|
||||
return render_template_string("Failed to authenticate")
|
||||
|
||||
# Make sure user has a correct domain
|
||||
if not auth.endswith(f'.{TLD}'):
|
||||
return render_template_string(f"You need to have a domain on .{TLD} to access this content.")
|
||||
|
||||
resp = make_response(render_template_string("Success"))
|
||||
# Gen cookie
|
||||
auth_cookie = secrets.token_hex(12 // 2)
|
||||
cookies.append({'name': auth, 'cookie': auth_cookie})
|
||||
|
||||
with open('cookies.json', 'w') as file:
|
||||
json.dump(cookies, file)
|
||||
|
||||
resp.set_cookie('auth', auth_cookie, max_age=60*60*24*30)
|
||||
return resp
|
||||
|
||||
@app.route('/logout')
|
||||
def logout():
|
||||
global cookies
|
||||
resp = make_response(redirect('/'))
|
||||
resp.set_cookie('auth', '', expires=0)
|
||||
|
||||
if 'auth' not in request.cookies:
|
||||
return resp
|
||||
cookies = [i for i in cookies if i['cookie'] != request.cookies['auth']]
|
||||
with open('cookies.json', 'w') as file:
|
||||
json.dump(cookies, file)
|
||||
|
||||
return resp
|
||||
|
||||
def login(request):
|
||||
dict = request.form.to_dict()
|
||||
keys = dict.keys()
|
||||
keys = list(keys)[0]
|
||||
keys = json.loads(keys)
|
||||
auth_request = keys['request']
|
||||
# return login(auth_request)
|
||||
r = requests.get(f'https://auth.varo.domains/verify/{auth_request}')
|
||||
r = r.json()
|
||||
if r['success'] == False:
|
||||
return False
|
||||
|
||||
if 'data' in r:
|
||||
data = r['data']
|
||||
if 'name' in data:
|
||||
return data['name']
|
||||
return False
|
||||
|
||||
# endregion
|
||||
|
||||
# Catch all
|
||||
@app.route('/', defaults={'path': ''})
|
||||
@app.route('/<path:path>', methods=['GET', 'POST'])
|
||||
def catch_all(path):
|
||||
for i in RESTRICTED:
|
||||
if path.lower().startswith(i) or path.lower() == i:
|
||||
# Check if user is logged in
|
||||
if 'auth' not in request.cookies:
|
||||
return render_template('auth.html', year=datetime.datetime.now().year,redirect=request.url,tld=TLD)
|
||||
auth = request.cookies['auth']
|
||||
if not any(i['cookie'] == auth for i in cookies):
|
||||
return render_template('auth.html', year=datetime.datetime.now().year,redirect=request.url,tld=TLD)
|
||||
break
|
||||
|
||||
|
||||
|
||||
res = requests.request(
|
||||
method = request.method,
|
||||
url = request.url.replace(request.host_url, f'{URL}/'),
|
||||
headers = {k:v for k,v in request.headers if k.lower() != 'host'},
|
||||
data = request.get_data(),
|
||||
cookies = request.cookies,
|
||||
allow_redirects = False,
|
||||
)
|
||||
excluded_headers = ['content-encoding', 'content-length', 'transfer-encoding', 'connection']
|
||||
headers = [
|
||||
(k,v) for k,v in res.raw.headers.items()
|
||||
if k.lower() not in excluded_headers
|
||||
]
|
||||
|
||||
# Replace all instances of the proxy URL with the local URL
|
||||
# If content type is html
|
||||
if 'text/html' in res.headers['Content-Type']:
|
||||
content = res.content.decode('utf-8')
|
||||
content = content.replace(URL, request.host_url)
|
||||
# TMP: Replace other domains
|
||||
content = content.replace('https://alee.freeconcept/', request.host_url)
|
||||
response = make_response(content, res.status_code, headers)
|
||||
return response
|
||||
|
||||
response = make_response(res.content, res.status_code, headers)
|
||||
return response
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True, port=5000, host='0.0.0.0')
|
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@ -0,0 +1,4 @@
|
||||
flask
|
||||
python-dotenv
|
||||
gunicorn
|
||||
requests
|
33
server.py
Normal file
33
server.py
Normal file
@ -0,0 +1,33 @@
|
||||
from flask import Flask
|
||||
from main import app
|
||||
import main
|
||||
from gunicorn.app.base import BaseApplication
|
||||
|
||||
|
||||
class GunicornApp(BaseApplication):
|
||||
def __init__(self, app, options=None):
|
||||
self.options = options or {}
|
||||
self.application = app
|
||||
super().__init__()
|
||||
|
||||
def load_config(self):
|
||||
for key, value in self.options.items():
|
||||
if key in self.cfg.settings and value is not None:
|
||||
self.cfg.set(key.lower(), value)
|
||||
|
||||
def load(self):
|
||||
return self.application
|
||||
|
||||
if __name__ == '__main__':
|
||||
workers = 1
|
||||
threads = 2
|
||||
workers = int(workers)
|
||||
threads = int(threads)
|
||||
options = {
|
||||
'bind': '0.0.0.0:5000',
|
||||
'workers': workers,
|
||||
'threads': threads,
|
||||
}
|
||||
gunicorn_app = GunicornApp(app, options)
|
||||
print('Starting server with ' + str(workers) + ' workers and ' + str(threads) + ' threads', flush=True)
|
||||
gunicorn_app.run()
|
82
templates/auth.html
Normal file
82
templates/auth.html
Normal file
@ -0,0 +1,82 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: #232429;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
/* Align in the center vertically */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 70vh
|
||||
|
||||
}
|
||||
bold {
|
||||
text-emphasis: bold;
|
||||
font-weight: bold;
|
||||
}
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
background-color: black;
|
||||
color: white;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
border-radius: 5px;
|
||||
font-size: 1.25em;
|
||||
}
|
||||
h1 {
|
||||
font-size: 4em;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
p {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
</style>
|
||||
<script src="https://auth.varo.domains/v1"></script>
|
||||
<!-- Import Jquery -->
|
||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div>
|
||||
<h1 style="color: red;" id="error">
|
||||
{{error}}
|
||||
</h1>
|
||||
<h1>This content is protected</h1>
|
||||
<p>Please verify you own a <bold>.{{tld}}</bold> domain to access this content</p>
|
||||
<script>
|
||||
var varo = new Varo;
|
||||
</script>
|
||||
<div id="varo-login">
|
||||
<button id="varo-login-button">Login</button>
|
||||
</div>
|
||||
<script>
|
||||
var button = document.getElementById('varo-login-button');
|
||||
button.onclick = function () {
|
||||
varo.auth().then(auth => {
|
||||
if (auth.success) {
|
||||
// handle success by calling your api to update the users session
|
||||
$.post("/auth", JSON.stringify(auth.data), (response) => {
|
||||
// If response returned true, redirect to the page
|
||||
if (response == 'Success')
|
||||
window.location = '{{redirect}}';
|
||||
else
|
||||
// Set error message
|
||||
document.getElementById('error').innerText = response;
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user