서버
AimiliVPN은 리눅스 VPS 한 대 위에서 VPN Gate 노드 목록을 가져와 OpenVPN으로 연결하고, 그 연결을 HTTP·HTTPS(CONNECT)·SOCKS5 프록시와 웹 관리 화면으로 제공하는 도구입니다. 저장소 이름의 “vpngate”는 일본 쓰쿠바대학이 운영하는 학술 프로젝트 VPN Gate를 가리키며, 이 저장소는 그 서비스의 공개 API를 소비하는 쪽입니다.
검토한 커밋은 2cd7dc58이며 VERSION 파일은 2.1.5, 라이선스는 GPL-3.0입니다. 추적 파일은 25개이고 핵심은 파이썬 5개와 설치 스크립트 1개입니다. 도커 이미지와 일반 리눅스 소스 설치를 모두 지원합니다.
VPN Gate는 전 세계 자원봉사자가 열어 둔 “공용 창구” 목록입니다. AimiliVPN은 목록에서 상태 좋은 창구를 골라 연결해 두고, 내 서버 안의 프로그램들이 줄 서는 “전용 접수 창구”(7928 포트)를 하나 세웁니다. 창구가 문을 닫으면 다른 곳으로 몰래 바꿔 줍니다.
다만 공용 창구의 운영자는 누구인지 알 수 없습니다. 이 도구는 연결 품질을 관리해 줄 뿐 그 창구를 믿을 만하게 만들어 주지는 않습니다.
https://www.vpngate.net/api/iphone/입니다.| 기능 | 구현 위치 | 핵심 포인트 |
|---|---|---|
| 노드 목록 수집 | vpngate_manager.py api_network_sources() | 공식 → 미러 → 로컬 캐시 → 동봉 스냅샷 순 폴백 |
| 설정 파일 검증 | snapshot_utils.py validate_openvpn_config() | 허용된 지시어만 통과 |
| OpenVPN 연결·전환 | openvpn_command(), connect_node() | 실패 시 직전 연결 유지, 자동 전환 |
| 프록시 게이트웨이 | proxy_server.py | 첫 바이트로 SOCKS5/HTTP 판별, tun0에 소켓 고정 |
| 웹 관리 화면 | Handler 클래스 | 비밀 경로 + 계정 + 쿠키 세션 |
| IP 유형 판별 | vpn_utils.py classify_ip_type() | 주거/모바일/데이터센터/미확인 + 신뢰도 |
README의 사용 범위 조항은 합법적인 네트워크 연구, 교육, 개발 테스트, 프라이버시 보호만 허용하고 규제 우회·무단 접근·공격·스팸·사기를 금지한다고 적습니다. 소스 안에도 공격 도구나 탐지 회피 기능은 보이지 않았습니다(7장 끝과 11장 참고). 이 문서는 도구의 구조를 설명하는 학습 자료이며 특정 지역의 이용 가능 여부를 판단해 주지 않습니다.
이 저장소에서 가져갈 핵심은 “남이 만든 접속 설정 파일을 실행하기 전에 무엇을 허용하고 무엇을 거부할지 코드로 정해 두는 방법”입니다.
TrendShift 목록에서 이 저장소는 23위로 잡혔습니다. 이 순위는 모멘텀 점수이며 설치 수나 품질을 뜻하지 않습니다. 릴리스는 v2.1.3, v2.1.4, v2.1.5가 확인되고, 최신 커밋 시각은 2026-10-14, 전체 커밋은 91개입니다.
install.sh가 패키지 설치(apt/apk/dnf/yum), /opt/aimilivpn 배포, systemd 또는 OpenRC(init.d) 서비스 등록, 관리 명령 ml 생성까지 처리합니다.mirror/vpngate.csv(약 99행)까지 내려갑니다.tun0에 묶습니다. SSH 같은 기존 연결이 끊기지 않게 하는 설계입니다(4장).tests/test_manager_logic.py 1,131줄에 테스트 함수 53개가 있고 릴리스 워크플로가 파이썬 3.9·3.11·3.13과 도커 4개 아키텍처를 검사합니다.| 구분 | 이 저장소 | 일반 VPN 클라이언트 | 직접 만든 WireGuard/OpenVPN 서버 |
|---|---|---|---|
| 서버 운영자 | 알 수 없는 자원봉사자 | 상업 사업자 | 본인 |
| 사용 형태 | VPS 안에서 프록시 포트로 사용 | 기기 전체 터널 | 기기 전체 터널 |
| 비용 구조 | 무료 노드, VPS 비용만 | 구독료 | VPS 비용 |
| 신뢰 모델 | 노드를 신뢰하지 않는다고 가정하고 쓰는 것이 안전 | 사업자 약관·정책에 의존 | 본인이 전부 책임 |
README 앞부분에는 VPS 업체와 AI 중계 서비스의 제휴·홍보 링크가 있고, 웹 관리 화면(vpngate_manager.py의 HTML 문자열)에도 VPS 업체 제휴 링크가 들어 있습니다. 도구 자체의 동작과는 별개이며, 링크의 수익 구조를 평가하는 문서가 아닙니다.
저장소에 requirements.txt나 pyproject.toml이 없습니다. 파이썬 쪽은 http.server, socket, subprocess, urllib, csv, base64, threading 같은 표준 라이브러리로 구성됩니다. 실제 터널링은 외부 프로그램인 OpenVPN이 맡고, 경로 설정은 ip(iproute2)와 sysctl을 호출합니다.
| 계층 | 사용 기술 | 근거 파일 |
|---|---|---|
| 백엔드 언어 | Python 3 (Docker는 debian:bookworm-slim의 python3, CI는 3.9/3.11/3.13) | Dockerfile, .github/workflows/release.yml |
| VPN 데이터 평면 | OpenVPN 서브프로세스, TUN 장치 | openvpn_command() |
| 프록시 | 직접 구현한 SOCKS5 + HTTP/CONNECT 서버 | proxy_server.py 474줄 |
| 관리 화면 | 파이썬 문자열에 내장된 HTML/CSS/JS, ThreadingHTTPServer | vpngate_manager.py 약 7,350줄 중 상당 부분 |
| 네트워크 제어 | ip rule, ip route 테이블 100, sysctl rp_filter=2 | setup_policy_routing() |
| IP 정보 조회 | ip-api.com 배치, 보조로 ipapi.is | vpn_utils.py |
| 설치·운영 | Bash 1,221줄 (install.sh이 ml 메뉴 스크립트를 내장) | install.sh |
| 컨테이너 | Docker Compose, host 네트워크, NET_ADMIN/NET_RAW만 추가 | compose.yaml |
| CI/CD | GitHub Actions: 릴리스, 15분 주기 미러 갱신 | .github/workflows/*.yml |
| 파일 | 줄 수 | 역할 |
|---|---|---|
vpngate_manager.py | 7,350 | 메인 프로세스, 상태 머신, 웹 UI 전부 |
install.sh | 1,221 | 설치·업데이트·ml 메뉴 |
tests/test_manager_logic.py | 1,131 | 단위 테스트 53개 |
vpn_utils.py | 824 | 지연 측정, IP 분류, 진단 코드 |
proxy_server.py | 474 | SOCKS5/HTTP 프록시 |
snapshot_utils.py | 173 | CSV·설정 파일 검증 |
메인 파일 한 개가 7천 줄을 넘지만 상당 부분은 내장 웹 화면의 HTML·CSS·JS입니다. 서버 로직만 보려면 def 목록(최상위 함수 약 95개)부터 훑고 connect_node, maintain_valid_nodes, auto_switch_node 세 곳에서 시작하세요.
api_network_sources()는 네 개의 주소를 순서대로 돌려줍니다: official_https, official_http, github_pages_https, github_pages_http_redirect_https. 환경 변수 VPNGATE_API_HTTPS_URL 등으로 바꿀 수 있습니다. 성공한 HTTPS 응답만 로컬 캐시에 저장하고, 평문 HTTP로 받은 데이터는 신뢰 캐시를 덮어쓰지 않습니다(test_http_source_does_not_replace_trusted_cache). 마지막 단계로 번들 스냅샷이 캐시를 시드합니다.
VPN Gate의 각 행에는 base64로 인코딩된 .ovpn 파일이 들어 있습니다. OpenVPN 설정은 up, script-security, plugin 같은 지시어로 로컬 프로그램을 실행할 수 있어서 제3자 파일을 그대로 쓰면 위험합니다. snapshot_utils.py는 이를 허용 목록으로 막습니다.
# snapshot_utils.py (요지)
MAX_CONFIG_BYTES = 128 * 1024
SAFE_DIRECTIVES = {"auth","cipher","client","dev","proto","remote",
"remote-cert-tls","tls-client","verb", ...} # 총 40개
SAFE_INLINE_BLOCKS = {"ca","cert","key","tls-auth","tls-crypt"}
for raw_line in config_text.splitlines():
directive = parts[0].lstrip("-").lower()
if directive not in SAFE_DIRECTIVES:
raise ValueError(f"Unsafe OpenVPN directive: {directive}")
# dev 는 tun* 만, remote 는 포트 1~65535, ca/cert/key 블록 필수
목록에 없는 지시어가 한 줄이라도 있으면 그 행은 버려지고(parse_and_validate_snapshot()의 except ... continue), 인라인 블록은 다섯 종류만 허용됩니다. test_snapshot_rejects_executable_openvpn_directive가 실행형 지시어 거부를 검사합니다. 허용 목록 방식이라 OpenVPN에 새 지시어가 생겨도 기본이 “거부”라는 점이 블랙리스트보다 안전합니다.
openvpn_command()는 --pull-filter ignore route-ipv6, --pull-filter ignore ifconfig-ipv6, --remote-cert-tls server, --auth-nocache, 허용 암호 목록(--data-ciphers 또는 구버전용 --ncp-ciphers)을 덧붙입니다. 파일 검증과 실행 인자, 두 겹입니다.
setup_policy_routing()은 ip route add default dev tun0 table 100과 ip rule add oif tun0 table 100을 실행하고 rp_filter를 loose(2)로 낮춥니다. 그리고 proxy_server.create_connection()은 외부로 나가는 소켓마다 SO_BINDTODEVICE로 tun0를 지정합니다. 효과는 이렇습니다.
resolve_dns_over_tun0()가 터널 안에서 8.8.8.8로 직접 보내 로컬 DNS 누수를 줄입니다.SOCKS5와 HTTP는 같은 포트를 씁니다. proxy_client()가 첫 1바이트를 읽어 0x05이면 SOCKS5, 아니면 HTTP로 처리하는 단순한 프로토콜 판별입니다. 동시 연결은 BoundedSemaphore로 기본 256개까지이고, 환경 변수 LOCAL_PROXY_USER·LOCAL_PROXY_PASS를 주면 기본 인증이 켜집니다(secrets.compare_digest 비교).
| 라우팅 모드 | 동작 |
|---|---|
auto | 검사 통과 후보 중 우선순위 최상위로 연결, 끊기면 자동 전환 |
fixed_region | 지정 국가(ISO 2글자) 노드만 허용 |
fixed_ip | 특정 노드 고정 |
favorites | 즐겨찾기 노드 중에서 선택 |
후보 우선순위는 probe_priority_key()가 (핑 오름차순, 점수 내림차순, 속도 내림차순, 접속 세션 수 오름차순) 튜플로 정합니다. 자동 전환은 연속 3번 실패하면 중단하고 백그라운드 보충 스레드를 하나만 띄웁니다(auto_switch_node()). 상태는 “터널 준비”와 “프록시 출구 준비”로 나뉘어, 둘 다 통과해야 화면에 “연결됨”이 뜹니다.
같은 VPN Gate 노드라도 가정용 회선이냐 데이터센터냐에 따라 쓸모가 다릅니다. classify_ip_type()은 ip-api.com의 mobile·hosting·proxy 플래그와 ISP/조직명 패턴으로 주거/모바일/데이터센터/미확인을 나누고, 근거가 약하면 ipapi.is로 2차 확인합니다. 소스 주석처럼 “proxy 플래그는 주거 여부를 바꾸지 않고 품질 정보로만 남깁니다”.
snapshot_utils.py (173줄): 허용 목록 검증. 가장 짧고 가장 배울 것이 많습니다.proxy_server.py (474줄): 소켓 프로그래밍 교재처럼 읽힙니다.vpngate_manager.py의 openvpn_command → run_openvpn_until_ready → connect_node 순서.tests/test_manager_logic.py: 의도한 동작을 문장처럼 적은 테스트 이름들.update-vpngate-mirror.yml은 매시 7·22·37·52분에 실행되어 build_vpngate_mirror.py로 공식 API를 받고, snapshot_utils로 같은 검증을 거친 결과를 GitHub Pages에 올립니다. 클라이언트와 미러가 같은 검증 모듈을 공유하므로 미러가 이상한 설정을 올려도 클라이언트가 한 번 더 걸러냅니다. vpngate.meta.json에는 행 수(검토 시점 99), 바이트 수, sha256이 들어 있지만, 앱은 이 메타를 신선도 표시에 쓰며(read_mirror_freshness()) 해시 검증에 쓰는 코드는 찾지 못했습니다.
남이 만든 설정 파일을 검증하는 최소 구현입니다. 크기 상한, NUL 바이트 거부, 지시어 허용 목록, 블록 짝 맞추기, 필수 요소 확인이 170줄 안에 있습니다. 아이디어: 같은 방식으로 .env나 CI 설정 검증기를 만들어 보세요.
ip rule/ip route table, rp_filter, SO_BINDTODEVICE가 한 곳에서 쓰입니다. 아이디어: 컨테이너 두 개의 출구를 다르게 만들어 curl --interface로 확인해 보세요.
첫 바이트 판별, 인증 협상, 양방향 중계(relay()의 select 루프)를 표준 라이브러리만으로 구현합니다. test_socks5_rejects_client_without_no_auth_method 같은 테스트로 프로토콜 경계 조건도 확인합니다.
[ERR_ROUTE_TABLE_ADD_FAILED] 같은 에러 코드(3003~3007 등)와 diagnose_openvpn_failure()가 로그를 사람이 읽는 원인 문장으로 바꿉니다. 시스템 전체 문제(예: TUN 없음)로 판정되면 후보 검사를 중단하고 낭비를 막는 테스트도 있습니다.
get_session_token()은 sha256(username:password + 고정 솔트)를 돌려줍니다. 로그인할 때마다 새 무작위 값을 만드는 구조가 아닙니다. 한편 active_sessions 딕셔너리로 만료(30일)를 관리하는 부분은 별도로 있습니다.| 항목 | 요구 조건 | 근거 |
|---|---|---|
| OS | Ubuntu, Debian, Alpine, CentOS/RHEL 계열, Fedora, Amazon Linux | README, install.sh 패키지 분기 |
| 권한 | root (또는 NET_ADMIN, NET_RAW) | compose.yaml |
| 장치 | /dev/net/tun | VPS 패널에서 TUN/TAP 활성화 필요 |
| 패키지 | openvpn, iptables, iproute2, psmisc, python3, curl, git | install.sh |
| CPU 아키텍처 | amd64, 386, arm64, armv7 (도커 이미지) | release.yml |
| 네트워크 | TCP 8787(관리), 127.0.0.1:7928(프록시), 외부로 OpenVPN 접속 | README |
| macOS / Windows | 프록시 클라이언트로만 가능, 게이트웨이 본체는 불가 | README |
이 프로그램은 VPN Gate API, GitHub Pages, GitHub Releases API, IP 정보 서비스(ip-api.com은 평문 HTTP 배치 조회)에 접속합니다. 노드 IP가 외부 서비스로 전달되며, check_and_fix_dns()는 이름 해석이 실패하면 /etc/resolv.conf에 공개 DNS(1.1.1.1, 8.8.8.8)를 덧붙입니다. 운영 서버의 DNS 정책이 엄격하다면 먼저 테스트 VPS에서 확인하세요.
또한 ml update와 설치 스크립트는 origin/main으로 강제 리셋(git reset --hard)합니다. 업데이트 전에 변경 내용을 읽는 습관이 필요합니다.
법적 측면: README는 VPS 제공자 약관과 해당 지역 법률이 VPN/TUN 사용을 허용하는지 먼저 확인하라고 요구합니다. VPN Gate 노드 운영자가 접속 기록을 남길 수 있으므로 계정·금융 정보를 이 경로로 보내지 말라는 경고도 README에 있습니다. 코드 검토에서 원격 코드 실행, 실행 파일 내려받기, 데이터 외부 전송 같은 악성 동작은 발견하지 못했습니다(검토한 파일: 파이썬 5개, 설치 스크립트, Dockerfile, 워크플로 2개).
저장소를 받아 읽기 전용으로 스냅샷 파일의 구조를 확인합니다. 아래 스크립트는 네트워크에 접속하지 않고 validate_openvpn_config만 호출합니다. 신뢰할 수 없는 폴더에서 바로 실행하지 말고 별도 작업 폴더에 복사한 코드만 사용하세요.
git clone https://github.com/OpenMili/aimili-vpngate
cd aimili-vpngate
python3 - <<'PY'
import snapshot_utils as s
text = open("mirror/vpngate.csv", encoding="utf-8").read()
rows = s.parse_and_validate_snapshot(text)
print("통과한 노드:", len(rows))
print("국가 분포:", sorted({r["CountryShort"] for r in rows}))
PY
최소 설정 문자열에 script-security 2 한 줄을 넣고 validate_openvpn_config()를 호출해 Unsafe OpenVPN directive 예외가 나는지 확인합니다. 그 다음 tests/test_manager_logic.py의 test_snapshot_rejects_executable_openvpn_directive와 비교해 보세요.
probe_priority_key()의 정렬 키 순서를 바꿔 (속도 우선)으로 만들고, 예시 노드 딕셔너리 5개로 어떤 순서가 되는지 비교합니다. 실제 파일을 고치지 말고 복사본에서 해보세요.
테스트용 VPS 또는 VM에서만 진행합니다. proxy_server.py는 tun0에 묶여 있으므로 일반 환경에서는 외부 연결이 실패하는 것이 정상입니다. 이 실패 메시지(ERR_ROUTE_DEV_NOT_FOUND)가 코드의 어느 분기에서 오는지 추적해 보는 것이 학습 목표입니다.
TUN을 지원하는 일회용 VPS에서만 합니다. 공개 노드는 신뢰할 수 없다는 전제로, 로그인 정보가 필요한 서비스에는 쓰지 않습니다.
docker compose pull && docker compose up -d
docker exec aimilivpn cat /data/ui_auth.json # 계정과 비밀 경로
ssh -N -L 8787:127.0.0.1:8787 -L 7928:127.0.0.1:7928 root@VPS_IP
curl --proxy socks5h://127.0.0.1:7928 https://api.ipify.org
확인할 것: 출력된 IP가 VPS의 원래 IP와 다른가, 웹 관리 화면에서 노드를 “테스트”했을 때 핑 값이 갱신되는가, VPS의 SSH가 끊기지 않는가. 공개 포트를 열지 말고 SSH 터널로만 접속하세요.
| 주차 | 주제 | 목표 산출물 |
|---|---|---|
| 1주 | OpenVPN 설정 구조, .ovpn 지시어 | 위험 지시어 목록 직접 정리 |
| 2주 | 허용 목록 vs 차단 목록, 입력 검증 | 작은 설정 검증기 구현 |
| 3주 | TUN/TAP, 라우팅 테이블, ip rule | 정책 라우팅으로 특정 프로세스만 다른 경로로 |
| 4주 | SOCKS5(RFC 1928), HTTP CONNECT | 100줄짜리 프록시 구현 |
| 5주 | DNS 누수와 터널 내 DNS | DNS 질의 경로 비교 실험 |
| 6주 | 파이썬 스레드·소켓 동시성 | 연결 수 상한 있는 서버 |
| 7주 | GitHub Actions, 멀티 아키텍처 Docker | 크론 기반 데이터 미러 |
| 8주 | 위협 모델링, 공개 노드의 한계 | “신뢰할 수 없는 출구” 위험 문서 |
| 용어 | 뜻 |
|---|---|
| VPN Gate | 쓰쿠바대학 학술 프로젝트가 운영하는 자원봉사자 VPN 서버 목록 |
| OpenVPN | SSL/TLS 기반 VPN 소프트웨어, 이 도구의 실제 터널 엔진 |
| TUN | IP 패킷 단위의 가상 네트워크 장치. tun0 |
| 정책 라우팅 | 출발 인터페이스 등 조건에 따라 다른 라우팅 테이블을 쓰는 방식 |
SO_BINDTODEVICE | 소켓을 특정 네트워크 인터페이스에만 묶는 소켓 옵션 |
| SOCKS5 / CONNECT | 범용 프록시 프로토콜 / HTTP 프록시로 TLS 터널을 여는 메서드 |
| 허용 목록 | 미리 허락한 항목 외에는 기본 거부하는 검증 방식 |
rp_filter | 역방향 경로 필터. loose(2)는 다른 인터페이스로 돌아온 응답도 허용 |
| 미러 | 원본 데이터를 주기적으로 복사해 두는 대체 배포처. 여기선 GitHub Pages |
| 주거/데이터센터 IP | 가정 회선 대역 vs 호스팅 업체 대역. 서비스가 다르게 취급하기도 함 |
검토한 체크아웃의 원격은 OpenMili/aimili-vpngate인데, 코드와 설치 스크립트, README의 기본 주소는 baoweise-bot/aimili-vpngate입니다. 설치기는 후자를 클론합니다. 이름 변경 또는 이관 이력인지 두 계정의 관계는 확인하지 못했습니다. 설치 전에 어느 저장소의 코드를 받는지 직접 확인하세요.