pinning + new version of backups

This commit is contained in:
KingKDot
2026-07-30 14:03:16 -05:00
parent 477b9a5353
commit 2669ab1388
32 changed files with 1527 additions and 133 deletions
+19 -14
View File
@@ -8,8 +8,6 @@ import (
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"encoding/hex"
"errors"
"fmt"
@@ -29,6 +27,7 @@ import (
"unicode/utf8"
agentRuntime "overlord-client/cmd/agent/runtime"
agentTLS "overlord-client/cmd/agent/tlsconfig"
"overlord-client/cmd/agent/wire"
)
@@ -769,17 +768,13 @@ func HandleFileUploadHTTP(ctx context.Context, env *agentRuntime.Env, cmdID stri
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: "unsupported upload url scheme"})
}
tlsConfig := &tls.Config{InsecureSkipVerify: env.Cfg.TLSInsecureSkipVerify, MinVersion: tls.VersionTLS12}
if caPath := strings.TrimSpace(env.Cfg.TLSCAPath); caPath != "" {
caBytes, err := os.ReadFile(caPath)
if err != nil {
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: fmt.Sprintf("failed to read TLS CA: %v", err)})
}
pool := x509.NewCertPool()
if !pool.AppendCertsFromPEM(caBytes) {
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: "failed to parse TLS CA"})
}
tlsConfig.RootCAs = pool
tlsConfig, err := agentTLS.Build(agentTLS.Options{
InsecureSkipVerify: env.Cfg.TLSInsecureSkipVerify,
CAPath: env.Cfg.TLSCAPath,
SPKIPins: env.Cfg.TLSSPKIPins,
})
if err != nil {
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: fmt.Sprintf("invalid TLS configuration: %v", err)})
}
transport := &http.Transport{
@@ -986,6 +981,9 @@ func resolveUploadPullURL(env *agentRuntime.Env, raw string) (string, error) {
if scheme != "http" && scheme != "https" {
return "", fmt.Errorf("unsupported upload url scheme: %s", parsed.Scheme)
}
if scheme == "http" && !env.Cfg.TLSInsecureSkipVerify {
return "", errors.New("plaintext file transfers are disabled; use https")
}
if !strings.HasPrefix(parsed.Path, "/api/file/upload/pull/") {
return parsed.String(), nil
}
@@ -1010,8 +1008,15 @@ func resolveUploadPullURL(env *agentRuntime.Env, raw string) (string, error) {
case "wss":
server.Scheme = "https"
case "ws":
if !env.Cfg.TLSInsecureSkipVerify {
return "", errors.New("plaintext file transfers are disabled; use wss")
}
server.Scheme = "http"
case "https", "http":
case "https":
case "http":
if !env.Cfg.TLSInsecureSkipVerify {
return "", errors.New("plaintext file transfers are disabled; use https")
}
default:
return "", fmt.Errorf("unsupported agent server scheme: %s", server.Scheme)
}
@@ -545,7 +545,8 @@ func TestHandleFileUploadHTTP_RewritesUploadPullURLToAgentServer(t *testing.T) {
env := &rt.Env{
Conn: writer,
Cfg: config.Config{
ServerURLs: []string{strings.Replace(ts.URL, "http://", "ws://", 1)},
ServerURLs: []string{strings.Replace(ts.URL, "http://", "ws://", 1)},
TLSInsecureSkipVerify: true,
},
}
@@ -2,19 +2,17 @@ package handlers
import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
"strings"
"overlord-client/cmd/agent/plugins"
agentRuntime "overlord-client/cmd/agent/runtime"
agentTLS "overlord-client/cmd/agent/tlsconfig"
"overlord-client/cmd/agent/wire"
)
@@ -31,17 +29,13 @@ func HandlePluginLoadHTTP(ctx context.Context, env *agentRuntime.Env, cmdID stri
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: err.Error()})
}
tlsConfig := &tls.Config{InsecureSkipVerify: env.Cfg.TLSInsecureSkipVerify, MinVersion: tls.VersionTLS12}
if caPath := strings.TrimSpace(env.Cfg.TLSCAPath); caPath != "" {
caBytes, err := os.ReadFile(caPath)
if err != nil {
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: fmt.Sprintf("failed to read TLS CA: %v", err)})
}
pool := x509.NewCertPool()
if !pool.AppendCertsFromPEM(caBytes) {
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: "failed to parse TLS CA"})
}
tlsConfig.RootCAs = pool
tlsConfig, err := agentTLS.Build(agentTLS.Options{
InsecureSkipVerify: env.Cfg.TLSInsecureSkipVerify,
CAPath: env.Cfg.TLSCAPath,
SPKIPins: env.Cfg.TLSSPKIPins,
})
if err != nil {
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: fmt.Sprintf("invalid TLS configuration: %v", err)})
}
client := &http.Client{Transport: &http.Transport{TLSClientConfig: tlsConfig}}
@@ -114,6 +108,9 @@ func resolvePluginPullURL(env *agentRuntime.Env, raw string) (string, error) {
if scheme != "http" && scheme != "https" {
return "", fmt.Errorf("unsupported plugin pull scheme: %s", parsed.Scheme)
}
if scheme == "http" && !env.Cfg.TLSInsecureSkipVerify {
return "", errors.New("plaintext plugin downloads are disabled; use https")
}
return parsed.String(), nil
}
@@ -132,8 +129,15 @@ func resolvePluginPullURL(env *agentRuntime.Env, raw string) (string, error) {
case "wss":
server.Scheme = "https"
case "ws":
if !env.Cfg.TLSInsecureSkipVerify {
return "", errors.New("plaintext plugin downloads are disabled; use wss")
}
server.Scheme = "http"
case "https", "http":
case "https":
case "http":
if !env.Cfg.TLSInsecureSkipVerify {
return "", errors.New("plaintext plugin downloads are disabled; use https")
}
default:
return "", fmt.Errorf("unsupported agent server scheme: %s", server.Scheme)
}
@@ -0,0 +1,48 @@
package handlers
import (
"strings"
"testing"
"overlord-client/cmd/agent/config"
agentRuntime "overlord-client/cmd/agent/runtime"
)
func TestSensitiveTransfersRejectPlaintextByDefault(t *testing.T) {
env := &agentRuntime.Env{Cfg: config.Config{
ServerURLs: []string{"ws://server.example.test:5173"},
}}
if _, err := resolveUploadPullURL(env, "/api/file/upload/pull/id"); err == nil ||
!strings.Contains(err.Error(), "plaintext") {
t.Fatalf("expected plaintext file transfer rejection, got %v", err)
}
if _, err := resolvePluginPullURL(env, "/api/plugins/pull/id"); err == nil ||
!strings.Contains(err.Error(), "plaintext") {
t.Fatalf("expected plaintext plugin transfer rejection, got %v", err)
}
if _, err := buildWhipURL(env, "/api/webrtc/whip"); err == nil ||
!strings.Contains(err.Error(), "plaintext") {
t.Fatalf("expected plaintext WebRTC signaling rejection, got %v", err)
}
}
func TestSensitiveTransfersAllowExplicitDevelopmentOptOut(t *testing.T) {
env := &agentRuntime.Env{Cfg: config.Config{
ServerURLs: []string{"ws://127.0.0.1:5173"},
TLSInsecureSkipVerify: true,
}}
if resolved, err := resolveUploadPullURL(env, "/api/file/upload/pull/id"); err != nil ||
!strings.HasPrefix(resolved, "http://") {
t.Fatalf("expected explicit plaintext file transfer, got %q, %v", resolved, err)
}
if resolved, err := resolvePluginPullURL(env, "/api/plugins/pull/id"); err != nil ||
!strings.HasPrefix(resolved, "http://") {
t.Fatalf("expected explicit plaintext plugin transfer, got %q, %v", resolved, err)
}
if resolved, err := buildWhipURL(env, "/api/webrtc/whip"); err != nil ||
!strings.HasPrefix(resolved, "http://") {
t.Fatalf("expected explicit plaintext signaling, got %q, %v", resolved, err)
}
}
@@ -103,6 +103,7 @@ func handleWebrtcPublish(ctx context.Context, env *runtime.Env, cmdID string, pa
PublishToken: token,
TLSInsecureSkipVerify: env.Cfg.TLSInsecureSkipVerify,
TLSCAPath: env.Cfg.TLSCAPath,
TLSSPKIPins: env.Cfg.TLSSPKIPins,
ICEServers: parseICEServers(payload["iceServers"]),
HasVideo: hasVideo,
HasAudio: hasAudio,
@@ -309,7 +310,17 @@ func buildWhipURL(env *runtime.Env, whipPath string) (string, error) {
case "wss":
base.Scheme = "https"
case "ws":
if !env.Cfg.TLSInsecureSkipVerify {
return "", fmt.Errorf("plaintext WebRTC signaling is disabled; use wss")
}
base.Scheme = "http"
case "http":
if !env.Cfg.TLSInsecureSkipVerify {
return "", fmt.Errorf("plaintext WebRTC signaling is disabled; use https")
}
case "https":
default:
return "", fmt.Errorf("unsupported WebRTC signaling scheme: %s", base.Scheme)
}
if !strings.HasPrefix(whipPath, "/") {
whipPath = "/" + whipPath