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