@@ -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 ()
@@ -128,7 +138,7 @@ func (e *Engine) startDaemon(tool types.Tool) (string, error) {
128138 tool .Instructions = types .CommandPrefix + instructions
129139
130140 port , ok := ports .daemonPorts [tool .ID ]
131- url := fmt .Sprintf ("http ://127.0.0.1:%d%s" , port , path )
141+ url := fmt .Sprintf ("https ://127.0.0.1:%d%s" , port , path )
132142 if ok && ports .daemonsRunning [url ] != nil {
133143 return url , nil
134144 }
@@ -144,11 +154,31 @@ func (e *Engine) startDaemon(tool types.Tool) (string, error) {
144154
145155 ctx := ports .daemonCtx
146156 port = nextPort ()
147- url = fmt .Sprintf ("http://127.0.0.1:%d%s" , port , path )
157+ url = fmt .Sprintf ("https://127.0.0.1:%d%s" , port , path )
158+
159+ // Generate a certificate for the daemon, unless one already exists.
160+ certificates .daemonLock .Lock ()
161+ defer certificates .daemonLock .Unlock ()
162+ cert , exists := certificates .daemonCerts [tool .ID ]
163+ if ! exists {
164+ var err error
165+ cert , err = certs .GenerateSelfSignedCert (tool .ID )
166+ if err != nil {
167+ return "" , fmt .Errorf ("failed to generate certificate for daemon: %v" , err )
168+ }
169+
170+ if certificates .daemonCerts == nil {
171+ certificates .daemonCerts = map [string ]certs.CertAndKey {}
172+ }
173+ certificates .daemonCerts [tool .ID ] = cert
174+ }
148175
149176 cmd , stop , err := e .newCommand (ctx , []string {
150177 fmt .Sprintf ("PORT=%d" , port ),
178+ fmt .Sprintf ("CERT=%s" , base64 .StdEncoding .EncodeToString (cert .Cert )),
179+ fmt .Sprintf ("PRIVATE_KEY=%s" , base64 .StdEncoding .EncodeToString (cert .Key )),
151180 fmt .Sprintf ("GPTSCRIPT_PORT=%d" , port ),
181+ fmt .Sprintf ("GPTSCRIPT_CERT=%s" , base64 .StdEncoding .EncodeToString (e .GPTScriptCert .Cert )),
152182 },
153183 tool ,
154184 "{}" ,
@@ -210,8 +240,30 @@ func (e *Engine) startDaemon(tool types.Tool) (string, error) {
210240 ports .daemonWG .Done ()
211241 }()
212242
243+ // Build HTTP client for checking the health of the daemon
244+ clientCert , err := tls .X509KeyPair (e .GPTScriptCert .Cert , e .GPTScriptCert .Key )
245+ if err != nil {
246+ return "" , fmt .Errorf ("failed to create client certificate: %v" , err )
247+ }
248+
249+ pool := x509 .NewCertPool ()
250+ if ! pool .AppendCertsFromPEM (cert .Cert ) {
251+ return "" , fmt .Errorf ("failed to append daemon certificate for [%s]" , tool .ID )
252+ }
253+
254+ httpClient := & http.Client {
255+ Transport : & http.Transport {
256+ TLSClientConfig : & tls.Config {
257+ Certificates : []tls.Certificate {clientCert },
258+ RootCAs : pool ,
259+ InsecureSkipVerify : false ,
260+ },
261+ },
262+ }
263+
264+ // Check the health of the daemon
213265 for i := 0 ; i < 120 ; i ++ {
214- resp , err := http .Get (url )
266+ resp , err := httpClient .Get (url )
215267 if err == nil && resp .StatusCode == http .StatusOK {
216268 go func () {
217269 _ , _ = io .ReadAll (resp .Body )
0 commit comments