- Introduced `getEnvOrPanic` helper to enforce non-empty required environment variables. - Added strict validation for server (`HOP_SERVER_*`) and client (`HOP_CLIENT_*`) configurations at startup. - Updated `.env` loader to prioritize OS env vars over `.env` file values. - Enhanced structured logging for validated environment variables. - Improved Makefile with `check-env-server` and `check-env-client` targets for build-time validation.
HopGate
Korean / English bilingual README. (ko/en 병기 README입니다.)
1. 프로젝트 개요 (Project Overview)
HopGate는 공인 서버와 여러 프라이빗 네트워크 클라이언트 사이에 DTLS 기반 HTTP 터널을 제공하는 게이트웨이입니다. HopGate is a gateway that provides a DTLS-based HTTP tunnel between a public server and multiple private-network clients.
주요 특징 (Key features):
- 서버는 80/443 포트를 점유하고, ACME(Let's Encrypt 등)로 TLS 인증서를 자동 발급/갱신합니다. The server listens on ports 80/443 and automatically issues/renews TLS certificates via ACME (e.g. Let's Encrypt).
- 서버–클라이언트 간 전송은 DTLS 위에서 이루어지며, 현재는 HTTP 요청/응답을 Protobuf 기반 length-prefixed Envelope 로 터널링합니다. Transport between server and clients uses DTLS; HTTP requests/responses are tunneled as Protobuf-based, length-prefixed envelopes.
- 관리 Plane(REST API)을 통해 도메인 등록/해제 및 클라이언트 API Key 발급을 수행합니다. An admin management plane (REST API) handles domain registration/unregistration and client API key issuance.
- 로그는 JSON 구조 형태로 stdout 에 출력되며, Prometheus + Loki + Grafana 스택에 친화적으로 설계되었습니다. Logs are JSON-structured and designed to work well with a Prometheus + Loki + Grafana stack.
참고: 대용량 HTTP 바디에 대해서는 DTLS/UDP MTU 한계 때문에 단일 Envelope 로는 한계가 있으므로,
progress.md의 3.3A 섹션에 정리된 것처럼StreamOpen/StreamData/StreamClose기반의 스트림/프레임 터널링으로 점진적으로 전환할 예정입니다. (ko) Note: For very large HTTP bodies, a single-envelope model still hits DTLS/UDP MTU limits. As outlined in section 3.3A ofprogress.md, the plan is to gradually move to a stream/frame-based tunneling model usingStreamOpen/StreamData/StreamClose. (en)
아키텍처 세부 내용은 ARCHITECTURE.md에 정리되어 있습니다.
Detailed architecture is documented in ARCHITECTURE.md.
2. 디렉터리 구조 (Directory Layout)
- 서버 엔트리 (Server entrypoint):
cmd/server/main.go - 클라이언트 엔트리 (Client entrypoint):
cmd/client/main.go - 설정 로더 (Config loader):
internal/config/config.go - DTLS 추상/구현 (DTLS abstraction & implementation):
internal/dtls - 관리 Plane (Admin plane HTTP API):
internal/admin - 도메인 스키마 (Domain schema, ent):
ent/schema/domain.go
3. 빌드 및 실행 (Build & Run)
3.1 의존성 (Dependencies)
- Go 1.21+ 권장 (go.mod 상 버전보다 최신 Go 사용을 추천) Go 1.21+ is recommended (even if go.mod specifies an older minor).
- PostgreSQL (관리 Plane + 실제 DomainValidator 에 필수) PostgreSQL (required for the admin plane and the real DomainValidator).
Go 모듈 의존성 설치 / 정리는 다음으로 수행할 수 있습니다:
You can install/cleanup Go module deps via:
go mod tidy
3.2 Makefile 사용 (Using Makefile)
서버/클라이언트 빌드를 위해 상위 Makefile을 제공합니다.
A top-level Makefile is provided for server/client builds.
# 서버/클라이언트 모두 빌드
make all
# 서버만 빌드
make server
# 클라이언트만 빌드
make client
빌드 결과는 ./bin/hop-gate-server, ./bin/hop-gate-client 로 생성됩니다.
Build artifacts are created as ./bin/hop-gate-server and ./bin/hop-gate-client.
3.3 환경변수와 .env 처리 (Environment variables and .env handling)
HopGate 는 공통 설정을 internal/config/config.go 에서 로드하며,
운영체제 환경변수(OS env)가 .env 파일보다 우선하도록 설계되어 있습니다.
HopGate loads shared configuration from internal/config/config.go and is designed so that OS-level environment variables take precedence over .env.
-
.env로더:loadDotEnvOnce- 현재 작업 디렉터리의
.env파일을 한 번만 읽습니다. - 이미 OS 환경변수에 설정된 키는 덮어쓰지 않고 그대로 유지하고, 비어 있는 키에 대해서만
.env값을 주입합니다. .env파일이 존재하지 않으면 조용히 무시합니다 (에러가 아닙니다). The loader reads the.envfile once, does not override existing OS env values, and only fills missing keys. If.envis missing, it is silently ignored.
- 현재 작업 디렉터리의
-
서버 설정 로더 (Server config loader):
LoadServerConfigFromEnv.env로더를 먼저 호출한 뒤,HOP_SERVER_*환경변수에서 서버 설정을 구성합니다.- 실제 실행 시점에는 서버 엔트리포인트
cmd/server/main.go에서 필수 환경변수가 모두 설정되었는지 한 번 더 검증합니다. It calls the.envloader first, then builds server config fromHOP_SERVER_*env vars, and finally the server entrypointcmd/server/main.govalidates required variables.
-
클라이언트 설정 로더 (Client config loader):
LoadClientConfigFromEnv.env로더를 동일하게 사용하며,HOP_CLIENT_*환경변수에서 클라이언트 설정을 구성합니다.- 이후 CLI 인자(예:
--server-addr,--domain)가 있을 경우 env 값보다 우선 적용됩니다. The same loader is used forHOP_CLIENT_*env vars, and CLI flags override env values when provided.
빌드/실행 시 필수 환경변수는 다음 두 단계에서 검증됩니다. Required environment variables are validated in two stages:
-
빌드 단계 (Build-time) – Makefile 체크 (optional guard)
-
실행 단계 (Runtime) – 엔트리포인트에서 엄격 검증 (strict runtime validation)
- 서버:
cmd/server/main.go- 헬퍼
getEnvOrPanic(logger, key)를 사용해HOP_SERVER_HTTP_LISTEN,HOP_SERVER_HTTPS_LISTEN,HOP_SERVER_DTLS_LISTEN,HOP_SERVER_DOMAIN,HOP_SERVER_DEBUG가 비어 있지 않은지 확인합니다. - 누락되었거나 공백인 경우, 구조화 에러 로그(JSON)와 함께 프로세스를 종료합니다.
- 헬퍼
- 클라이언트:
cmd/client/main.goHOP_CLIENT_SERVER_ADDR,HOP_CLIENT_DOMAIN,HOP_CLIENT_API_KEY,HOP_CLIENT_LOCAL_TARGET,HOP_CLIENT_DEBUG를 동일한 방식으로 검증합니다.
- 두 경우 모두
HOP_*_DEBUG값은 문자열"true"또는"false"만 허용합니다. Both server and client use a helper (getEnvOrPanic) to enforce non-empty required env vars at startup and log structured JSON errors on failure. The debug flags must be the strings"true"or"false".
- 서버:
실제 배포 환경에서는 .env 보다는 시스템 환경변수(Kubernetes env, Docker -e, systemd Environment= 등)를 사용하는 것을 권장하며,
로컬 개발에서는 .env.example 을 복사한 .env 파일을 사용해 빠르게 설정을 구성할 수 있습니다.
For production deployments, prefer OS-level env (Kubernetes env, Docker -e, systemd Environment=, etc.), and use a local .env (copied from .env.example) mainly for development.
4. DTLS 핸드셰이크 테스트 (Testing DTLS Handshake)
HopGate는 DTLS 위에서 도메인 + 클라이언트 API Key 기반의 애플리케이션 레벨 핸드셰이크를 수행합니다.
HopGate performs an application-level handshake over DTLS using domain + client API key.
4.1 서버 설정 예시 (Server .env example)
.env:
HOP_SERVER_DTLS_LISTEN=:8443
HOP_SERVER_DEBUG=true
HOP_SERVER_DTLS_LISTEN
DTLS 서버가 바인딩할 UDP 포트입니다. 예::8443
UDP port for the DTLS server to bind on, e.g.:8443.HOP_SERVER_DEBUG=true
디버그 모드에서는dtls.NewSelfSignedLocalhostConfig()를 사용해 self-signed localhost 인증서를 생성합니다.
In debug mode the server usesdtls.NewSelfSignedLocalhostConfig()to generate a self-signed localhost certificate.
4.2 클라이언트 설정 예시 (Client .env example)
.env:
HOP_CLIENT_SERVER_ADDR=localhost:8443
HOP_CLIENT_DOMAIN=test.example.com
HOP_CLIENT_API_KEY=TEST_LOCALHOST_API_KEY_0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ
HOP_CLIENT_LOCAL_TARGET=127.0.0.1:8080
HOP_CLIENT_DEBUG=true
HOP_CLIENT_SERVER_ADDR: DTLS 서버 주소 (예:localhost:8443) DTLS server address, e.g.localhost:8443.HOP_CLIENT_DOMAIN/HOP_CLIENT_API_KEY: 관리 Plane 에서 발급받은 도메인/키 (실제 ent + PostgreSQL 기반 DomainValidator 에 의해 검증) Domain and API key issued by the admin plane (validated by a real ent + PostgreSQL based DomainValidator).HOP_CLIENT_LOCAL_TARGET: 실제로 HTTP 요청을 보낼 로컬 서버 주소 Local HTTP target address.HOP_CLIENT_DEBUG=true: 서버 인증서 체인 검증을 스킵(InsecureSkipVerify)하여 self-signed 인증서를 신뢰
Skips server certificate chain verification (InsecureSkipVerify) and trusts the self-signed cert.
4.3 서버/클라이언트 실행 (Run server/client)
# 서버 실행 (Server)
./bin/hop-gate-server
# 클라이언트 실행 (Client)
./bin/hop-gate-client
성공 시 로그에는 다음과 같은 정보가 찍힙니다.
On success, logs will include information like:
- 서버: 세션 ID, 연결된 도메인
Server: session ID and connected domain. - 클라이언트: 핸드셰이크 성공 메시지, 도메인, local_target
Client: handshake success message, domain, and local_target.
로그 출력 형식은 구조적 JSON 이며, Loki/Grafana 에서 쉽게 수집/조회할 수 있습니다.
Logs are JSON-structured and easy to ingest/query with Loki/Grafana.
5. 관리 Plane 요약 (Admin Plane Summary)
관리 Plane 은 https://{server-hostname}/api/v1/admin 하위 경로로 동작합니다.
The admin plane is served under https://{server-hostname}/api/v1/admin.
-
인증 (Authentication)
- 헤더
Authorization: Bearer {ADMIN_API_KEY}사용
UsesAuthorization: Bearer {ADMIN_API_KEY}header.
- 헤더
-
도메인 등록 (Domain register)
POST /api/v1/admin/domains/register- 요청(JSON):
{"domain":"example.com","memo":"text"} - 응답(JSON): 성공 시
{"success":true,"client_api_key":"..."}
-
도메인 해제 (Domain unregister)
POST /api/v1/admin/domains/unregister- 요청(JSON):
{"domain":"example.com","client_api_key":"..."} - 응답(JSON):
{"success":true}또는 에러 메시지
자세한 구현 뼈대는 internal/admin 및 ent/schema/domain.go 를 참고하세요.
For implementation skeleton, see internal/admin and ent/schema/domain.go.
6. 주의사항 (Caveats)
Debug=true설정은 개발/테스트 용도입니다. self-signed 인증서 및 InsecureSkipVerify 사용은 프로덕션 환경에서 절대 사용하지 마세요.Debug=trueis strictly for development/testing. Do not use self-signed certs or InsecureSkipVerify in production.- 현재 버전은 ACME 기반 인증서, PostgreSQL + ent 기반 DomainValidator, Proxy 레이어가 기본적으로 연동되어 있으나,
대용량 HTTP 바디에 대해서는 JSON 단일 메시지 기반 터널링 특성상 DTLS/UDP MTU 한계에 부딪힐 수 있습니다.
스트림/프레임 기반 DTLS 터널링으로의 전환 및 하드닝 작업은
progress.md에 정의된 다음 단계에 포함되어 있습니다. (ko) The current version wires ACME certificates, a PostgreSQL+ent-based DomainValidator, and the proxy layer by default, but for very large HTTP bodies the JSON single-message tunneling model can still hit DTLS/UDP MTU limits. Moving to a stream/frame-based DTLS tunneling model and further hardening are tracked as next steps inprogress.md. (en)
HopGate는 아직 초기 단계의 실험적 프로젝트입니다. API 및 동작은 언제든지 변경될 수 있습니다. HopGate is still experimental; APIs and behavior may change at any time.