package agent import ( "bytes" "encoding/json" "fmt" "io" "net/http" "strings" "time" ) // httpClient is shared by the Vault and Gitea calls. A modest timeout keeps a // hung endpoint from wedging watchpr's poll loop. var httpClient = &http.Client{Timeout: 30 * time.Second} // approleLogin logs in with role_id only (no secret_id) and returns the // resulting client_token. func approleLogin(vaultAddr, roleID string) (string, error) { body, _ := json.Marshal(map[string]string{"role_id": roleID}) url := strings.TrimRight(vaultAddr, "/") + "/v1/auth/approle/login" req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(body)) if err != nil { return "", err } req.Header.Set("Content-Type", "application/json") resp, err := httpClient.Do(req) if err != nil { return "", fmt.Errorf("vault approle login: %w", err) } defer resp.Body.Close() data, _ := io.ReadAll(resp.Body) if resp.StatusCode != http.StatusOK { return "", fmt.Errorf("vault approle login: HTTP %d: %s", resp.StatusCode, strings.TrimSpace(string(data))) } var out struct { Auth struct { ClientToken string `json:"client_token"` } `json:"auth"` } if err := json.Unmarshal(data, &out); err != nil { return "", fmt.Errorf("vault approle login: decoding response: %w", err) } if out.Auth.ClientToken == "" { return "", fmt.Errorf("vault approle login: no client_token in response") } return out.Auth.ClientToken, nil } // readGiteaCreds reads the Gitea creds secret and returns the token field. func readGiteaCreds(vaultAddr, clientToken string) (string, error) { url := strings.TrimRight(vaultAddr, "/") + "/v1/" + GiteaCredsPath req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { return "", err } req.Header.Set("X-Vault-Token", clientToken) resp, err := httpClient.Do(req) if err != nil { return "", fmt.Errorf("vault read %s: %w", GiteaCredsPath, err) } defer resp.Body.Close() data, _ := io.ReadAll(resp.Body) if resp.StatusCode != http.StatusOK { return "", fmt.Errorf("vault read %s: HTTP %d: %s", GiteaCredsPath, resp.StatusCode, strings.TrimSpace(string(data))) } var out struct { Data struct { Token string `json:"token"` } `json:"data"` } if err := json.Unmarshal(data, &out); err != nil { return "", fmt.Errorf("vault read %s: decoding response: %w", GiteaCredsPath, err) } if out.Data.Token == "" { return "", fmt.Errorf("vault read %s: no token field in secret", GiteaCredsPath) } return out.Data.Token, nil }