File size: 3,385 Bytes
5d66f59 915b179 f8390d3 1dbbd42 f8390d3 bc3acf5 915b179 f8390d3 bc3acf5 5d66f59 bc3acf5 5d66f59 f8390d3 5d66f59 bc3acf5 5d66f59 915b179 1dbbd42 f8390d3 5d66f59 1dbbd42 bc3acf5 1dbbd42 5d66f59 915b179 5d66f59 bc3acf5 915b179 5d66f59 1dbbd42 915b179 1dbbd42 5d66f59 f8390d3 5d66f59 f8390d3 5d66f59 1dbbd42 915b179 f8390d3 5d66f59 f8390d3 bc3acf5 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
import re
import logging
from flask import Flask, request, Response
import requests
from urllib.parse import urlparse
# --- 设置日志 ---
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
app = Flask(__name__)
# --- 白名单过滤规则 ---
ALLOWED_PATTERNS = [
re.compile(r'^https://github\.com/[^/]+/[^/]+/(?:releases|archive)/.*$', re.IGNORECASE),
re.compile(r'^https://github\.com/[^/]+/[^/]+/(?:blob|raw)/.*$', re.IGNORECASE),
# --- 这里是修正后的一行 ---
re.compile(r'^https://github\.com/[^/]+/[^/]+/(?:info|git-).*$', re.IGNORECASE),
re.compile(r'^https://raw\.(?:githubusercontent|github)\.com/[^/]+/[^/]+/.*/.*$', re.IGNORECASE),
re.compile(r'^https://gist\.(?:githubusercontent|github)\.com/[^/]+/[^/]+/.*/.*$', re.IGNORECASE),
re.compile(r'^https://github\.com/[^/]+/[^/]+/tags.*$', re.IGNORECASE),
re.compile(r'^https://avatars\.githubusercontent\.com/.*$', re.IGNORECASE),
re.compile(r'^https://github\.githubassets\.com/.*$', re.IGNORECASE),
re.compile(r'^https://github\.com/[^/]+/?$', re.IGNORECASE),
re.compile(r'^https://github\.com/[^/]+/[^/]+/?$', re.IGNORECASE),
]
def is_url_allowed(url):
"""检查给定的URL是否匹配白名单中的任何一个模式。"""
for pattern in ALLOWED_PATTERNS:
if pattern.match(url):
return True
return False
# --- 核心代理逻辑 ---
@app.route('/', defaults={'path': ''}, methods=['GET', 'POST', 'PUT', 'DELETE'])
@app.route('/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE'])
def proxy(path):
target_path = request.full_path
if target_path.startswith('/'):
target_path = target_path[1:]
if not target_path:
return ("<p>GitHub reverse proxy is active.</p>"), 200
if not target_path.startswith(('http://', 'https://')):
target_url = 'https://' + target_path
else:
target_url = target_path
if not is_url_allowed(target_url):
logging.warning(f"URL Denied! No pattern matched: {target_url}")
error_message = "<h1>403 Forbidden</h1><p>Request blocked by proxy security policy.</p>"
return error_message, 403
try:
target_host = urlparse(target_url).hostname
if not target_host:
raise ValueError("Could not parse hostname")
except Exception as e:
return f"Invalid target URL in path: {e}", 400
headers = {key: value for (key, value) in request.headers if key.lower() != 'host'}
headers['Host'] = target_host
try:
resp = requests.request(
method=request.method,
url=target_url,
headers=headers,
data=request.get_data(),
cookies=request.cookies,
allow_redirects=False,
stream=True,
timeout=30
)
excluded_headers = ['content-encoding', 'content-length', 'transfer-encoding', 'connection']
response_headers = [(name, value) for (name, value) in resp.raw.headers.items() if name.lower() not in excluded_headers]
return Response(resp.iter_content(chunk_size=8192), status=resp.status_code, headers=response_headers)
except requests.exceptions.RequestException as e:
return f"An error occurred while proxying: {e}", 502
if __name__ == '__main__':
app.run(host='0.0.0.0', port=7860)
|