mirror of
https://github.com/dalbodeule/hop-gate.git
synced 2025-12-07 20:35:44 +09:00
132 lines
3.8 KiB
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)
|
|
}
|
|
}
|