레이블이 flask인 게시물을 표시합니다. 모든 게시물 표시
레이블이 flask인 게시물을 표시합니다. 모든 게시물 표시

2025년 3월 15일 토요일

Python 으로 작성한 리버스 프록시 코드 (Flask 사용 내부 테스트용)

파이썬으로 작성한 리버스프록시 코드. 안드로이드 앱이 외부 접속을 할 때 https 가 아니면 안되는 경우가 있어서 제작함.


from flask import Flask, request, Response
import requests
import logging
from logging.handlers import RotatingFileHandler

app = Flask(__name__)
# netsh advfirewall firewall add rule name="Allow [EXTERNAL_PORT]" dir=in action=allow protocol=TCP localport=[EXTERNAL_PORT]
# 포워딩할 대상 URL (내부 HTTP 서버)
INTERNAL_PORT = 54321  # 내부 서버 포트 (예시)
TARGET = f"http://127.0.0.1:{INTERNAL_PORT}"

# 로그 파일 설정: server.log 파일에 최대 10,000바이트까지 저장하며, 1개의 백업 파일 생성
handler = RotatingFileHandler('server.log', maxBytes=10000, backupCount=1, encoding='utf-8')
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s %(levelname)s: %(message)s')
handler.setFormatter(formatter)
app.logger.addHandler(handler)
app.logger.setLevel(logging.INFO)

# 모든 경로와 HTTP 메서드를 처리하도록 라우팅
@app.route('/', defaults={'path': ''}, methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'])
@app.route('/', methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'])
def proxy(path):
    target_url = f"{TARGET}/{path}"
    app.logger.info(f"Request: {request.method} {request.url} -> Forwarding to: {target_url}")
    
    headers = {key: value for key, value in request.headers if key.lower() != 'host'}
    
    # 대상 서버에 요청 전송
    resp = requests.request(
        method=request.method,
        url=target_url,
        headers=headers,
        params=request.args,
        data=request.get_data(),
        cookies=request.cookies,
        allow_redirects=False
    )
    
    app.logger.info(f"Response: Status code {resp.status_code} from {target_url}")
    
    # 대상 서버의 응답을 그대로 클라이언트에 전달
    response = Response(resp.content, resp.status_code)
    for key, value in resp.headers.items():
        response.headers[key] = value
    return response

if __name__ == '__main__':
    # Let's Encrypt 인증서 사용 (인증서 파일 경로는 마스킹 처리됨)
    ssl_context = ("[인증서 경로]/fullchain.pem", "[인증서 경로]/privkey.pem")
    EXTERNAL_PORT = 12345  # 외부에 노출될 포트 (예시)
    app.logger.info(f"Starting HTTPS reverse proxy server on port {EXTERNAL_PORT} with Let's Encrypt certificate")
    app.run(host='0.0.0.0', port=EXTERNAL_PORT, ssl_context=ssl_context)