mirror of
https://github.com/dalbodeule/hop-gate.git
synced 2025-12-09 21:35:43 +09:00
[feat](server, client, build): integrate dotenv for environment variable management (by @ryu31847)
- Added `github.com/joho/godotenv` for loading `.env` files in server and client. - Implemented environment variable validation and logging in both main programs. - Updated Makefile with `.env` export and validation steps for required variables. - Simplified error handling in `writeErrorPage` rendering logic.
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"flag"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
@@ -13,8 +14,25 @@ import (
|
||||
"github.com/dalbodeule/hop-gate/internal/dtls"
|
||||
"github.com/dalbodeule/hop-gate/internal/logging"
|
||||
"github.com/dalbodeule/hop-gate/internal/proxy"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// .env 파일 로드
|
||||
if err := godotenv.Overload(); err != nil {
|
||||
log.Fatalf(".env 파일을 로드할 수 없습니다: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func getEnvOrPanic(key string) string {
|
||||
value, exists := os.LookupEnv(key)
|
||||
if !exists || value == "" {
|
||||
log.Fatalf("필수 환경 변수 %s가 설정되지 않았습니다.", key)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// maskAPIKey 는 로그에 노출할 때 클라이언트 API Key 를 일부만 보여주기 위한 헬퍼입니다.
|
||||
func maskAPIKey(key string) string {
|
||||
if len(key) <= 8 {
|
||||
@@ -36,6 +54,30 @@ func firstNonEmpty(values ...string) string {
|
||||
func main() {
|
||||
logger := logging.NewStdJSONLogger("client")
|
||||
|
||||
// .env 파일 로드
|
||||
if err := godotenv.Load(); err != nil {
|
||||
log.Println("Failed to load .env file. Using default environment variables.")
|
||||
}
|
||||
|
||||
// 필수 환경 변수 유효성 검사
|
||||
serverAddr := getEnvOrPanic("HOP_CLIENT_SERVER_ADDR")
|
||||
clientDomain := getEnvOrPanic("HOP_CLIENT_DOMAIN")
|
||||
apiKey := getEnvOrPanic("HOP_CLIENT_API_KEY")
|
||||
localTarget := getEnvOrPanic("HOP_CLIENT_LOCAL_TARGET")
|
||||
debug := getEnvOrPanic("HOP_CLIENT_DEBUG")
|
||||
|
||||
// 디버깅 플래그 확인
|
||||
if debug != "true" && debug != "false" {
|
||||
log.Fatalf("Invalid value for HOP_CLIENT_DEBUG. It must be set to 'true' or 'false'.")
|
||||
}
|
||||
|
||||
// 유효성 검사 결과 출력
|
||||
log.Printf("SERVER_ADDR: %s", serverAddr)
|
||||
log.Printf("CLIENT_DOMAIN: %s", clientDomain)
|
||||
log.Printf("API_KEY: %s", maskAPIKey(apiKey))
|
||||
log.Printf("LOCAL_TARGET: %s", localTarget)
|
||||
log.Printf("DEBUG: %s", debug)
|
||||
|
||||
// CLI 인자 정의 (env 보다 우선 적용됨)
|
||||
serverAddrFlag := flag.String("server-addr", "", "DTLS server address (host:port)")
|
||||
domainFlag := flag.String("domain", "", "registered domain (e.g. api.example.com)")
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
stdfs "io/fs"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -28,6 +29,8 @@ import (
|
||||
"github.com/dalbodeule/hop-gate/internal/observability"
|
||||
"github.com/dalbodeule/hop-gate/internal/protocol"
|
||||
"github.com/dalbodeule/hop-gate/internal/store"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
type dtlsSessionWrapper struct {
|
||||
@@ -35,6 +38,21 @@ type dtlsSessionWrapper struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func init() {
|
||||
// .env 파일 로드
|
||||
if err := godotenv.Overload(); err != nil {
|
||||
log.Fatalf(".env 파일을 로드할 수 없습니다: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func getEnvOrPanic(key string) string {
|
||||
value, exists := os.LookupEnv(key)
|
||||
if !exists || value == "" {
|
||||
log.Fatalf("필수 환경 변수 %s가 설정되지 않았습니다.", key)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// canonicalizeDomainForDNS 는 DTLS 핸드셰이크에서 전달된 도메인 문자열을
|
||||
// DNS 조회 및 DB 조회에 사용할 수 있는 정규화된 호스트명으로 변환합니다. (ko)
|
||||
// canonicalizeDomainForDNS normalizes the domain string from the DTLS handshake
|
||||
@@ -277,8 +295,8 @@ var hopGateOwnedHeaders = map[string]struct{}{
|
||||
"Referrer-Policy": {},
|
||||
}
|
||||
|
||||
// writeErrorPage 는 주요 HTTP 에러 코드(400/404/500/502/504/525)에 대해 정적 HTML 에러 페이지를 렌더링합니다. (ko)
|
||||
// writeErrorPage renders static HTML error pages for key HTTP error codes (400/404/500/502/504/525). (en)
|
||||
// writeErrorPage 는 주요 HTTP 에러 코드(400/404/500/525)에 대해 정적 HTML 에러 페이지를 렌더링합니다. (ko)
|
||||
// writeErrorPage renders static HTML error pages for key HTTP error codes (400/404/500/525). (en)
|
||||
//
|
||||
// 템플릿 로딩 우선순위: (ko)
|
||||
// 1. HOP_ERROR_PAGES_DIR/<status>.html (또는 ./errors/<status>.html) (ko)
|
||||
@@ -294,31 +312,9 @@ func writeErrorPage(w http.ResponseWriter, r *http.Request, status int) {
|
||||
setSecurityAndIdentityHeaders(w, r)
|
||||
}
|
||||
|
||||
// 4xx / 5xx 대역에 대한 템플릿 매핑 규칙: (ko)
|
||||
// - 400 series: 400.html 로 렌더링 (단, 404 는 404.html 사용) (ko)
|
||||
// - 500 series: 500.html 로 렌더링 (단, 502/504/525 는 개별 템플릿 사용) (ko)
|
||||
//
|
||||
// Mapping rules for 4xx / 5xx ranges: (en)
|
||||
// - 400 series: render using 400.html (except 404 uses 404.html). (en)
|
||||
// - 500 series: render using 500.html (except 502/504/525 which have dedicated templates). (en)
|
||||
mapped := status
|
||||
switch {
|
||||
case status >= 400 && status < 500:
|
||||
if status != http.StatusBadRequest && status != http.StatusNotFound {
|
||||
mapped = http.StatusBadRequest
|
||||
}
|
||||
case status >= 500 && status < 600:
|
||||
if status != http.StatusInternalServerError &&
|
||||
status != http.StatusBadGateway &&
|
||||
status != errorpages.StatusGatewayTimeout &&
|
||||
status != errorpages.StatusTLSHandshakeFailed {
|
||||
mapped = http.StatusInternalServerError
|
||||
}
|
||||
}
|
||||
|
||||
// Delegates actual HTML rendering to internal/errorpages with mapped status. (en)
|
||||
// 실제 HTML 렌더링은 매핑된 상태 코드로 internal/errorpages 패키지에 위임합니다. (ko)
|
||||
errorpages.Render(w, r, mapped)
|
||||
// Delegates actual HTML rendering to internal/errorpages. (en)
|
||||
// 실제 HTML 렌더링은 internal/errorpages 패키지에 위임합니다. (ko)
|
||||
errorpages.Render(w, r, status)
|
||||
}
|
||||
|
||||
// setSecurityAndIdentityHeaders 는 HopGate 에서 공통으로 추가하는 보안/식별 헤더를 설정합니다. (ko)
|
||||
@@ -652,6 +648,30 @@ func newHTTPHandler(logger logging.Logger, proxyTimeout time.Duration) http.Hand
|
||||
func main() {
|
||||
logger := logging.NewStdJSONLogger("server")
|
||||
|
||||
// .env 파일 로드
|
||||
if err := godotenv.Load(); err != nil {
|
||||
log.Println(".env 파일을 로드할 수 없습니다. 기본 환경 변수를 사용합니다.")
|
||||
}
|
||||
|
||||
// 필수 환경 변수 유효성 검사
|
||||
httpListen := getEnvOrPanic("HOP_SERVER_HTTP_LISTEN")
|
||||
httpsListen := getEnvOrPanic("HOP_SERVER_HTTPS_LISTEN")
|
||||
dtlsListen := getEnvOrPanic("HOP_SERVER_DTLS_LISTEN")
|
||||
domain := getEnvOrPanic("HOP_SERVER_DOMAIN")
|
||||
debug := getEnvOrPanic("HOP_SERVER_DEBUG")
|
||||
|
||||
// 디버깅 플래그 확인
|
||||
if debug != "true" && debug != "false" {
|
||||
log.Fatalf("HOP_SERVER_DEBUG 값이 잘못되었습니다. true 또는 false로 설정해야 합니다.")
|
||||
}
|
||||
|
||||
// 유효성 검사 결과 출력
|
||||
log.Printf("HTTP_LISTEN: %s", httpListen)
|
||||
log.Printf("HTTPS_LISTEN: %s", httpsListen)
|
||||
log.Printf("DTLS_LISTEN: %s", dtlsListen)
|
||||
log.Printf("DOMAIN: %s", domain)
|
||||
log.Printf("DEBUG: %s", debug)
|
||||
|
||||
// Prometheus 메트릭 등록
|
||||
observability.MustRegister()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user