Files
AetherForge/agent/deploy/cloud_instance_meta.go
AetherForge 990105f7bf
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Elect one fleet torrent seeder per AWS VPC via IMDS cloud_instance_meta.
Agents read vpc-id from EC2 IMDS on auth; the server scopes subnet_primary_seeder to vpc-id with /24 fallback, exposes VPC seeder badges, and documents cross-VPC gossip via peering/TGW.
2026-06-07 10:06:42 -07:00

106 lines
3.3 KiB
Go

package deploy
import (
"context"
"fmt"
"io"
"net/http"
"strings"
"time"
)
const ec2IMDSBase = "http://169.254.169.254/latest"
type CloudInstanceMeta struct {
VpcID string `json:"vpc_id,omitempty"`
SubnetID string `json:"subnet_id,omitempty"`
Region string `json:"region,omitempty"`
}
func (m CloudInstanceMeta) Present() bool {
return strings.TrimSpace(m.VpcID) != "" || strings.TrimSpace(m.SubnetID) != "" || strings.TrimSpace(m.Region) != ""
}
var ec2IMDSReadMeta = readEC2InstanceMetaImpl
func ReadEC2InstanceMeta() CloudInstanceMeta {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
meta, _ := ec2IMDSReadMeta(ctx)
return meta
}
func readEC2InstanceMetaImpl(ctx context.Context) (CloudInstanceMeta, error) {
token, err := fetchEC2IMDSToken(ctx)
if err != nil {
return CloudInstanceMeta{}, err
}
region, _ := fetchEC2IMDSPath(ctx, token, "meta-data/placement/region")
if strings.TrimSpace(region) == "" {
az, _ := fetchEC2IMDSPath(ctx, token, "meta-data/placement/availability-zone")
az = strings.TrimSpace(az)
if len(az) > 1 {
region = az[:len(az)-1]
}
}
macs, err := fetchEC2IMDSPath(ctx, token, "meta-data/network/interfaces/macs/")
if err != nil || strings.TrimSpace(macs) == "" {
return CloudInstanceMeta{Region: strings.TrimSpace(region)}, nil
}
mac := strings.TrimSpace(strings.Split(macs, "\n")[0])
if mac == "" {
return CloudInstanceMeta{Region: strings.TrimSpace(region)}, nil
}
if !strings.HasSuffix(mac, "/") {
mac += "/"
}
base := "meta-data/network/interfaces/macs/" + mac
vpcID, _ := fetchEC2IMDSPath(ctx, token, base+"vpc-id")
subnetID, _ := fetchEC2IMDSPath(ctx, token, base+"subnet-id")
return CloudInstanceMeta{VpcID: strings.TrimSpace(vpcID), SubnetID: strings.TrimSpace(subnetID), Region: strings.TrimSpace(region)}, nil
}
func fetchEC2IMDSToken(ctx context.Context) (string, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodPut, ec2IMDSBase+"/api/token", nil)
if err != nil {
return "", err
}
req.Header.Set("X-aws-ec2-metadata-token-ttl-seconds", "60")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("imds token HTTP %d", resp.StatusCode)
}
body, err := io.ReadAll(io.LimitReader(resp.Body, 128))
return strings.TrimSpace(string(body)), err
}
func fetchEC2IMDSPath(ctx context.Context, token, path string) (string, error) {
path = strings.TrimPrefix(path, "/")
req, err := http.NewRequestWithContext(ctx, http.MethodGet, ec2IMDSBase+"/"+path, nil)
if err != nil {
return "", err
}
if token != "" {
req.Header.Set("X-aws-ec2-metadata-token", token)
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("imds HTTP %d", resp.StatusCode)
}
body, err := io.ReadAll(io.LimitReader(resp.Body, 64<<10))
return strings.TrimSpace(string(body)), err
}
// ec2IMDSToken and ec2IMDSFetch are shared helpers for cloud venue probing.
func ec2IMDSToken(ctx context.Context) (string, error) { return fetchEC2IMDSToken(ctx) }
func ec2IMDSFetch(ctx context.Context, token, path string) (string, error) {
return fetchEC2IMDSPath(ctx, token, path)
}