diff --git a/internal/agent/http.go b/internal/agent/http.go index 8b86884..dec6575 100644 --- a/internal/agent/http.go +++ b/internal/agent/http.go @@ -3,13 +3,16 @@ package agent import ( + "encoding/base64" "encoding/json" "fmt" "io" "net/http" + "os" "strings" "time" + "github.com/billgraziano/dpapi" "github.com/sonroyaalmerol/pbs-plus/internal/utils" "golang.org/x/sys/windows/registry" ) @@ -22,9 +25,22 @@ func ProxmoxHTTPRequest(method, url string, body io.Reader, respBody any) error if err != nil { return fmt.Errorf("ProxmoxHTTPRequest: server url not found -> %w", err) } - defer key.Close() + var drivePublicKey *string + keyStr := "Software\\PBSPlus\\Config\\SFTP-C" + if driveKey, err := registry.OpenKey(registry.LOCAL_MACHINE, keyStr, registry.QUERY_VALUE); err == nil { + defer driveKey.Close() + if publicKey, _, err := driveKey.GetStringValue("ServerKey"); err == nil { + if decrypted, err := dpapi.Decrypt(publicKey); err == nil { + if decoded, err := base64.StdEncoding.DecodeString(decrypted); err == nil { + decodedStr := string(decoded) + drivePublicKey = &decodedStr + } + } + } + } + if serverUrl, _, err = key.GetStringValue("ServerURL"); err != nil || serverUrl == "" { return fmt.Errorf("ProxmoxHTTPRequest: server url not found -> %w", err) } @@ -43,7 +59,13 @@ func ProxmoxHTTPRequest(method, url string, body io.Reader, respBody any) error return fmt.Errorf("ProxmoxHTTPRequest: error creating http request -> %w", err) } + hostname, _ := os.Hostname() + req.Header.Add("Content-Type", "application/json") + if drivePublicKey != nil { + encodedKey := base64.StdEncoding.EncodeToString([]byte(*drivePublicKey)) + req.Header.Set("Authorization", fmt.Sprintf("PBSPlusAPIAgent=%s---C:%s", hostname, encodedKey)) + } if httpClient == nil { httpClient = &http.Client{ diff --git a/internal/proxy/controllers/agents/agents.go b/internal/proxy/controllers/agents/agents.go index 839ff83..1f71fac 100644 --- a/internal/proxy/controllers/agents/agents.go +++ b/internal/proxy/controllers/agents/agents.go @@ -22,6 +22,10 @@ func AgentLogHandler(storeInstance *store.Store) func(http.ResponseWriter, *http http.Error(w, "Invalid HTTP method", http.StatusBadRequest) } + if err := store.CheckAgentAuth(r); err != nil { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + } + syslogger, err := syslog.InitializeLogger() if err != nil { w.WriteHeader(http.StatusInternalServerError) diff --git a/internal/proxy/controllers/targets/targets.go b/internal/proxy/controllers/targets/targets.go index d0132e8..f3d20af 100644 --- a/internal/proxy/controllers/targets/targets.go +++ b/internal/proxy/controllers/targets/targets.go @@ -104,6 +104,10 @@ func D2DTargetAgentHandler(storeInstance *store.Store) func(http.ResponseWriter, http.Error(w, "Invalid HTTP method", http.StatusBadRequest) } + if err := store.CheckAgentAuth(r); err != nil { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + } + var reqParsed NewAgentHostnameRequest err := json.NewDecoder(r.Body).Decode(&reqParsed) if err != nil { diff --git a/internal/store/auth.go b/internal/store/auth.go new file mode 100644 index 0000000..86940f2 --- /dev/null +++ b/internal/store/auth.go @@ -0,0 +1,51 @@ +package store + +import ( + "encoding/base64" + "fmt" + "net/http" + "os" + "path/filepath" + "reflect" + "strings" + + "github.com/sonroyaalmerol/pbs-plus/internal/utils" +) + +func CheckAgentAuth(r *http.Request) error { + auth := r.Header.Get("Authorization") + if !strings.HasPrefix(auth, "PBSPlusAPIAgent=") { + return fmt.Errorf("CheckAgentAuth: invalid auth prefix") + } + + privKeyDir := filepath.Join(DbBasePath, "agent_keys") + + authTok := strings.TrimPrefix(auth, "PBSPlusAPIAgent=") + authSplit := strings.Split(authTok, ":") + + privKeyFilePath := filepath.Join( + privKeyDir, + fmt.Sprintf("%s.key", authSplit[0]), + ) + + privKeyFile, err := os.ReadFile(privKeyFilePath) + if err != nil { + return fmt.Errorf("CheckAgentAuth: error opening private key file \"%s\" -> %w", privKeyFilePath, err) + } + + pubKey, err := utils.GeneratePublicKeyFromPrivateKey(privKeyFile) + if err != nil { + return fmt.Errorf("CheckAgentAuth: error generating pub key \"%s\" -> %w", privKeyFilePath, err) + } + + passedPub, err := base64.StdEncoding.DecodeString(authSplit[1]) + if err != nil { + return fmt.Errorf("CheckAgentAuth: error pub key -> %w", err) + } + + if !reflect.DeepEqual(pubKey, passedPub) { + return fmt.Errorf("CheckAgentAuth: invalid auth") + } + + return nil +} diff --git a/internal/utils/ssh_keys.go b/internal/utils/ssh_keys.go index 7c597cd..ea5fc6c 100644 --- a/internal/utils/ssh_keys.go +++ b/internal/utils/ssh_keys.go @@ -6,6 +6,7 @@ import ( "crypto/x509" "encoding/pem" "fmt" + "sync" "golang.org/x/crypto/ssh" ) @@ -55,3 +56,30 @@ func generatePublicKey(privatekey *rsa.PublicKey) ([]byte, error) { return pubKeyBytes, nil } + +var pubKeyCache sync.Map + +func GeneratePublicKeyFromPrivateKey(encodedPrivateKey []byte) ([]byte, error) { + cached, ok := pubKeyCache.Load(encodedPrivateKey) + if ok { + return cached.([]byte), nil + } + + block, _ := pem.Decode(encodedPrivateKey) + if block == nil || block.Type != "RSA PRIVATE KEY" { + return nil, fmt.Errorf("GeneratePublicKeyFromPrivateKey: invalid private key type or format") + } + + privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return nil, fmt.Errorf("GeneratePublicKeyFromPrivateKey: error parsing private key -> %w", err) + } + + publicKey, err := generatePublicKey(&privateKey.PublicKey) + if err != nil { + return nil, fmt.Errorf("GeneratePublicKeyFromPrivateKey: error generating public key -> %w", err) + } + + pubKeyCache.Store(encodedPrivateKey, publicKey) + return publicKey, nil +}