github.com/blend/go-sdk@v1.20220411.3/envoyutil/identity_processor_test.go (about)

     1  /*
     2  
     3  Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file.
     5  
     6  */
     7  
     8  package envoyutil_test
     9  
    10  import (
    11  	"testing"
    12  
    13  	sdkAssert "github.com/blend/go-sdk/assert"
    14  	"github.com/blend/go-sdk/collections"
    15  	"github.com/blend/go-sdk/spiffeutil"
    16  	"github.com/blend/go-sdk/uuid"
    17  
    18  	"github.com/blend/go-sdk/envoyutil"
    19  )
    20  
    21  func TestOptIdentityType(t *testing.T) {
    22  	assert := sdkAssert.New(t)
    23  
    24  	ip := &envoyutil.IdentityProcessor{}
    25  	opt := envoyutil.OptIdentityType(envoyutil.ServerIdentity)
    26  	opt(ip)
    27  
    28  	expected := &envoyutil.IdentityProcessor{
    29  		Type: envoyutil.ServerIdentity,
    30  	}
    31  	assert.Equal(expected, ip)
    32  }
    33  
    34  func TestOptAllowedTrustDomains(t *testing.T) {
    35  	assert := sdkAssert.New(t)
    36  
    37  	ip := &envoyutil.IdentityProcessor{
    38  		AllowedTrustDomains: []string{"x.invalid"},
    39  	}
    40  	opt := envoyutil.OptAllowedTrustDomains("y.invalid")
    41  	opt(ip)
    42  
    43  	expected := &envoyutil.IdentityProcessor{
    44  		AllowedTrustDomains: []string{"x.invalid", "y.invalid"},
    45  	}
    46  	assert.Equal(expected, ip)
    47  }
    48  
    49  func TestOptDeniedTrustDomains(t *testing.T) {
    50  	assert := sdkAssert.New(t)
    51  
    52  	ip := &envoyutil.IdentityProcessor{
    53  		DeniedTrustDomains: []string{"y.invalid"},
    54  	}
    55  	opt := envoyutil.OptDeniedTrustDomains("z.invalid")
    56  	opt(ip)
    57  
    58  	expected := &envoyutil.IdentityProcessor{
    59  		DeniedTrustDomains: []string{"y.invalid", "z.invalid"},
    60  	}
    61  	assert.Equal(expected, ip)
    62  }
    63  
    64  func TestOptAllowedIdentities(t *testing.T) {
    65  	assert := sdkAssert.New(t)
    66  
    67  	ip := &envoyutil.IdentityProcessor{
    68  		AllowedIdentities: collections.NewSetOfString("x.invalid", "y.invalid"),
    69  	}
    70  	opt := envoyutil.OptAllowedIdentities("y.invalid", "z.invalid")
    71  	opt(ip)
    72  
    73  	expected := &envoyutil.IdentityProcessor{
    74  		AllowedIdentities: collections.NewSetOfString("x.invalid", "y.invalid", "z.invalid"),
    75  	}
    76  	assert.Equal(expected, ip)
    77  }
    78  
    79  func TestOptDeniedIdentities(t *testing.T) {
    80  	assert := sdkAssert.New(t)
    81  
    82  	ip := &envoyutil.IdentityProcessor{
    83  		DeniedIdentities: collections.NewSetOfString("x.invalid", "y.invalid"),
    84  	}
    85  	opt := envoyutil.OptDeniedIdentities("y.invalid", "z.invalid")
    86  	opt(ip)
    87  
    88  	expected := &envoyutil.IdentityProcessor{
    89  		DeniedIdentities: collections.NewSetOfString("x.invalid", "y.invalid", "z.invalid"),
    90  	}
    91  	assert.Equal(expected, ip)
    92  }
    93  
    94  func TestOptFormatIdentity(t *testing.T) {
    95  	assert := sdkAssert.New(t)
    96  
    97  	ip := &envoyutil.IdentityProcessor{
    98  		FormatIdentity: makeMockFormatter("not-here"),
    99  	}
   100  	sentinel := uuid.V4().ToFullString()
   101  	formatter := makeMockFormatter(sentinel)
   102  	opt := envoyutil.OptFormatIdentity(formatter)
   103  	opt(ip)
   104  
   105  	// Can't compare functions for equality, see https://github.com/blend/go-sdk/issues/167
   106  	// so we make sure our function is as expected.
   107  	s, err := ip.FormatIdentity(envoyutil.XFCCElement{}, nil)
   108  	assert.Equal(sentinel, s)
   109  	assert.Nil(err)
   110  }
   111  
   112  func TestIdentityProcessorIdentityProvider(t *testing.T) {
   113  	assert := sdkAssert.New(t)
   114  
   115  	// Empty URI value.
   116  	ip := envoyutil.IdentityProcessor{}
   117  	xfcc := envoyutil.XFCCElement{}
   118  	clientIdentity, err := ip.IdentityProvider(xfcc)
   119  	assert.Equal("", clientIdentity)
   120  	assert.True(envoyutil.IsValidationError(err))
   121  	var expected error = &envoyutil.XFCCValidationError{
   122  		Class: envoyutil.ErrInvalidClientIdentity,
   123  		XFCC:  xfcc.String(),
   124  	}
   125  	assert.Equal(expected, err)
   126  
   127  	// Invalid URI value.
   128  	ip = envoyutil.IdentityProcessor{}
   129  	xfcc = envoyutil.XFCCElement{URI: "spiffe://cluster.local/not-k8s"}
   130  	clientIdentity, err = ip.IdentityProvider(xfcc)
   131  	assert.Equal("", clientIdentity)
   132  	assert.True(envoyutil.IsExtractionError(err))
   133  	expected = &envoyutil.XFCCExtractionError{
   134  		Class: envoyutil.ErrInvalidClientIdentity,
   135  		XFCC:  xfcc.String(),
   136  	}
   137  	assert.Equal(expected, err)
   138  
   139  	// Use explicit `FormatIdentity`.
   140  	ip = envoyutil.IdentityProcessor{
   141  		FormatIdentity: makeMockFormatter("sentinel"),
   142  	}
   143  	xfcc = envoyutil.XFCCElement{
   144  		By:  "spiffe://cluster.local/ns/song/sa/lyric",
   145  		URI: "spiffe://cluster.local/ns/foo/sa/bar",
   146  	}
   147  	clientIdentity, err = ip.IdentityProvider(xfcc)
   148  	assert.Equal("sentinel", clientIdentity)
   149  	assert.Nil(err)
   150  
   151  	// Trust domain in allow list.
   152  	ip = envoyutil.IdentityProcessor{
   153  		AllowedTrustDomains: []string{"cluster.local"},
   154  	}
   155  	clientIdentity, err = ip.IdentityProvider(xfcc)
   156  	assert.Equal("bar.foo", clientIdentity)
   157  	assert.Nil(err)
   158  
   159  	// Trust domain not in allow list.
   160  	ip = envoyutil.IdentityProcessor{
   161  		AllowedTrustDomains: []string{"not-local.invalid"},
   162  	}
   163  	clientIdentity, err = ip.IdentityProvider(xfcc)
   164  	assert.Equal("", clientIdentity)
   165  	assert.True(envoyutil.IsValidationError(err))
   166  	expected = &envoyutil.XFCCValidationError{
   167  		Class:    envoyutil.ErrInvalidClientIdentity,
   168  		XFCC:     xfcc.String(),
   169  		Metadata: map[string]string{"trustDomain": "cluster.local"},
   170  	}
   171  	assert.Equal(expected, err)
   172  
   173  	// Trust domain in deny list.
   174  	ip = envoyutil.IdentityProcessor{
   175  		DeniedTrustDomains: []string{"cluster.local"},
   176  	}
   177  	clientIdentity, err = ip.IdentityProvider(xfcc)
   178  	assert.Equal("", clientIdentity)
   179  	assert.True(envoyutil.IsValidationError(err))
   180  	expected = &envoyutil.XFCCValidationError{
   181  		Class:    envoyutil.ErrInvalidClientIdentity,
   182  		XFCC:     xfcc.String(),
   183  		Metadata: map[string]string{"trustDomain": "cluster.local"},
   184  	}
   185  	assert.Equal(expected, err)
   186  
   187  	// Trust domain not in deny list.
   188  	ip = envoyutil.IdentityProcessor{
   189  		DeniedTrustDomains: []string{"not-local.invalid"},
   190  	}
   191  	clientIdentity, err = ip.IdentityProvider(xfcc)
   192  	assert.Equal("bar.foo", clientIdentity)
   193  	assert.Nil(err)
   194  
   195  	// Client identity not among allow list.
   196  	ip = envoyutil.IdentityProcessor{
   197  		AllowedIdentities: collections.NewSetOfString("ecks.why"),
   198  	}
   199  	clientIdentity, err = ip.IdentityProvider(xfcc)
   200  	assert.Equal("", clientIdentity)
   201  	assert.True(envoyutil.IsValidationError(err))
   202  	expected = &envoyutil.XFCCValidationError{
   203  		Class:    envoyutil.ErrDeniedClientIdentity,
   204  		XFCC:     xfcc.String(),
   205  		Metadata: map[string]string{"clientIdentity": "bar.foo"},
   206  	}
   207  	assert.Equal(expected, err)
   208  
   209  	// Server identity not among allow list.
   210  	ip = envoyutil.IdentityProcessor{
   211  		Type:              envoyutil.ServerIdentity,
   212  		AllowedIdentities: collections.NewSetOfString("ecks.why"),
   213  	}
   214  	serverIdentity, err := ip.IdentityProvider(xfcc)
   215  	assert.Equal("", serverIdentity)
   216  	assert.True(envoyutil.IsValidationError(err))
   217  	expected = &envoyutil.XFCCValidationError{
   218  		Class:    envoyutil.ErrDeniedServerIdentity,
   219  		XFCC:     xfcc.String(),
   220  		Metadata: map[string]string{"serverIdentity": "lyric.song"},
   221  	}
   222  	assert.Equal(expected, err)
   223  
   224  	// Client identity among allow list.
   225  	ip = envoyutil.IdentityProcessor{
   226  		AllowedIdentities: collections.NewSetOfString("ecks.why", "bar.foo"),
   227  	}
   228  	clientIdentity, err = ip.IdentityProvider(xfcc)
   229  	assert.Equal("bar.foo", clientIdentity)
   230  	assert.Nil(err)
   231  
   232  	// Extract server identity.
   233  	ip = envoyutil.IdentityProcessor{
   234  		Type: envoyutil.ServerIdentity,
   235  	}
   236  	serverIdentity, err = ip.IdentityProvider(xfcc)
   237  	assert.Equal("lyric.song", serverIdentity)
   238  	assert.Nil(err)
   239  
   240  	// Server identity is contained in deny list.
   241  	ip = envoyutil.IdentityProcessor{
   242  		Type:             envoyutil.ServerIdentity,
   243  		DeniedIdentities: collections.NewSetOfString("lyric.song"),
   244  	}
   245  	serverIdentity, err = ip.IdentityProvider(xfcc)
   246  	assert.Equal("", serverIdentity)
   247  	assert.True(envoyutil.IsValidationError(err))
   248  	expected = &envoyutil.XFCCValidationError{
   249  		Class:    envoyutil.ErrDeniedServerIdentity,
   250  		XFCC:     xfcc.String(),
   251  		Metadata: map[string]string{"serverIdentity": "lyric.song"},
   252  	}
   253  	assert.Equal(expected, err)
   254  
   255  	// Server identity is **not** contained in deny list.
   256  	ip = envoyutil.IdentityProcessor{
   257  		Type:             envoyutil.ServerIdentity,
   258  		DeniedIdentities: collections.NewSetOfString("not.music"),
   259  	}
   260  	serverIdentity, err = ip.IdentityProvider(xfcc)
   261  	assert.Equal("lyric.song", serverIdentity)
   262  	assert.Nil(err)
   263  
   264  	// Invalid server identity.
   265  	xfcc = envoyutil.XFCCElement{By: "a=b"}
   266  	ip = envoyutil.IdentityProcessor{Type: envoyutil.ServerIdentity}
   267  	serverIdentity, err = ip.IdentityProvider(xfcc)
   268  	assert.Equal("", serverIdentity)
   269  	assert.True(envoyutil.IsExtractionError(err))
   270  	expected = &envoyutil.XFCCExtractionError{
   271  		Class: envoyutil.ErrInvalidServerIdentity,
   272  		XFCC:  xfcc.String(),
   273  	}
   274  	assert.Equal(expected, err)
   275  }
   276  
   277  func TestIdentityProcessorKubernetesIdentityFormatter(t *testing.T) {
   278  	assert := sdkAssert.New(t)
   279  
   280  	xfcc := envoyutil.XFCCElement{By: "anything", URI: "goes"}
   281  
   282  	// Valid identity.
   283  	ip := &envoyutil.IdentityProcessor{}
   284  	pu := &spiffeutil.ParsedURI{WorkloadID: "ns/packets/sa/ketchup"}
   285  	identity, err := ip.KubernetesIdentityFormatter(xfcc, pu)
   286  	assert.Equal("ketchup.packets", identity)
   287  	assert.Nil(err)
   288  
   289  	// Invalid client identity.
   290  	pu = &spiffeutil.ParsedURI{WorkloadID: "not-k8s"}
   291  	clientIdentity, err := ip.KubernetesIdentityFormatter(xfcc, pu)
   292  	assert.Equal("", clientIdentity)
   293  	assert.True(envoyutil.IsExtractionError(err))
   294  	expected := &envoyutil.XFCCExtractionError{
   295  		Class: envoyutil.ErrInvalidClientIdentity,
   296  		XFCC:  xfcc.String(),
   297  	}
   298  	assert.Equal(expected, err)
   299  
   300  	// Invalid server identity.
   301  	ip = &envoyutil.IdentityProcessor{Type: envoyutil.ServerIdentity}
   302  	serverIdentity, err := ip.KubernetesIdentityFormatter(xfcc, pu)
   303  	assert.Equal("", serverIdentity)
   304  	assert.True(envoyutil.IsExtractionError(err))
   305  	expected = &envoyutil.XFCCExtractionError{
   306  		Class: envoyutil.ErrInvalidServerIdentity,
   307  		XFCC:  xfcc.String(),
   308  	}
   309  	assert.Equal(expected, err)
   310  }
   311  
   312  func makeMockFormatter(identity string) envoyutil.IdentityFormatter {
   313  	return func(_ envoyutil.XFCCElement, _ *spiffeutil.ParsedURI) (string, error) {
   314  		return identity, nil
   315  	}
   316  }