@@ -2,6 +2,7 @@ package mcpproxy
22
33import (
44 "context"
5+ "crypto/tls"
56 "errors"
67 "fmt"
78 "net/http"
@@ -23,6 +24,7 @@ import (
2324 "github.com/sigbit/mcp-auth-proxy/pkg/idp"
2425 "github.com/sigbit/mcp-auth-proxy/pkg/proxy"
2526 "github.com/sigbit/mcp-auth-proxy/pkg/repository"
27+ "github.com/sigbit/mcp-auth-proxy/pkg/tlsreload"
2628 "github.com/sigbit/mcp-auth-proxy/pkg/utils"
2729 "go.uber.org/zap"
2830 "golang.org/x/crypto/acme"
@@ -39,6 +41,8 @@ func Run(
3941 tlsHost string ,
4042 tlsDirectoryURL string ,
4143 tlsAcceptTOS bool ,
44+ tlsCertFile string ,
45+ tlsKeyFile string ,
4246 dataPath string ,
4347 repositoryBackend string ,
4448 repositoryDSN string ,
@@ -78,6 +82,20 @@ func Run(
7882 return fmt .Errorf ("external URL must not have a path, got: %s" , parsedExternalURL .Path )
7983 }
8084
85+ if (tlsCertFile == "" ) != (tlsKeyFile == "" ) {
86+ return fmt .Errorf ("both TLS certificate and key files must be provided together" )
87+ }
88+ var manualTLS bool
89+ if tlsCertFile != "" && tlsKeyFile != "" {
90+ manualTLS = true
91+ }
92+ if manualTLS && tlsHost != "" {
93+ return fmt .Errorf ("tlsHost cannot be used when TLS certificate and key files are provided" )
94+ }
95+ if ! manualTLS && ! autoTLS && tlsHost != "" {
96+ return fmt .Errorf ("tlsHost requires automatic TLS; remove noAutoTLS or provide certificate files instead" )
97+ }
98+
8199 secret , err := utils .LoadOrGenerateSecret (path .Join (dataPath , "secret" ))
82100 if err != nil {
83101 return fmt .Errorf ("failed to load or generate secret: %w" , err )
@@ -271,7 +289,7 @@ func Run(
271289 proxyRouter .SetupRoutes (router )
272290
273291 var tlsHostDetected bool
274- if autoTLS &&
292+ if autoTLS && ! manualTLS &&
275293 tlsHost == "" &&
276294 parsedExternalURL .Scheme == "https" &&
277295 parsedExternalURL .Host != "localhost" {
@@ -284,13 +302,75 @@ func Run(
284302 errs := []error {}
285303 lock := sync.Mutex {}
286304
287- if tlsHost != "" {
305+ if manualTLS {
306+ certReloader , err := tlsreload .NewFileReloader (tlsCertFile , tlsKeyFile , logger )
307+ if err != nil {
308+ return fmt .Errorf ("failed to prepare TLS certificate reloader: %w" , err )
309+ }
310+
311+ logger .Info ("Starting server with provided TLS certificate" )
312+ httpServer := & http.Server {
313+ Addr : listen ,
314+ Handler : http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
315+ host := r .Host
316+ if host == "" {
317+ host = r .URL .Host
318+ }
319+ target := "https://" + host + r .RequestURI
320+ http .Redirect (w , r , target , http .StatusMovedPermanently )
321+ }),
322+ }
323+ httpsServer := & http.Server {
324+ Addr : tlsListen ,
325+ Handler : router ,
326+ TLSConfig : & tls.Config {GetCertificate : certReloader .GetCertificate },
327+ }
328+ wg .Add (1 )
329+ go func () {
330+ defer wg .Done ()
331+ err := httpServer .ListenAndServe ()
332+ if err != nil && ! errors .Is (err , http .ErrServerClosed ) {
333+ lock .Lock ()
334+ errs = append (errs , err )
335+ lock .Unlock ()
336+ }
337+ logger .Debug ("HTTP server closed" )
338+ exit <- struct {}{}
339+ }()
340+ go func () {
341+ <- ctx .Done ()
342+ shutdownCtx , shutdownCancel := context .WithTimeout (context .Background (), ServerShutdownTimeout )
343+ defer shutdownCancel ()
344+ if shutdownErr := httpServer .Shutdown (shutdownCtx ); shutdownErr != nil {
345+ logger .Warn ("HTTP server shutdown error" , zap .Error (shutdownErr ))
346+ }
347+ }()
348+ wg .Add (1 )
349+ go func () {
350+ defer wg .Done ()
351+ err := httpsServer .ListenAndServeTLS ("" , "" )
352+ if err != nil && ! errors .Is (err , http .ErrServerClosed ) {
353+ lock .Lock ()
354+ errs = append (errs , err )
355+ lock .Unlock ()
356+ }
357+ logger .Debug ("HTTPS server closed" )
358+ exit <- struct {}{}
359+ }()
360+ go func () {
361+ <- ctx .Done ()
362+ shutdownCtx , shutdownCancel := context .WithTimeout (context .Background (), ServerShutdownTimeout )
363+ defer shutdownCancel ()
364+ if shutdownErr := httpsServer .Shutdown (shutdownCtx ); shutdownErr != nil {
365+ logger .Warn ("HTTPS server shutdown error" , zap .Error (shutdownErr ))
366+ }
367+ }()
368+ } else if tlsHost != "" {
288369 if ! tlsAcceptTOS {
289370 if tlsHostDetected {
290371 return errors .New ("TLS host is auto-detected, but tlsAcceptTOS is not set to true. Please agree to the TOS or set noAutoTLS to true" )
291- } else {
292- return errors .New ("TLS is enabled, but tlsAcceptTOS is not set to true. Please explicitly agree to the TOS" )
293372 }
373+ return errors .New ("TLS is enabled, but tlsAcceptTOS is not set to true. Please explicitly agree to the TOS" )
294374 }
295375
296376 m := autocert.Manager {
@@ -401,7 +481,7 @@ func Run(
401481 }()
402482 }
403483
404- if tlsHost != "" {
484+ if manualTLS || tlsHost != "" {
405485 logger .Info ("Starting server" , zap .Strings ("listen" , []string {listen , tlsListen }))
406486 } else {
407487 logger .Info ("Starting server" , zap .Strings ("listen" , []string {listen }))
0 commit comments