diff --git a/wazuh-agent-k8s-autoadd/cmd/cli/main.go b/wazuh-agent-k8s-autoadd/cmd/cli/main.go index 63a5f62..ccdb690 100644 --- a/wazuh-agent-k8s-autoadd/cmd/cli/main.go +++ b/wazuh-agent-k8s-autoadd/cmd/cli/main.go @@ -15,9 +15,12 @@ func main() { os.Exit(1) } + // agent, err := wazuh_api.AddAgent err = wazuh.AddAgent(cfg) if err != nil { log.Print(err) os.Exit(2) } + + // wazuh_tmpl.Generate(agent) } diff --git a/wazuh-agent-k8s-autoadd/internal/wazuh/wazuh_api.go b/wazuh-agent-k8s-autoadd/internal/wazuh/wazuh_api.go new file mode 100644 index 0000000..a715313 --- /dev/null +++ b/wazuh-agent-k8s-autoadd/internal/wazuh/wazuh_api.go @@ -0,0 +1,259 @@ +package wazuh + +import ( + "bytes" + "crypto/tls" + "encoding/json" + "fmt" + "io" + "log" + "net/http" + + "forge.cadoles.com/cadoles/wazuh-agent-k8s-autoadd/internal/config" +) + +const APIAuthenticate = "/security/user/authenticate" +const APIAgents = "/agents" +// TODO: add APIAgentsSearch and APIAgentsGetKey + +type AuthResponse struct { + Data struct { + Token string `json:"token"` + } `json:"data"` + Error int `json:"error"` +} + +type AddAgentResponse struct { + Data struct { + ID string `json:"id"` + Key string `json:"key"` + } `json:"data"` + Error int `json:"error"` +} + +type ListAgentsResponse struct { + Data struct { + AffectedItems []struct { + ID string `json:"id"` + } `json:"affected_items"` + TotalAffectedItems int `json:"total_affected_items"` + TotalFailedItems int `json:"total_failed_items"` + FailedItems []struct{} `json:"failed_items"` + } `json:"data"` + Message string `json: message` + Error int `json:"error"` +} + +type GetAgentKeyResponse struct { + Data struct { + AffectedItems []struct { + ID string `json:"id"` + Key string `json:"key"` + } `json:"affected_items"` + TotalAffectedItems int `json:"total_affected_items"` + TotalFailedItems int `json:"total_failed_items"` + FailedItems []struct{} `json:"failed_items"` + } `json:"data"` + Message string `json: message` + Error int `json:"error"` +} + +func responseToError(res *http.Response) error { + bytedata, err := io.ReadAll(res.Body) + if err != nil { + log.Print("Error while reading a response's body") + } + reqBody := string(bytedata) + return fmt.Errorf("Bad status on %v: %d\n%+v", res.Request.URL.Host + res.Request.URL.Path, res.StatusCode, reqBody) +} + +func sendRequest(cfg *config.Config, req *http.Request) (*http.Response, error) { + client := http.DefaultClient + if cfg.SkipSSLVerification { + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client = &http.Client{Transport: tr} + } + + res, err := client.Do(req) + return res, err +} + +func getJWT(cfg *config.Config) (string, error) { + req, err := http.NewRequest(http.MethodPost, cfg.BaseURL+APIAuthenticate, http.NoBody) + + if err != nil { + return "", fmt.Errorf("Cannot create request for %v : %+v", cfg.BaseURL+APIAuthenticate, err) + } + req.SetBasicAuth(cfg.User, cfg.Passwd) + + + res, err := sendRequest(cfg, req) + if err != nil { + return "", err + } + + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + return "", responseToError(res) + } + + body, err := io.ReadAll(res.Body) + if err != nil { + return "", err + } + + var authInfo AuthResponse + if err := json.Unmarshal(body, &authInfo); err != nil { + return "", fmt.Errorf("Cannot unmarshal JSON: %v", string(body)) + } + return authInfo.Data.Token, nil +} + +func searchAgent(cfg *config.Config, name string, token string) (string, error) { + url := cfg.BaseURL+APIAgents+"?q=name="+name+"&select=id" + req, err := http.NewRequest(http.MethodGet, url, http.NoBody) + + if err != nil { + return "", fmt.Errorf("cannot create request for %v : %+v", url, err) + } + req.Header.Set("Authorization", "Bearer "+token) + + res, err := sendRequest(cfg, req) + if err != nil { + return "", err + } + + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + return "", responseToError(res) + } + + body, err := io.ReadAll(res.Body) + if err != nil { + return "", err + } + + var agents ListAgentsResponse + if err := json.Unmarshal(body, &agents); err != nil { + return "", fmt.Errorf("Cannot unmarshal JSON: %v", string(body)) + } + if agents.Data.TotalAffectedItems == 0 { + return "", nil + } + if agents.Data.TotalAffectedItems > 1 { + log.Print("Warning: more than 1 agent has name"+name) + } + return agents.Data.AffectedItems[0].ID, nil +} + +func getKey(cfg *config.Config, id string, token string) (string, error) { + url := cfg.BaseURL+APIAgents+"/"+id+"/key" + req, err := http.NewRequest(http.MethodGet, url, http.NoBody) + + if err != nil { + return "", fmt.Errorf("Cannot create request for %v : %+v", url, err) + } + req.Header.Set("Authorization", "Bearer "+token) + + res, err := sendRequest(cfg, req) + if err != nil { + return "", err + } + + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + return "", responseToError(res) + } + + body, err := io.ReadAll(res.Body) + if err != nil { + return "", err + } + + var agent GetAgentKeyResponse + if err := json.Unmarshal(body, &agent); err != nil { + return "", fmt.Errorf("Cannot unmarshal JSON: %v", string(body)) + } + if agent.Data.TotalAffectedItems == 0 { + return "", fmt.Errorf("Error: no key found for id: %s", id) + } + if agent.Data.TotalAffectedItems > 1 { + log.Print("Warning: more than 1 agent has id: ", id) + } + return agent.Data.AffectedItems[0].Key, nil +} + +func AddAgent(cfg *config.Config) error { + token, err := getJWT(cfg) + if err != nil { + return err + } + + log.Print(token) ///////////////////////TODO: remove + + // First : look an agent is already registered with the same name + // Second a : If agent is already registered, get token + // Second b : Elese, add it simply + // Outside : templatiser le fichier de conf ossec.conf + // Outside : import key? Ou l'écrire dans un fichier ? + // Au démarrage du conteneur, manage_agents pour importer la clé + wazuh-control pour restart wazuh agent ? + id, err := searchAgent(cfg, "Test", token) + if err != nil { + return err + } + + if id != "" { + log.Print("ID for "+"Test"+" found: ", id) + key, err := getKey(cfg, id, token) + if err != nil { + return err + } + log.Print("key: ", key) + return nil + } + + agent := struct { + Name string `json:"name"` + }{Name: "Test"} + json_agent, err := json.Marshal(agent) + if err != nil { + return fmt.Errorf("Cannot marshal struct %+v: %+v", agent, err) + } + req, err := http.NewRequest(http.MethodPost, cfg.BaseURL+APIAgents, bytes.NewReader(json_agent)) + + if err != nil { + return fmt.Errorf("Cannot create request for %v : %+v", cfg.BaseURL+APIAgents, err) + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+token) + + res, err := sendRequest(cfg, req) + if err != nil { + return err + } + + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + return responseToError(res) + } + + body, err := io.ReadAll(res.Body) + if err != nil { + return err + } + + var agentInfo AddAgentResponse + if err := json.Unmarshal(body, &agentInfo); err != nil { + return fmt.Errorf("Cannot unmarshal JSON: %v", string(body)) + } + + fmt.Printf("%+v", agentInfo) + + return nil +}