Changed app for proxy and https++
This commit is contained in:
179
app.py
179
app.py
@@ -8,6 +8,7 @@ import argparse
|
|||||||
import sys
|
import sys
|
||||||
import secrets
|
import secrets
|
||||||
import logging
|
import logging
|
||||||
|
import json
|
||||||
|
|
||||||
# Configure logging
|
# Configure logging
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
@@ -46,15 +47,48 @@ app.config.update(
|
|||||||
PREFERRED_URL_SCHEME='https'
|
PREFERRED_URL_SCHEME='https'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Set this to True to use the special cookie fix for Zoraxy
|
||||||
|
ZORAXY_COOKIE_FIX = True
|
||||||
|
|
||||||
|
def fix_cookie_for_zoraxy(response):
|
||||||
|
"""Special handler for Zoraxy cookie issues with SameSite=None"""
|
||||||
|
if not ZORAXY_COOKIE_FIX:
|
||||||
|
return response
|
||||||
|
|
||||||
|
# Get all cookies from the response
|
||||||
|
cookies = response.headers.getlist('Set-Cookie')
|
||||||
|
if not cookies:
|
||||||
|
return response
|
||||||
|
|
||||||
|
# Clear existing cookies
|
||||||
|
del response.headers['Set-Cookie']
|
||||||
|
|
||||||
|
# Fix each cookie and add it back
|
||||||
|
for cookie in cookies:
|
||||||
|
if 'malias_session' in cookie and 'SameSite' not in cookie:
|
||||||
|
# Add SameSite=None and Secure attributes
|
||||||
|
if 'HttpOnly' in cookie:
|
||||||
|
cookie = cookie.replace('HttpOnly', 'HttpOnly; SameSite=None; Secure')
|
||||||
|
else:
|
||||||
|
cookie += '; SameSite=None; Secure'
|
||||||
|
response.headers.add('Set-Cookie', cookie)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
@app.after_request
|
@app.after_request
|
||||||
def add_security_headers(response):
|
def after_request(response):
|
||||||
"""Add security headers to every response"""
|
"""Process the response before it's sent"""
|
||||||
# Add SameSite=None explicitly for all cookies if Auth proxy is detected
|
# Apply special cookie fix for Zoraxy
|
||||||
if request.headers.get('Remote-User') or request.headers.get('X-Remote-User'):
|
response = fix_cookie_for_zoraxy(response)
|
||||||
cookie_header = response.headers.get('Set-Cookie', '')
|
|
||||||
if cookie_header and 'SameSite=' not in cookie_header:
|
# Set CORS headers to allow Zoraxy and Authelia to work together
|
||||||
cookie_header = cookie_header.replace('HttpOnly', 'HttpOnly; SameSite=None; Secure')
|
response.headers['Access-Control-Allow-Origin'] = request.headers.get('Origin', '*')
|
||||||
response.headers['Set-Cookie'] = cookie_header
|
response.headers['Access-Control-Allow-Credentials'] = 'true'
|
||||||
|
|
||||||
|
# Cache control
|
||||||
|
response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'
|
||||||
|
response.headers['Pragma'] = 'no-cache'
|
||||||
|
response.headers['Expires'] = '0'
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@@ -64,9 +98,12 @@ def get_authelia_user():
|
|||||||
auth_headers = [
|
auth_headers = [
|
||||||
'Remote-User',
|
'Remote-User',
|
||||||
'X-Remote-User',
|
'X-Remote-User',
|
||||||
'X-Authelia-Username',
|
'X-Authelia-Username',
|
||||||
'X-Forwarded-User',
|
'X-Forwarded-User',
|
||||||
'REMOTE_USER'
|
'REMOTE_USER',
|
||||||
|
'Http-Remote-User',
|
||||||
|
'Http-X-Remote-User',
|
||||||
|
'X-Authenticated-User'
|
||||||
]
|
]
|
||||||
|
|
||||||
for header in auth_headers:
|
for header in auth_headers:
|
||||||
@@ -75,6 +112,19 @@ def get_authelia_user():
|
|||||||
logger.info(f"Authelia user detected via {header}: {user}")
|
logger.info(f"Authelia user detected via {header}: {user}")
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
# Check Zoraxy forwarded headers (sometimes encoded differently)
|
||||||
|
if 'X-Forwarded-Headers' in request.headers:
|
||||||
|
try:
|
||||||
|
# Some reverse proxies encode headers as JSON
|
||||||
|
fwd_headers = json.loads(request.headers.get('X-Forwarded-Headers'))
|
||||||
|
for header in auth_headers:
|
||||||
|
if header in fwd_headers:
|
||||||
|
user = fwd_headers[header]
|
||||||
|
logger.info(f"Authelia user detected via forwarded headers - {header}: {user}")
|
||||||
|
return user
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def login_required(f):
|
def login_required(f):
|
||||||
@@ -105,7 +155,9 @@ def login_required(f):
|
|||||||
# Regular session check
|
# Regular session check
|
||||||
if not session.get('logged_in'):
|
if not session.get('logged_in'):
|
||||||
logger.warning("Access denied: User not authenticated")
|
logger.warning("Access denied: User not authenticated")
|
||||||
return jsonify({'status': 'error', 'message': 'Not authenticated', 'redirect': '/login'}), 401
|
if request.is_json:
|
||||||
|
return jsonify({'status': 'error', 'message': 'Not authenticated', 'redirect': '/login'}), 401
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
return decorated_function
|
return decorated_function
|
||||||
@@ -130,7 +182,20 @@ def login():
|
|||||||
session['user_token'] = secrets.token_urlsafe(32)
|
session['user_token'] = secrets.token_urlsafe(32)
|
||||||
session['auth_method'] = 'authelia'
|
session['auth_method'] = 'authelia'
|
||||||
session.modified = True
|
session.modified = True
|
||||||
return redirect(url_for('index'))
|
|
||||||
|
# Set a cookie manually to ensure it's properly formatted for Zoraxy
|
||||||
|
response = redirect(url_for('index'))
|
||||||
|
# Set cookie parameters to work with Zoraxy/Authelia
|
||||||
|
response.set_cookie(
|
||||||
|
key=app.config['SESSION_COOKIE_NAME'],
|
||||||
|
value=request.cookies.get(app.config['SESSION_COOKIE_NAME']),
|
||||||
|
max_age=int(app.config['PERMANENT_SESSION_LIFETIME'].total_seconds()),
|
||||||
|
path=app.config['SESSION_COOKIE_PATH'],
|
||||||
|
secure=app.config['SESSION_COOKIE_SECURE'],
|
||||||
|
httponly=app.config['SESSION_COOKIE_HTTPONLY'],
|
||||||
|
samesite='None'
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|
||||||
# Handle form submission for local authentication
|
# Handle form submission for local authentication
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
@@ -146,7 +211,24 @@ def login():
|
|||||||
session['auth_method'] = 'local'
|
session['auth_method'] = 'local'
|
||||||
session.modified = True
|
session.modified = True
|
||||||
|
|
||||||
|
# Return JSON response
|
||||||
response = jsonify({'status': 'success', 'message': 'Login successful'})
|
response = jsonify({'status': 'success', 'message': 'Login successful'})
|
||||||
|
|
||||||
|
# Manually set cookie with correct parameters for Zoraxy
|
||||||
|
if ZORAXY_COOKIE_FIX:
|
||||||
|
max_age = int(app.config['PERMANENT_SESSION_LIFETIME'].total_seconds())
|
||||||
|
cookie_value = request.cookies.get(app.config['SESSION_COOKIE_NAME']) or session.sid
|
||||||
|
response.set_cookie(
|
||||||
|
app.config['SESSION_COOKIE_NAME'],
|
||||||
|
cookie_value,
|
||||||
|
max_age=max_age,
|
||||||
|
secure=app.config['SESSION_COOKIE_SECURE'],
|
||||||
|
httponly=app.config['SESSION_COOKIE_HTTPONLY'],
|
||||||
|
samesite='None',
|
||||||
|
path=app.config['SESSION_COOKIE_PATH']
|
||||||
|
)
|
||||||
|
logger.info(f"Set fixed cookie for Zoraxy: {app.config['SESSION_COOKIE_NAME']}")
|
||||||
|
|
||||||
return response
|
return response
|
||||||
else:
|
else:
|
||||||
logger.warning("Login failed: Invalid password")
|
logger.warning("Login failed: Invalid password")
|
||||||
@@ -156,6 +238,12 @@ def login():
|
|||||||
if session.get('logged_in'):
|
if session.get('logged_in'):
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
|
# Check cookies and client IP for troubleshooting
|
||||||
|
if app.debug:
|
||||||
|
logger.info(f"Cookies: {request.cookies}")
|
||||||
|
logger.info(f"Client IP: {request.remote_addr}")
|
||||||
|
logger.info(f"X-Forwarded-For: {request.headers.get('X-Forwarded-For')}")
|
||||||
|
|
||||||
# Show login form
|
# Show login form
|
||||||
return render_template('login.html')
|
return render_template('login.html')
|
||||||
|
|
||||||
@@ -195,7 +283,12 @@ def logout():
|
|||||||
return redirect(common_authelia_urls[0])
|
return redirect(common_authelia_urls[0])
|
||||||
|
|
||||||
# Default case: redirect to login page
|
# Default case: redirect to login page
|
||||||
return redirect(url_for('login'))
|
response = redirect(url_for('login'))
|
||||||
|
|
||||||
|
# Clear cookie by setting expired date
|
||||||
|
response.set_cookie(app.config['SESSION_COOKIE_NAME'], '', expires=0)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def index():
|
def index():
|
||||||
@@ -213,11 +306,34 @@ def index():
|
|||||||
session['user_token'] = secrets.token_urlsafe(32)
|
session['user_token'] = secrets.token_urlsafe(32)
|
||||||
session['auth_method'] = 'authelia'
|
session['auth_method'] = 'authelia'
|
||||||
session.modified = True
|
session.modified = True
|
||||||
|
|
||||||
|
# Set cookie manually with correct parameters
|
||||||
|
response = make_response(render_template('index.html'))
|
||||||
|
|
||||||
|
if ZORAXY_COOKIE_FIX:
|
||||||
|
max_age = int(app.config['PERMANENT_SESSION_LIFETIME'].total_seconds())
|
||||||
|
cookie_value = request.cookies.get(app.config['SESSION_COOKIE_NAME']) or session.sid
|
||||||
|
response.set_cookie(
|
||||||
|
app.config['SESSION_COOKIE_NAME'],
|
||||||
|
cookie_value,
|
||||||
|
max_age=max_age,
|
||||||
|
secure=app.config['SESSION_COOKIE_SECURE'],
|
||||||
|
httponly=app.config['SESSION_COOKIE_HTTPONLY'],
|
||||||
|
samesite='None',
|
||||||
|
path=app.config['SESSION_COOKIE_PATH']
|
||||||
|
)
|
||||||
|
logger.info(f"Set fixed cookie for Zoraxy on index: {app.config['SESSION_COOKIE_NAME']}")
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
# Check if logged in
|
# Check if logged in
|
||||||
if not session.get('logged_in'):
|
if not session.get('logged_in'):
|
||||||
return redirect(url_for('login'))
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
# Debug logging
|
||||||
|
if app.debug and session.get('logged_in'):
|
||||||
|
logger.info(f"User authenticated with method: {session.get('auth_method', 'unknown')}")
|
||||||
|
|
||||||
# Show main page
|
# Show main page
|
||||||
return render_template('index.html')
|
return render_template('index.html')
|
||||||
|
|
||||||
@@ -453,7 +569,10 @@ def health_check():
|
|||||||
'authenticated': session.get('logged_in', False),
|
'authenticated': session.get('logged_in', False),
|
||||||
'authelia_user': session.get('authelia_user', None),
|
'authelia_user': session.get('authelia_user', None),
|
||||||
'auth_method': session.get('auth_method'),
|
'auth_method': session.get('auth_method'),
|
||||||
'version': '1.0.1'
|
'version': '1.0.2',
|
||||||
|
'cookies': {k: '***' for k in request.cookies.keys()}, # Only show cookie names
|
||||||
|
'authelia_headers_present': get_authelia_user() is not None,
|
||||||
|
'zoraxy_headers_present': 'X-Forwarded-Server' in request.headers
|
||||||
})
|
})
|
||||||
|
|
||||||
# Add a debugging endpoint
|
# Add a debugging endpoint
|
||||||
@@ -489,6 +608,7 @@ def debug_info():
|
|||||||
'app_config': {
|
'app_config': {
|
||||||
'DEBUG': app.debug,
|
'DEBUG': app.debug,
|
||||||
'PREFERRED_URL_SCHEME': app.config['PREFERRED_URL_SCHEME'],
|
'PREFERRED_URL_SCHEME': app.config['PREFERRED_URL_SCHEME'],
|
||||||
|
'ZORAXY_COOKIE_FIX': ZORAXY_COOKIE_FIX
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,6 +628,37 @@ def show_headers():
|
|||||||
'authelia_user': get_authelia_user()
|
'authelia_user': get_authelia_user()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# Self-test endpoint for cookies
|
||||||
|
@app.route('/cookie-test')
|
||||||
|
def cookie_test():
|
||||||
|
"""Test cookie handling"""
|
||||||
|
# Clear any existing cookie
|
||||||
|
resp = make_response(jsonify({'status': 'ok', 'message': 'Cookie set test'}))
|
||||||
|
|
||||||
|
# Set a test cookie with the same attributes as session
|
||||||
|
resp.set_cookie(
|
||||||
|
'malias_test_cookie',
|
||||||
|
'test-value',
|
||||||
|
max_age=3600,
|
||||||
|
secure=app.config['SESSION_COOKIE_SECURE'],
|
||||||
|
httponly=app.config['SESSION_COOKIE_HTTPONLY'],
|
||||||
|
samesite='None',
|
||||||
|
path=app.config['SESSION_COOKIE_PATH']
|
||||||
|
)
|
||||||
|
|
||||||
|
return resp
|
||||||
|
|
||||||
|
# Endpoint to check if test cookie is set
|
||||||
|
@app.route('/cookie-check')
|
||||||
|
def cookie_check():
|
||||||
|
"""Check if test cookie was properly set"""
|
||||||
|
test_cookie = request.cookies.get('malias_test_cookie')
|
||||||
|
return jsonify({
|
||||||
|
'test_cookie_present': test_cookie is not None,
|
||||||
|
'test_cookie_value': test_cookie if test_cookie else None,
|
||||||
|
'all_cookies': {k: '***' for k in request.cookies.keys()}
|
||||||
|
})
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# Parse command-line arguments
|
# Parse command-line arguments
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
|
|||||||
Reference in New Issue
Block a user