mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-10-31 08:11:32 +01:00 
			
		
		
		
	Updates #12774 Signed-off-by: Andrew Dunham <andrew@du.nham.ca> Change-Id: I1661b6a2da7966ab667b075894837afd96f4742f
		
			
				
	
	
		
			124 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			124 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) Tailscale Inc & AUTHORS
 | |
| // SPDX-License-Identifier: BSD-3-Clause
 | |
| 
 | |
| package magicsock
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"net/http"
 | |
| 	"net/http/httptest"
 | |
| 	"net/netip"
 | |
| 	"slices"
 | |
| 	"testing"
 | |
| 
 | |
| 	"tailscale.com/util/cloudenv"
 | |
| )
 | |
| 
 | |
| func TestCloudInfo_AWS(t *testing.T) {
 | |
| 	const (
 | |
| 		mac1      = "06:1d:00:00:00:00"
 | |
| 		mac2      = "06:1d:00:00:00:01"
 | |
| 		publicV4  = "1.2.3.4"
 | |
| 		otherV4_1 = "5.6.7.8"
 | |
| 		otherV4_2 = "11.12.13.14"
 | |
| 		v6addr    = "2001:db8::1"
 | |
| 
 | |
| 		macsPrefix = "/latest/meta-data/network/interfaces/macs/"
 | |
| 	)
 | |
| 	// Launch a fake AWS IMDS server
 | |
| 	fake := &fakeIMDS{
 | |
| 		tb: t,
 | |
| 		paths: map[string]string{
 | |
| 			macsPrefix: mac1 + "\n" + mac2,
 | |
| 			// This is the "main" public IP address for the instance
 | |
| 			macsPrefix + mac1 + "/public-ipv4s": publicV4,
 | |
| 
 | |
| 			// There's another interface with two public IPs
 | |
| 			// attached to it and an IPv6 address, all of which we
 | |
| 			// should discover.
 | |
| 			macsPrefix + mac2 + "/public-ipv4s": otherV4_1 + "\n" + otherV4_2,
 | |
| 			macsPrefix + mac2 + "/ipv6s":        v6addr,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	srv := httptest.NewServer(fake)
 | |
| 	defer srv.Close()
 | |
| 
 | |
| 	ci := newCloudInfo(t.Logf)
 | |
| 	ci.cloud = cloudenv.AWS
 | |
| 	ci.endpoint = srv.URL
 | |
| 
 | |
| 	ips, err := ci.GetPublicIPs(context.Background())
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("unexpected error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	wantIPs := []netip.Addr{
 | |
| 		netip.MustParseAddr(publicV4),
 | |
| 		netip.MustParseAddr(otherV4_1),
 | |
| 		netip.MustParseAddr(otherV4_2),
 | |
| 		netip.MustParseAddr(v6addr),
 | |
| 	}
 | |
| 	if !slices.Equal(ips, wantIPs) {
 | |
| 		t.Fatalf("got %v, want %v", ips, wantIPs)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestCloudInfo_AWSNotPublic(t *testing.T) {
 | |
| 	returns404 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | |
| 		if r.Method == "PUT" && r.URL.Path == "/latest/api/token" {
 | |
| 			w.Header().Set("Server", "EC2ws")
 | |
| 			w.Write([]byte("fake-imds-token"))
 | |
| 			return
 | |
| 		}
 | |
| 		http.NotFound(w, r)
 | |
| 	})
 | |
| 	srv := httptest.NewServer(returns404)
 | |
| 	defer srv.Close()
 | |
| 
 | |
| 	ci := newCloudInfo(t.Logf)
 | |
| 	ci.cloud = cloudenv.AWS
 | |
| 	ci.endpoint = srv.URL
 | |
| 
 | |
| 	// If the IMDS server doesn't return any public IPs, it's not an error
 | |
| 	// and we should just get an empty list.
 | |
| 	ips, err := ci.GetPublicIPs(context.Background())
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("unexpected error: %v", err)
 | |
| 	}
 | |
| 	if len(ips) != 0 {
 | |
| 		t.Fatalf("got %v, want none", ips)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type fakeIMDS struct {
 | |
| 	tb    testing.TB
 | |
| 	paths map[string]string
 | |
| }
 | |
| 
 | |
| func (f *fakeIMDS) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | |
| 	f.tb.Logf("%s %s", r.Method, r.URL.Path)
 | |
| 	path := r.URL.Path
 | |
| 
 | |
| 	// Handle the /latest/api/token case
 | |
| 	const token = "fake-imds-token"
 | |
| 	if r.Method == "PUT" && path == "/latest/api/token" {
 | |
| 		w.Header().Set("Server", "EC2ws")
 | |
| 		w.Write([]byte(token))
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// Otherwise, require the IMDSv2 token to be set
 | |
| 	if r.Header.Get("X-aws-ec2-metadata-token") != token {
 | |
| 		f.tb.Errorf("missing or invalid IMDSv2 token")
 | |
| 		http.Error(w, "missing or invalid IMDSv2 token", http.StatusForbidden)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	if v, ok := f.paths[path]; ok {
 | |
| 		w.Write([]byte(v))
 | |
| 		return
 | |
| 	}
 | |
| 	http.NotFound(w, r)
 | |
| }
 |