@@ -2,6 +2,9 @@ package engine
22
33import (
44 "context"
5+ "crypto/tls"
6+ "crypto/x509"
7+ "encoding/base64"
58 "fmt"
69 "io"
710 "math/rand"
@@ -11,11 +14,13 @@ import (
1114 "sync"
1215 "time"
1316
17+ "github.com/gptscript-ai/gptscript/pkg/certs"
1418 "github.com/gptscript-ai/gptscript/pkg/system"
1519 "github.com/gptscript-ai/gptscript/pkg/types"
1620)
1721
1822var ports Ports
23+ var certificates Certs
1924
2025type Ports struct {
2126 daemonPorts map [string ]int64
@@ -29,6 +34,11 @@ type Ports struct {
2934 daemonWG sync.WaitGroup
3035}
3136
37+ type Certs struct {
38+ daemonCerts map [string ]certs.CertAndKey
39+ daemonLock sync.Mutex
40+ }
41+
3242func IsDaemonRunning (url string ) bool {
3343 ports .daemonLock .Lock ()
3444 defer ports .daemonLock .Unlock ()
@@ -117,7 +127,7 @@ func (e *Engine) startDaemon(tool types.Tool) (string, error) {
117127 tool .Instructions = types .CommandPrefix + instructions
118128
119129 port , ok := ports .daemonPorts [tool .ID ]
120- url := fmt .Sprintf ("http ://127.0.0.1:%d%s" , port , path )
130+ url := fmt .Sprintf ("https ://127.0.0.1:%d%s" , port , path )
121131 if ok {
122132 return url , nil
123133 }
@@ -133,11 +143,31 @@ func (e *Engine) startDaemon(tool types.Tool) (string, error) {
133143
134144 ctx := ports .daemonCtx
135145 port = nextPort ()
136- url = fmt .Sprintf ("http://127.0.0.1:%d%s" , port , path )
146+ url = fmt .Sprintf ("https://127.0.0.1:%d%s" , port , path )
147+
148+ // Generate a certificate for the daemon, unless one already exists.
149+ certificates .daemonLock .Lock ()
150+ defer certificates .daemonLock .Unlock ()
151+ cert , exists := certificates .daemonCerts [tool .ID ]
152+ if ! exists {
153+ var err error
154+ cert , err = certs .GenerateSelfSignedCert (tool .ID )
155+ if err != nil {
156+ return "" , fmt .Errorf ("failed to generate certificate for daemon: %v" , err )
157+ }
158+
159+ if certificates .daemonCerts == nil {
160+ certificates .daemonCerts = map [string ]certs.CertAndKey {}
161+ }
162+ certificates .daemonCerts [tool .ID ] = cert
163+ }
137164
138165 cmd , stop , err := e .newCommand (ctx , []string {
139166 fmt .Sprintf ("PORT=%d" , port ),
167+ fmt .Sprintf ("CERT=%s" , base64 .StdEncoding .EncodeToString (cert .Cert )),
168+ fmt .Sprintf ("PRIVATE_KEY=%s" , base64 .StdEncoding .EncodeToString (cert .Key )),
140169 fmt .Sprintf ("GPTSCRIPT_PORT=%d" , port ),
170+ fmt .Sprintf ("GPTSCRIPT_CERT=%s" , base64 .StdEncoding .EncodeToString (e .GPTScriptCert .Cert )),
141171 },
142172 tool ,
143173 "{}" ,
@@ -199,8 +229,30 @@ func (e *Engine) startDaemon(tool types.Tool) (string, error) {
199229 ports .daemonWG .Done ()
200230 }()
201231
232+ // Build HTTP client for checking the health of the daemon
233+ clientCert , err := tls .X509KeyPair (e .GPTScriptCert .Cert , e .GPTScriptCert .Key )
234+ if err != nil {
235+ return "" , fmt .Errorf ("failed to create client certificate: %v" , err )
236+ }
237+
238+ pool := x509 .NewCertPool ()
239+ if ! pool .AppendCertsFromPEM (cert .Cert ) {
240+ return "" , fmt .Errorf ("failed to append daemon certificate for [%s]" , tool .ID )
241+ }
242+
243+ httpClient := & http.Client {
244+ Transport : & http.Transport {
245+ TLSClientConfig : & tls.Config {
246+ Certificates : []tls.Certificate {clientCert },
247+ RootCAs : pool ,
248+ InsecureSkipVerify : false ,
249+ },
250+ },
251+ }
252+
253+ // Check the health of the daemon
202254 for i := 0 ; i < 120 ; i ++ {
203- resp , err := http .Get (url )
255+ resp , err := httpClient .Get (url )
204256 if err == nil && resp .StatusCode == http .StatusOK {
205257 go func () {
206258 _ , _ = io .ReadAll (resp .Body )
0 commit comments