Files
hop-gate/cmd/server/main.go

132 lines
3.8 KiB
Go

package main
import (
"context"
"crypto/tls"
"os"
"github.com/dalbodeule/hop-gate/internal/config"
"github.com/dalbodeule/hop-gate/internal/dtls"
"github.com/dalbodeule/hop-gate/internal/logging"
"github.com/dalbodeule/hop-gate/internal/store"
)
func main() {
logger := logging.NewStdJSONLogger("server")
// 1. 서버 설정 로드 (.env + 환경변수)
cfg, err := config.LoadServerConfigFromEnv()
if err != nil {
logger.Error("failed to load server config from env", logging.Fields{
"error": err.Error(),
})
os.Exit(1)
}
logger.Info("hop-gate server starting", logging.Fields{
"stack": "prometheus-loki-grafana",
"http_listen": cfg.HTTPListen,
"https_listen": cfg.HTTPSListen,
"dtls_listen": cfg.DTLSListen,
"domain": cfg.Domain,
"debug": cfg.Debug,
})
// 2. PostgreSQL 연결 및 스키마 초기화 (ent 기반)
ctx := context.Background()
dbClient, err := store.OpenPostgresFromEnv(ctx, logger)
if err != nil {
logger.Error("failed to init postgres for admin/domain store", logging.Fields{
"error": err.Error(),
})
os.Exit(1)
}
defer dbClient.Close()
logger.Info("postgres connected and schema ready", logging.Fields{
"component": "store",
})
// 3. DTLS 서버 리스너 생성 (pion/dtls 기반)
//
// Debug 모드일 때는 self-signed localhost 인증서를 사용해 테스트 할 수 있도록
// internal/dtls.NewSelfSignedLocalhostConfig() 를 사용합니다.
// 운영 환경에서는 internal/acme.Manager 를 통해 얻은 tls.Config 를
// PionServerConfig.TLSConfig 로 전달해야 합니다.
var tlsCfg *tls.Config
if cfg.Debug {
tlsCfg, err = dtls.NewSelfSignedLocalhostConfig()
if err != nil {
logger.Error("failed to create self-signed localhost cert", logging.Fields{
"error": err.Error(),
})
os.Exit(1)
}
logger.Warn("using self-signed localhost certificate for DTLS (debug mode)", logging.Fields{
"note": "do not use this in production",
})
}
dtlsServer, err := dtls.NewPionServer(dtls.PionServerConfig{
Addr: cfg.DTLSListen,
TLSConfig: tlsCfg, // debug 모드면 self-signed, 아니면 nil(기본값/ACME로 교체 예정)
})
if err != nil {
logger.Error("failed to start dtls server", logging.Fields{
"error": err.Error(),
})
os.Exit(1)
}
defer dtlsServer.Close()
logger.Info("dtls server listening", logging.Fields{
"addr": cfg.DTLSListen,
})
// 3. 도메인 검증기 준비 (현재는 Dummy 구현)
//
// DomainValidator 는 (domain, client_api_key) 조합을 검증합니다.
// 지금은 DummyDomainValidator 로 모두 허용하지만,
// 향후 ent + PostgreSQL 기반 구현으로 교체해야 합니다.
validator := dtls.DummyDomainValidator{
Logger: logger,
}
// 4. DTLS Accept 루프 + Handshake
for {
sess, err := dtlsServer.Accept()
if err != nil {
logger.Error("dtls accept failed", logging.Fields{
"error": err.Error(),
})
continue
}
// 각 세션별로 goroutine 에서 핸드셰이크 및 후속 처리를 수행합니다.
go func(s dtls.Session) {
defer s.Close()
hsRes, err := dtls.PerformServerHandshake(ctx, s, validator, logger)
if err != nil {
// PerformServerHandshake 내부에서 이미 상세 로그를 남기므로 여기서는 요약만 기록합니다.
logger.Warn("dtls handshake failed", logging.Fields{
"session_id": s.ID(),
"error": err.Error(),
})
return
}
// Handshake 성공: 서버 측은 어떤 도메인이 연결되었는지 알 수 있습니다.
logger.Info("dtls handshake completed", logging.Fields{
"session_id": s.ID(),
"domain": hsRes.Domain,
})
// TODO:
// - hsRes.Domain 과 연결된 세션을 proxy 레이어에 등록
// - HTTP 요청을 이 세션을 통해 해당 클라이언트로 라우팅
// - 세션 생명주기/타임아웃 관리 등
}(sess)
}
}