| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- // Copyright (c) Tailscale Inc & AUTHORS
- // SPDX-License-Identifier: BSD-3-Clause
- package tsconsensus
- import (
- "context"
- "fmt"
- "net/netip"
- "testing"
- "tailscale.com/ipn"
- "tailscale.com/ipn/ipnstate"
- "tailscale.com/tailcfg"
- "tailscale.com/types/key"
- "tailscale.com/types/views"
- )
- type testStatusGetter struct {
- status *ipnstate.Status
- }
- func (sg testStatusGetter) getStatus(ctx context.Context) (*ipnstate.Status, error) {
- return sg.status, nil
- }
- const testTag string = "tag:clusterTag"
- func makeAuthTestPeer(i int, tags views.Slice[string]) *ipnstate.PeerStatus {
- return &ipnstate.PeerStatus{
- ID: tailcfg.StableNodeID(fmt.Sprintf("%d", i)),
- Tags: &tags,
- TailscaleIPs: []netip.Addr{
- netip.AddrFrom4([4]byte{100, 0, 0, byte(i)}),
- netip.MustParseAddr(fmt.Sprintf("fd7a:115c:a1e0:0::%d", i)),
- },
- }
- }
- func makeAuthTestPeers(tags [][]string) []*ipnstate.PeerStatus {
- peers := make([]*ipnstate.PeerStatus, len(tags))
- for i, ts := range tags {
- peers[i] = makeAuthTestPeer(i, views.SliceOf(ts))
- }
- return peers
- }
- func authForStatus(s *ipnstate.Status) *authorization {
- return &authorization{
- sg: testStatusGetter{
- status: s,
- },
- tag: testTag,
- }
- }
- func authForPeers(self *ipnstate.PeerStatus, peers []*ipnstate.PeerStatus) *authorization {
- s := &ipnstate.Status{
- BackendState: ipn.Running.String(),
- Self: self,
- Peer: map[key.NodePublic]*ipnstate.PeerStatus{},
- }
- for _, p := range peers {
- s.Peer[key.NewNode().Public()] = p
- }
- return authForStatus(s)
- }
- func TestAuthRefreshErrorsNotRunning(t *testing.T) {
- tests := []struct {
- in *ipnstate.Status
- expected string
- }{
- {
- in: nil,
- expected: "no status",
- },
- {
- in: &ipnstate.Status{
- BackendState: "NeedsMachineAuth",
- },
- expected: "ts Server is not running",
- },
- }
- for _, tt := range tests {
- t.Run(tt.expected, func(t *testing.T) {
- ctx := t.Context()
- a := authForStatus(tt.in)
- err := a.Refresh(ctx)
- if err == nil {
- t.Fatalf("expected err to be non-nil")
- }
- if err.Error() != tt.expected {
- t.Fatalf("expected: %s, got: %s", tt.expected, err.Error())
- }
- })
- }
- }
- func TestAuthUnrefreshed(t *testing.T) {
- a := authForStatus(nil)
- if a.AllowsHost(netip.MustParseAddr("100.0.0.1")) {
- t.Fatalf("never refreshed authorization, allowsHost: expected false, got true")
- }
- gotAllowedPeers := a.AllowedPeers()
- if gotAllowedPeers.Len() != 0 {
- t.Fatalf("never refreshed authorization, allowedPeers: expected [], got %v", gotAllowedPeers)
- }
- if a.SelfAllowed() != false {
- t.Fatalf("never refreshed authorization, selfAllowed: expected false got true")
- }
- }
- func TestAuthAllowsHost(t *testing.T) {
- peerTags := [][]string{
- {"woo"},
- nil,
- {"woo", testTag},
- {testTag},
- }
- peers := makeAuthTestPeers(peerTags)
- tests := []struct {
- name string
- peerStatus *ipnstate.PeerStatus
- expected bool
- }{
- {
- name: "tagged with different tag",
- peerStatus: peers[0],
- expected: false,
- },
- {
- name: "not tagged",
- peerStatus: peers[1],
- expected: false,
- },
- {
- name: "tags includes testTag",
- peerStatus: peers[2],
- expected: true,
- },
- {
- name: "only tag is testTag",
- peerStatus: peers[3],
- expected: true,
- },
- }
- a := authForPeers(nil, peers)
- err := a.Refresh(t.Context())
- if err != nil {
- t.Fatal(err)
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- // test we get the expected result for any of the peers TailscaleIPs
- for _, addr := range tt.peerStatus.TailscaleIPs {
- got := a.AllowsHost(addr)
- if got != tt.expected {
- t.Fatalf("allowed for peer with tags: %v, expected: %t, got %t", tt.peerStatus.Tags, tt.expected, got)
- }
- }
- })
- }
- }
- func TestAuthAllowedPeers(t *testing.T) {
- ctx := t.Context()
- peerTags := [][]string{
- {"woo"},
- nil,
- {"woo", testTag},
- {testTag},
- }
- peers := makeAuthTestPeers(peerTags)
- a := authForPeers(nil, peers)
- err := a.Refresh(ctx)
- if err != nil {
- t.Fatal(err)
- }
- ps := a.AllowedPeers()
- if ps.Len() != 2 {
- t.Fatalf("expected: 2, got: %d", ps.Len())
- }
- for _, i := range []int{2, 3} {
- if !ps.ContainsFunc(func(p *ipnstate.PeerStatus) bool {
- return p.ID == peers[i].ID
- }) {
- t.Fatalf("expected peers[%d] to be in AllowedPeers because it is tagged with testTag", i)
- }
- }
- }
- func TestAuthSelfAllowed(t *testing.T) {
- tests := []struct {
- name string
- in []string
- expected bool
- }{
- {
- name: "self has different tag",
- in: []string{"woo"},
- expected: false,
- },
- {
- name: "selfs tags include testTag",
- in: []string{"woo", testTag},
- expected: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- ctx := t.Context()
- self := makeAuthTestPeer(0, views.SliceOf(tt.in))
- a := authForPeers(self, nil)
- err := a.Refresh(ctx)
- if err != nil {
- t.Fatal(err)
- }
- got := a.SelfAllowed()
- if got != tt.expected {
- t.Fatalf("expected: %t, got: %t", tt.expected, got)
- }
- })
- }
- }
|