fix public ip selfcheck logic
This commit is contained in:
1 parent
a6f9646da5
commit
67adc6670e
18 files changed
+209
-90
No files matched your search
@@ -11,8 +11,12 @@ package agentcore
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"cloudipvalidator/internal/apiclient"
|
||||
@@ -139,19 +143,16 @@ func (a *Agent) handleSelfCheckAndRun(ctx context.Context, assignment assignment
|
||||
selfCtx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
var whoami struct {
|
||||
IP string `json:"ip"`
|
||||
}
|
||||
_, err := a.client.Do(selfCtx, "GET", "/api/v1/whatsmyip", nil, &whoami)
|
||||
success := err == nil && whoami.IP == assignment.IPAddress
|
||||
detectedIP, err := a.detectPublicIP(selfCtx)
|
||||
success := err == nil && detectedIP == assignment.IPAddress
|
||||
detail := "matched"
|
||||
if err != nil {
|
||||
detail = "whatsmyip request failed: " + err.Error()
|
||||
detail = "ip echo request failed: " + err.Error()
|
||||
} else if !success {
|
||||
detail = fmt.Sprintf("egress ip %q does not match assigned fip %q", whoami.IP, assignment.IPAddress)
|
||||
detail = fmt.Sprintf("egress ip %q does not match assigned fip %q", detectedIP, assignment.IPAddress)
|
||||
}
|
||||
|
||||
a.postSelfCheck(ctx, assignment.IPID, whoami.IP, success, detail)
|
||||
a.postSelfCheck(ctx, assignment.IPID, detectedIP, success, detail)
|
||||
a.postEvent(ctx, assignment.IPID, "self_check_result", fmt.Sprintf(`{"success":%t}`, success))
|
||||
|
||||
if !success {
|
||||
@@ -161,6 +162,59 @@ func (a *Agent) handleSelfCheckAndRun(ctx context.Context, assignment assignment
|
||||
a.runChecks(ctx, assignment)
|
||||
}
|
||||
|
||||
// detectPublicIP asks each configured IP-echo URL, in order, for the
|
||||
// address this validator is currently seen egressing from, returning the
|
||||
// first one that answers with a parseable IP. These must be resources
|
||||
// genuinely outside the cloud project (see config.SelfCheckCfg) — OpenStack
|
||||
// only applies floating-IP SNAT to traffic leaving via the external
|
||||
// network, so anything reachable over the project's internal network would
|
||||
// report the validator's private address instead, regardless of whether
|
||||
// the floating IP is correctly attached.
|
||||
func (a *Agent) detectPublicIP(ctx context.Context) (string, error) {
|
||||
var lastErr error
|
||||
for _, url := range a.cfg.SelfCheck.IPEchoURLs {
|
||||
ip, err := fetchIPEcho(ctx, url)
|
||||
if err != nil {
|
||||
lastErr = fmt.Errorf("%s: %w", url, err)
|
||||
continue
|
||||
}
|
||||
return ip, nil
|
||||
}
|
||||
if lastErr == nil {
|
||||
lastErr = fmt.Errorf("no self_check.ip_echo_urls configured")
|
||||
}
|
||||
return "", lastErr
|
||||
}
|
||||
|
||||
// fetchIPEcho performs a single GET against an IP-echo endpoint that
|
||||
// returns the caller's address as a bare string in the response body
|
||||
// (the common contract shared by services like api.ipify.org,
|
||||
// ifconfig.me/ip, icanhazip.com — and by the local stub used in
|
||||
// scripts/run-local-e2e.sh).
|
||||
func fetchIPEcho(ctx context.Context, url string) (string, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("build request: %w", err)
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode >= 400 {
|
||||
return "", fmt.Errorf("unexpected status %d", resp.StatusCode)
|
||||
}
|
||||
body, err := io.ReadAll(io.LimitReader(resp.Body, 256))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("read response: %w", err)
|
||||
}
|
||||
ip := strings.TrimSpace(string(body))
|
||||
if net.ParseIP(ip) == nil {
|
||||
return "", fmt.Errorf("response is not a valid IP: %q", ip)
|
||||
}
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
func (a *Agent) runChecks(ctx context.Context, assignment assignmentResp) {
|
||||
var results []checkResultDTO
|
||||
for _, ct := range assignment.CheckConfig {
|
||||
|
||||
Reference in new issue
Block a user