diff --git a/wazuh-agent-supervisor/Dockerfile b/wazuh-agent-supervisor/Dockerfile
new file mode 100644
index 0000000..72ee48f
--- /dev/null
+++ b/wazuh-agent-supervisor/Dockerfile
@@ -0,0 +1,46 @@
+# Build the tool
+
+FROM reg.cadoles.com/proxy_cache/library/golang:1.24 AS build
+
+ARG VERSION=v1.0.0
+
+COPY go.mod .
+COPY go.sum .
+COPY src src
+
+RUN env CGO_ENABLED=0 go install -v -ldflags="-w -s -X main.version=${VERSION} -X 'main.BuildDate=$(/usr/bin/date --utc --rfc-3339=seconds)'" ./...
+
+# Copy the tool and install gotmpl and wazuh
+
+FROM reg.cadoles.com/proxy_cache/library/debian:12.10
+
+ARG WAZUH_VERSION=4.11.1-1
+
+RUN export DEBIAN_FRONTEND=noninteractive && \
+ apt-get update -y && \
+ apt-get install -y gpg curl && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
+
+RUN curl -s https://packages.wazuh.com/key/GPG-KEY-WAZUH | gpg --no-default-keyring --keyring gnupg-ring:/usr/share/keyrings/wazuh.gpg --import && chmod 644 /usr/share/keyrings/wazuh.gpg
+
+RUN echo "deb [signed-by=/usr/share/keyrings/wazuh.gpg] https://packages.wazuh.com/4.x/apt/ stable main" | tee -a /etc/apt/sources.list.d/wazuh.list
+
+RUN apt-get update && \
+ apt-get install -y wazuh-agent=$WAZUH_VERSION procps && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
+
+
+ARG GOMPLATE_VERSION=v4.3.2
+
+# Install gomplate (little file templating engine: https://github.com/hairyhenderson/gomplate)
+RUN curl -o /usr/local/bin/gomplate -sSL https://github.com/hairyhenderson/gomplate/releases/download/$GOMPLATE_VERSION/gomplate_linux-amd64 \
+ && chmod +x /usr/local/bin/gomplate
+
+COPY --from=build /go/bin/wazuh-agent-autoadd /usr/local/bin/
+
+COPY init_wazuh_agent.sh .
+RUN chmod 755 /init_wazuh_agent.sh
+
+ENTRYPOINT ["/init_wazuh_agent.sh"]
diff --git a/wazuh-agent-supervisor/go.mod b/wazuh-agent-supervisor/go.mod
new file mode 100644
index 0000000..6cd8363
--- /dev/null
+++ b/wazuh-agent-supervisor/go.mod
@@ -0,0 +1,5 @@
+module forge.cadoles.com/cadoles/wazuh-agent-k8s-autoadd
+
+go 1.24.3
+
+require github.com/caarlos0/env/v11 v11.3.1
diff --git a/wazuh-agent-supervisor/go.sum b/wazuh-agent-supervisor/go.sum
new file mode 100644
index 0000000..1724948
--- /dev/null
+++ b/wazuh-agent-supervisor/go.sum
@@ -0,0 +1,2 @@
+github.com/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA=
+github.com/caarlos0/env/v11 v11.3.1/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U=
diff --git a/wazuh-agent-supervisor/init_wazuh_agent.sh b/wazuh-agent-supervisor/init_wazuh_agent.sh
new file mode 100644
index 0000000..18fe365
--- /dev/null
+++ b/wazuh-agent-supervisor/init_wazuh_agent.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+set -e
+set -o pipefail
+
+# Generate ossec configuration
+TEMPLATE_CONF_FILES=$(find /var/ossec/etc -name '*.gotmpl')
+
+for TEMPLATE_FILE in $TEMPLATE_CONF_FILES; do
+ DEST_FILE=${TEMPLATE_FILE%'.gotmpl'}
+ echo 'Generating file '$DEST_FILE'...'
+ gomplate -f $TEMPLATE_FILE > $DEST_FILE
+ chown --reference=$TEMPLATE_FILE $DEST_FILE
+ chmod --reference=$TEMPLATE_FILE $DEST_FILE
+done
+
+# Add agent to Wazuh manager server
+wazuh-agent-autoadd
+#wazuh-agent-supervisor
+# TODO: rename autoadd -> supervisor + remove following lines
+
+while true; do
+ sleep 10
+done
+
+echo "ERROR: program exited. Container will stop in 5s."
+sleep 5
diff --git a/wazuh-agent-supervisor/src/cmd/wazuh-agent-autoadd/wazuh-agent-autoadd.go b/wazuh-agent-supervisor/src/cmd/wazuh-agent-autoadd/wazuh-agent-autoadd.go
new file mode 100644
index 0000000..d38ea71
--- /dev/null
+++ b/wazuh-agent-supervisor/src/cmd/wazuh-agent-autoadd/wazuh-agent-autoadd.go
@@ -0,0 +1,35 @@
+package main
+
+import (
+ "log"
+ "os"
+
+ "forge.cadoles.com/cadoles/wazuh-agent-k8s-autoadd/src/internal/config"
+ "forge.cadoles.com/cadoles/wazuh-agent-k8s-autoadd/src/internal/wazuh"
+)
+
+func main() {
+ cfg, err := config.NewConfig()
+ if err != nil {
+ log.Print(err)
+ os.Exit(1)
+ }
+
+ key, err := wazuh.AddAgent(cfg)
+ if err != nil {
+ log.Print(err)
+ os.Exit(2)
+ }
+
+ err = wazuh.ImportAuthKey(key)
+ if err != nil {
+ log.Print(err)
+ os.Exit(2)
+ }
+
+ err = wazuh.Start()
+ if err != nil {
+ log.Print(err)
+ os.Exit(2)
+ }
+}
diff --git a/wazuh-agent-supervisor/src/internal/config/config.go b/wazuh-agent-supervisor/src/internal/config/config.go
new file mode 100644
index 0000000..610c25b
--- /dev/null
+++ b/wazuh-agent-supervisor/src/internal/config/config.go
@@ -0,0 +1,27 @@
+package config
+
+import (
+ "github.com/caarlos0/env/v11"
+ "strconv"
+)
+
+type Config struct {
+ WazuhManagerHost string `env:"WAZUH_MANAGER_HOST,required,notEmpty"`
+ WazuhManagerAPIPort int `env:"WAZUH_MANAGER_API_PORT,notEmpty" envDefault:"55000"`
+ BaseURL string
+ User string `env:"WAZUH_MANAGER_USER,required,notEmpty"`
+ Passwd string `env:"WAZUH_MANAGER_PASSWD,required,notEmpty"`
+ SkipSSLVerification bool `env:"WAZUH_MANAGER_SKIP_SSL_VERIFICATION" envDefault:"false"`
+ NodeName string `env:"NODE_NAME,required,notEmpty"`
+}
+
+func NewConfig() (*Config, error) {
+ cfg := &Config{}
+ if err := env.Parse(cfg); err != nil {
+ return nil, err
+ }
+
+ cfg.BaseURL = "https://" + cfg.WazuhManagerHost + ":" + strconv.Itoa(cfg.WazuhManagerAPIPort)
+
+ return cfg, nil
+}
diff --git a/wazuh-agent-supervisor/src/internal/wazuh/wazuh_api.go b/wazuh-agent-supervisor/src/internal/wazuh/wazuh_api.go
new file mode 100644
index 0000000..d3fb774
--- /dev/null
+++ b/wazuh-agent-supervisor/src/internal/wazuh/wazuh_api.go
@@ -0,0 +1,255 @@
+package wazuh
+
+import (
+ "bytes"
+ "crypto/tls"
+ "encoding/json"
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+
+ "forge.cadoles.com/cadoles/wazuh-agent-k8s-autoadd/src/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) (string, 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, cfg.NodeName, token)
+ if err != nil {
+ return "", err
+ }
+
+ if id != "" {
+ log.Print("ID for "+cfg.NodeName+" found: ", id)
+ key, err := getKey(cfg, id, token)
+ return key, err
+ }
+
+ agent := struct {
+ Name string `json:"name"`
+ }{Name: cfg.NodeName}
+ 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))
+ }
+
+ log.Print("ID for "+cfg.NodeName+" created: ", agentInfo.Data.ID)
+
+ return agentInfo.Data.Key, nil
+}
diff --git a/wazuh-agent-supervisor/src/internal/wazuh/wazuh_cmd.go b/wazuh-agent-supervisor/src/internal/wazuh/wazuh_cmd.go
new file mode 100644
index 0000000..0d23662
--- /dev/null
+++ b/wazuh-agent-supervisor/src/internal/wazuh/wazuh_cmd.go
@@ -0,0 +1,40 @@
+package wazuh
+
+import (
+ "io"
+ "log"
+ "os/exec"
+)
+
+func ExecStatus() error {
+ return nil
+}
+
+func ImportAuthKey(key string) error {
+ cmd := exec.Command("/var/ossec/bin/manage_agents", "-i", key)
+ stdin, err := cmd.StdinPipe()
+ if err != nil {
+ return err
+ }
+
+ go func() {
+ defer stdin.Close()
+ io.WriteString(stdin, "y\n\n")
+ }()
+
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ log.Print("Error while importing auth key:", out)
+ }
+ return err
+
+}
+
+func Start() error {
+ cmd := exec.Command("/var/ossec/bin/wazuh-control", "start")
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ log.Print("Error while starting wazuh client:", out)
+ }
+ return err
+}
diff --git a/wazuh-agent-supervisor/var_ossec_etc/ossec.conf b/wazuh-agent-supervisor/var_ossec_etc/ossec.conf
new file mode 100644
index 0000000..cc74489
--- /dev/null
+++ b/wazuh-agent-supervisor/var_ossec_etc/ossec.conf
@@ -0,0 +1,8 @@
+
+
+
+ wazuh-pp.in.nuonet.fr
+ 1514
+
+
+
\ No newline at end of file
diff --git a/wazuh-agent-supervisor/var_ossec_etc/ossec.conf.gotmpl b/wazuh-agent-supervisor/var_ossec_etc/ossec.conf.gotmpl
new file mode 100644
index 0000000..44b5506
--- /dev/null
+++ b/wazuh-agent-supervisor/var_ossec_etc/ossec.conf.gotmpl
@@ -0,0 +1,13 @@
+
+
+
+ {{ getenv "WAZUH_MANAGER_HOST" }}
+ {{ getenv "WAZUH_MANAGER_PORT" "1514" }}
+
+
+
+
+ /var/log/containers/*.log
+ syslog
+
+
\ No newline at end of file