From 69c823adbe08ee1eeca5367f5fb845e661529fb0 Mon Sep 17 00:00:00 2001 From: Grant Linville Date: Wed, 4 Dec 2024 15:13:33 -0500 Subject: [PATCH 1/4] enhance: add functions for daemon servers for mTLS Signed-off-by: Grant Linville --- pkg/daemon/daemon.go | 80 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 pkg/daemon/daemon.go diff --git a/pkg/daemon/daemon.go b/pkg/daemon/daemon.go new file mode 100644 index 0000000..b46d5c7 --- /dev/null +++ b/pkg/daemon/daemon.go @@ -0,0 +1,80 @@ +package daemon + +import ( + "crypto/tls" + "crypto/x509" + "encoding/base64" + "fmt" + "net/http" + "os" +) + +// CreateServer creates a new HTTP server with TLS configured for GPTScript. +// This function should be used when creating a new server for a daemon tool. +// The server should then be started with the StartServer function. +func CreateServer() (*http.Server, error) { + tlsConfig, err := getTLSConfig() + if err != nil { + return nil, fmt.Errorf("failed to get TLS config: %v\n", err) + } + + server := &http.Server{ + Addr: fmt.Sprintf("127.0.0.1:%s", os.Getenv("PORT")), + TLSConfig: tlsConfig, + } + return server, nil +} + +// StartServer starts an HTTP server created by the CreateServer function. +// This is for use with daemon tools. +func StartServer(server *http.Server) error { + if err := server.ListenAndServeTLS("", ""); err != nil { + return fmt.Errorf("stopped serving: %v\n", err) + } + return nil +} + +func getTLSConfig() (*tls.Config, error) { + certB64 := os.Getenv("CERT") + privateKeyB64 := os.Getenv("PRIVATE_KEY") + gptscriptCertB64 := os.Getenv("GPTSCRIPT_CERT") + + if certB64 == "" { + return nil, fmt.Errorf("CERT not set") + } else if privateKeyB64 == "" { + return nil, fmt.Errorf("PRIVATE_KEY not set") + } else if gptscriptCertB64 == "" { + return nil, fmt.Errorf("GPTSCRIPT_CERT not set") + } + + certBytes, err := base64.StdEncoding.DecodeString(certB64) + if err != nil { + return nil, fmt.Errorf("failed to decode cert base64: %v\n", err) + } + + privateKeyBytes, err := base64.StdEncoding.DecodeString(privateKeyB64) + if err != nil { + return nil, fmt.Errorf("failed to decode private key base64: %v\n", err) + } + + gptscriptCertBytes, err := base64.StdEncoding.DecodeString(gptscriptCertB64) + if err != nil { + return nil, fmt.Errorf("failed to decode gptscript cert base64: %v\n", err) + } + + cert, err := tls.X509KeyPair(certBytes, privateKeyBytes) + if err != nil { + return nil, fmt.Errorf("failed to create X509 key pair: %v\n", err) + } + + pool := x509.NewCertPool() + if !pool.AppendCertsFromPEM(gptscriptCertBytes) { + return nil, fmt.Errorf("failed to append gptscript cert to pool") + } + + return &tls.Config{ + Certificates: []tls.Certificate{cert}, + ClientCAs: pool, + ClientAuth: tls.RequireAndVerifyClientCert, + }, nil +} From a75da4296a29c2b9f2bdadbd4284ce9fbe47c392 Mon Sep 17 00:00:00 2001 From: Grant Linville Date: Wed, 4 Dec 2024 15:18:36 -0500 Subject: [PATCH 2/4] fix lint issues Signed-off-by: Grant Linville --- pkg/daemon/daemon.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/daemon/daemon.go b/pkg/daemon/daemon.go index b46d5c7..4dab317 100644 --- a/pkg/daemon/daemon.go +++ b/pkg/daemon/daemon.go @@ -15,7 +15,7 @@ import ( func CreateServer() (*http.Server, error) { tlsConfig, err := getTLSConfig() if err != nil { - return nil, fmt.Errorf("failed to get TLS config: %v\n", err) + return nil, fmt.Errorf("failed to get TLS config: %v", err) } server := &http.Server{ @@ -29,7 +29,7 @@ func CreateServer() (*http.Server, error) { // This is for use with daemon tools. func StartServer(server *http.Server) error { if err := server.ListenAndServeTLS("", ""); err != nil { - return fmt.Errorf("stopped serving: %v\n", err) + return fmt.Errorf("stopped serving: %v", err) } return nil } @@ -49,22 +49,22 @@ func getTLSConfig() (*tls.Config, error) { certBytes, err := base64.StdEncoding.DecodeString(certB64) if err != nil { - return nil, fmt.Errorf("failed to decode cert base64: %v\n", err) + return nil, fmt.Errorf("failed to decode cert base64: %v", err) } privateKeyBytes, err := base64.StdEncoding.DecodeString(privateKeyB64) if err != nil { - return nil, fmt.Errorf("failed to decode private key base64: %v\n", err) + return nil, fmt.Errorf("failed to decode private key base64: %v", err) } gptscriptCertBytes, err := base64.StdEncoding.DecodeString(gptscriptCertB64) if err != nil { - return nil, fmt.Errorf("failed to decode gptscript cert base64: %v\n", err) + return nil, fmt.Errorf("failed to decode gptscript cert base64: %v", err) } cert, err := tls.X509KeyPair(certBytes, privateKeyBytes) if err != nil { - return nil, fmt.Errorf("failed to create X509 key pair: %v\n", err) + return nil, fmt.Errorf("failed to create X509 key pair: %v", err) } pool := x509.NewCertPool() From d3c7d384d37d6dcedc4efcc8b9481b2dbdb4131e Mon Sep 17 00:00:00 2001 From: Grant Linville Date: Thu, 5 Dec 2024 22:36:53 -0500 Subject: [PATCH 3/4] add CreateServerWithMux function Signed-off-by: Grant Linville --- pkg/daemon/daemon.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pkg/daemon/daemon.go b/pkg/daemon/daemon.go index 4dab317..488791a 100644 --- a/pkg/daemon/daemon.go +++ b/pkg/daemon/daemon.go @@ -25,6 +25,23 @@ func CreateServer() (*http.Server, error) { return server, nil } +// CreateServerWithMux creates a new HTTP server with TLS configured for GPTScript. +// This function should be used when creating a new server for a daemon tool with a custom ServeMux. +// The server should then be started with the StartServer function. +func CreateServerWithMux(mux *http.ServeMux) (*http.Server, error) { + tlsConfig, err := getTLSConfig() + if err != nil { + return nil, fmt.Errorf("failed to get TLS config: %v", err) + } + + server := &http.Server{ + Addr: fmt.Sprintf("127.0.0.1:%s", os.Getenv("PORT")), + TLSConfig: tlsConfig, + Handler: mux, + } + return server, nil +} + // StartServer starts an HTTP server created by the CreateServer function. // This is for use with daemon tools. func StartServer(server *http.Server) error { From 37f76eaf70113e4ed26ae4e4aea665bb4650c03b Mon Sep 17 00:00:00 2001 From: Grant Linville Date: Mon, 16 Dec 2024 14:47:47 -0500 Subject: [PATCH 4/4] PR feedback Signed-off-by: Grant Linville --- pkg/daemon/daemon.go | 45 ++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/pkg/daemon/daemon.go b/pkg/daemon/daemon.go index 488791a..b25a49a 100644 --- a/pkg/daemon/daemon.go +++ b/pkg/daemon/daemon.go @@ -4,53 +4,58 @@ import ( "crypto/tls" "crypto/x509" "encoding/base64" + "errors" "fmt" "net/http" "os" ) +type Server struct { + mux *http.ServeMux + tlsConfig *tls.Config +} + // CreateServer creates a new HTTP server with TLS configured for GPTScript. // This function should be used when creating a new server for a daemon tool. // The server should then be started with the StartServer function. -func CreateServer() (*http.Server, error) { - tlsConfig, err := getTLSConfig() - if err != nil { - return nil, fmt.Errorf("failed to get TLS config: %v", err) - } - - server := &http.Server{ - Addr: fmt.Sprintf("127.0.0.1:%s", os.Getenv("PORT")), - TLSConfig: tlsConfig, - } - return server, nil +func CreateServer() (*Server, error) { + return CreateServerWithMux(http.DefaultServeMux) } // CreateServerWithMux creates a new HTTP server with TLS configured for GPTScript. // This function should be used when creating a new server for a daemon tool with a custom ServeMux. // The server should then be started with the StartServer function. -func CreateServerWithMux(mux *http.ServeMux) (*http.Server, error) { +func CreateServerWithMux(mux *http.ServeMux) (*Server, error) { tlsConfig, err := getTLSConfig() if err != nil { return nil, fmt.Errorf("failed to get TLS config: %v", err) } + return &Server{ + mux: mux, + tlsConfig: tlsConfig, + }, nil +} + +// Start starts an HTTP server created by the CreateServer function. +// This is for use with daemon tools. +func (s *Server) Start() error { server := &http.Server{ Addr: fmt.Sprintf("127.0.0.1:%s", os.Getenv("PORT")), - TLSConfig: tlsConfig, - Handler: mux, + TLSConfig: s.tlsConfig, + Handler: s.mux, } - return server, nil -} -// StartServer starts an HTTP server created by the CreateServer function. -// This is for use with daemon tools. -func StartServer(server *http.Server) error { - if err := server.ListenAndServeTLS("", ""); err != nil { + if err := server.ListenAndServeTLS("", ""); err != nil && !errors.Is(err, http.ErrServerClosed) { return fmt.Errorf("stopped serving: %v", err) } return nil } +func (s *Server) HandleFunc(pattern string, handler http.HandlerFunc) { + s.mux.HandleFunc(pattern, handler) +} + func getTLSConfig() (*tls.Config, error) { certB64 := os.Getenv("CERT") privateKeyB64 := os.Getenv("PRIVATE_KEY")