github.com/kiali/kiali@v1.84.0/business/checkers/authorization/no_host_checker_test.go (about)

     1  package authorization
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/assert"
     7  	networking_v1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1"
     8  	security_v1beta "istio.io/client-go/pkg/apis/security/v1beta1"
     9  
    10  	"github.com/kiali/kiali/kubernetes"
    11  	"github.com/kiali/kiali/models"
    12  	"github.com/kiali/kiali/tests/data"
    13  	"github.com/kiali/kiali/tests/testutils/validations"
    14  )
    15  
    16  func TestPresentService(t *testing.T) {
    17  	assert := assert.New(t)
    18  
    19  	registryService1 := data.CreateFakeRegistryServices("details.bookinfo.svc.cluster.local", "bookinfo", "*")
    20  	registryService2 := data.CreateFakeRegistryServices("reviews.bookinfo.svc.cluster.local", "bookinfo", "*")
    21  
    22  	validations, valid := NoHostChecker{
    23  		AuthorizationPolicy: authPolicyWithHost([]string{"details", "reviews"}),
    24  		Namespaces:          models.Namespaces{models.Namespace{Name: "outside"}, models.Namespace{Name: "bookinfo"}},
    25  		ServiceEntries:      map[string][]string{},
    26  		RegistryServices:    append(registryService1, registryService2...),
    27  		PolicyAllowAny:      true,
    28  	}.Check()
    29  
    30  	// Well configured object
    31  	assert.True(valid)
    32  	assert.Empty(validations)
    33  }
    34  
    35  func TestNonExistingService(t *testing.T) {
    36  	assert := assert.New(t)
    37  
    38  	registryService1 := data.CreateFakeRegistryServices("details.bookinfo.svc.cluster.local", "bookinfo", "*")
    39  	registryService2 := data.CreateFakeRegistryServices("reviews.bookinfo.svc.cluster.local", "bookinfo", "*")
    40  
    41  	vals, valid := NoHostChecker{
    42  		AuthorizationPolicy: authPolicyWithHost([]string{"details", "wrong"}),
    43  		Namespaces:          models.Namespaces{models.Namespace{Name: "outside"}, models.Namespace{Name: "bookinfo"}},
    44  		ServiceEntries:      map[string][]string{},
    45  		RegistryServices:    append(registryService1, registryService2...),
    46  		PolicyAllowAny:      true,
    47  	}.Check()
    48  
    49  	// Wrong host is not present
    50  	assert.False(valid)
    51  	assert.NotEmpty(vals)
    52  	assert.Len(vals, 1)
    53  	assert.Equal(models.WarningSeverity, vals[0].Severity)
    54  	assert.NoError(validations.ConfirmIstioCheckMessage("authorizationpolicy.nodest.matchingregistry", vals[0]))
    55  	assert.Equal("spec/rules[0]/to[0]/operation/hosts[1]", vals[0].Path)
    56  }
    57  
    58  func TestWildcardHost(t *testing.T) {
    59  	assert := assert.New(t)
    60  
    61  	registryService1 := data.CreateFakeRegistryServices("details.bookinfo.svc.cluster.local", "bookinfo", "*")
    62  	registryService2 := data.CreateFakeRegistryServices("reviews.bookinfo.svc.cluster.local", "bookinfo", "*")
    63  
    64  	vals, valid := NoHostChecker{
    65  		AuthorizationPolicy: authPolicyWithHost([]string{"*", "*.bookinfo", "*.bookinfo.svc.cluster.local"}),
    66  		Namespaces:          models.Namespaces{models.Namespace{Name: "outside"}, models.Namespace{Name: "bookinfo"}},
    67  		ServiceEntries:      map[string][]string{},
    68  		RegistryServices:    append(registryService1, registryService2...),
    69  	}.Check()
    70  
    71  	// Well configured object
    72  	assert.True(valid)
    73  	assert.Empty(vals)
    74  }
    75  
    76  func TestWildcardHostOutsideNamespace(t *testing.T) {
    77  	assert := assert.New(t)
    78  
    79  	registryService1 := data.CreateFakeRegistryServices("details.bookinfo.svc.cluster.local", "bookinfo", "*")
    80  	registryService2 := data.CreateFakeRegistryServices("reviews.bookinfo.svc.cluster.local", "bookinfo", "*")
    81  
    82  	vals, valid := NoHostChecker{
    83  		AuthorizationPolicy: authPolicyWithHost([]string{"*.outside", "*.outside.svc.cluster.local"}),
    84  		Namespaces:          models.Namespaces{models.Namespace{Name: "outside"}, models.Namespace{Name: "bookinfo"}},
    85  		ServiceEntries:      map[string][]string{},
    86  		RegistryServices:    append(registryService1, registryService2...),
    87  	}.Check()
    88  
    89  	assert.False(valid)
    90  	assert.NotEmpty(vals)
    91  	assert.Len(vals, 2)
    92  	assert.Equal(models.ErrorSeverity, vals[0].Severity)
    93  	assert.NoError(validations.ConfirmIstioCheckMessage("authorizationpolicy.nodest.matchingregistry", vals[0]))
    94  	assert.Equal("spec/rules[0]/to[0]/operation/hosts[0]", vals[0].Path)
    95  	assert.Equal(models.ErrorSeverity, vals[1].Severity)
    96  	assert.NoError(validations.ConfirmIstioCheckMessage("authorizationpolicy.nodest.matchingregistry", vals[1]))
    97  	assert.Equal("spec/rules[0]/to[0]/operation/hosts[1]", vals[1].Path)
    98  }
    99  
   100  func TestServiceEntryPresent(t *testing.T) {
   101  	assert := assert.New(t)
   102  
   103  	serviceEntry := data.CreateExternalServiceEntry()
   104  
   105  	validations, valid := NoHostChecker{
   106  		AuthorizationPolicy: authPolicyWithHost([]string{"wikipedia.org"}),
   107  		Namespaces:          models.Namespaces{models.Namespace{Name: "outside"}, models.Namespace{Name: "bookinfo"}},
   108  		ServiceEntries:      kubernetes.ServiceEntryHostnames([]*networking_v1beta1.ServiceEntry{serviceEntry}),
   109  	}.Check()
   110  
   111  	// Well configured object
   112  	assert.True(valid)
   113  	assert.Empty(validations)
   114  }
   115  
   116  func TestExportedInternalServiceEntryPresent(t *testing.T) {
   117  	assert := assert.New(t)
   118  
   119  	serviceEntry := data.CreateEmptyMeshInternalServiceEntry("details-se", "bookinfo3", []string{"details.bookinfo2.svc.cluster.local"})
   120  
   121  	validations, valid := NoHostChecker{
   122  		AuthorizationPolicy: authPolicyWithHost([]string{"details.bookinfo2.svc.cluster.local"}),
   123  		Namespaces:          models.Namespaces{models.Namespace{Name: "bookinfo"}, models.Namespace{Name: "bookinfo2"}, models.Namespace{Name: "bookinfo3"}},
   124  		ServiceEntries:      kubernetes.ServiceEntryHostnames([]*networking_v1beta1.ServiceEntry{serviceEntry}),
   125  	}.Check()
   126  
   127  	// Well configured object
   128  	assert.True(valid)
   129  	assert.Empty(validations)
   130  }
   131  
   132  func TestExportedExternalServiceEntryPresent(t *testing.T) {
   133  	assert := assert.New(t)
   134  
   135  	serviceEntry := data.CreateEmptyMeshExternalServiceEntry("details-se", "bookinfo3", []string{"www.myhost.com"})
   136  
   137  	validations, valid := NoHostChecker{
   138  		AuthorizationPolicy: authPolicyWithHost([]string{"www.myhost.com"}),
   139  		Namespaces:          models.Namespaces{models.Namespace{Name: "bookinfo"}, models.Namespace{Name: "bookinfo2"}, models.Namespace{Name: "bookinfo3"}},
   140  		ServiceEntries:      kubernetes.ServiceEntryHostnames([]*networking_v1beta1.ServiceEntry{serviceEntry}),
   141  	}.Check()
   142  
   143  	// Well configured object
   144  	assert.True(valid)
   145  	assert.Empty(validations)
   146  }
   147  
   148  func TestExportedExternalServiceEntryFail(t *testing.T) {
   149  	assert := assert.New(t)
   150  
   151  	serviceEntry := data.CreateEmptyMeshExternalServiceEntry("details-se", "bookinfo3", []string{"www.myhost.com"})
   152  
   153  	vals, valid := NoHostChecker{
   154  		AuthorizationPolicy: authPolicyWithHost([]string{"www.wrong.com"}),
   155  		Namespaces:          models.Namespaces{models.Namespace{Name: "bookinfo"}, models.Namespace{Name: "bookinfo2"}, models.Namespace{Name: "bookinfo3"}},
   156  		ServiceEntries:      kubernetes.ServiceEntryHostnames([]*networking_v1beta1.ServiceEntry{serviceEntry}),
   157  	}.Check()
   158  
   159  	// www.wrong.com host is not present
   160  	assert.False(valid)
   161  	assert.NotEmpty(vals)
   162  	assert.Len(vals, 1)
   163  	assert.Equal(models.ErrorSeverity, vals[0].Severity)
   164  	assert.NoError(validations.ConfirmIstioCheckMessage("authorizationpolicy.nodest.matchingregistry", vals[0]))
   165  	assert.Equal("spec/rules[0]/to[0]/operation/hosts[0]", vals[0].Path)
   166  }
   167  
   168  func TestWildcardExportedInternalServiceEntryPresent(t *testing.T) {
   169  	assert := assert.New(t)
   170  
   171  	serviceEntry := data.CreateEmptyMeshInternalServiceEntry("details-se", "bookinfo3", []string{"*.bookinfo2.svc.cluster.local"})
   172  
   173  	validations, valid := NoHostChecker{
   174  		AuthorizationPolicy: authPolicyWithHost([]string{"details.bookinfo2.svc.cluster.local"}),
   175  		Namespaces:          models.Namespaces{models.Namespace{Name: "bookinfo"}, models.Namespace{Name: "bookinfo2"}, models.Namespace{Name: "bookinfo3"}},
   176  		ServiceEntries:      kubernetes.ServiceEntryHostnames([]*networking_v1beta1.ServiceEntry{serviceEntry}),
   177  	}.Check()
   178  
   179  	// Well configured object
   180  	assert.True(valid)
   181  	assert.Empty(validations)
   182  }
   183  
   184  func TestWildcardExportedInternalServiceEntryFail(t *testing.T) {
   185  	assert := assert.New(t)
   186  
   187  	serviceEntry := data.CreateEmptyMeshInternalServiceEntry("details-se", "bookinfo3", []string{"details.bookinfo2.svc.cluster.local"})
   188  
   189  	vals, valid := NoHostChecker{
   190  		AuthorizationPolicy: authPolicyWithHost([]string{"details.bookinfo3.svc.cluster.local"}),
   191  		Namespaces:          models.Namespaces{models.Namespace{Name: "bookinfo"}, models.Namespace{Name: "bookinfo2"}, models.Namespace{Name: "bookinfo3"}},
   192  		ServiceEntries:      kubernetes.ServiceEntryHostnames([]*networking_v1beta1.ServiceEntry{serviceEntry}),
   193  	}.Check()
   194  
   195  	// details.bookinfo3.svc.cluster.local host is not present
   196  	assert.False(valid)
   197  	assert.NotEmpty(vals)
   198  	assert.Len(vals, 1)
   199  	assert.Equal(models.ErrorSeverity, vals[0].Severity)
   200  	assert.NoError(validations.ConfirmIstioCheckMessage("authorizationpolicy.nodest.matchingregistry", vals[0]))
   201  	assert.Equal("spec/rules[0]/to[0]/operation/hosts[0]", vals[0].Path)
   202  }
   203  
   204  func TestExportedNonFQDNInternalServiceEntryFail(t *testing.T) {
   205  	assert := assert.New(t)
   206  
   207  	serviceEntry := data.CreateEmptyMeshInternalServiceEntry("details-se", "bookinfo3", []string{"details"})
   208  
   209  	vals, valid := NoHostChecker{
   210  		AuthorizationPolicy: authPolicyWithHost([]string{"details.bookinfo2.svc.cluster.local"}),
   211  		Namespaces:          models.Namespaces{models.Namespace{Name: "bookinfo"}, models.Namespace{Name: "bookinfo2"}, models.Namespace{Name: "bookinfo3"}},
   212  		ServiceEntries:      kubernetes.ServiceEntryHostnames([]*networking_v1beta1.ServiceEntry{serviceEntry}),
   213  	}.Check()
   214  
   215  	// details.bookinfo2.svc.cluster.local host is not present
   216  	assert.False(valid)
   217  	assert.NotEmpty(vals)
   218  	assert.Len(vals, 1)
   219  	assert.Equal(models.ErrorSeverity, vals[0].Severity)
   220  	assert.NoError(validations.ConfirmIstioCheckMessage("authorizationpolicy.nodest.matchingregistry", vals[0]))
   221  	assert.Equal("spec/rules[0]/to[0]/operation/hosts[0]", vals[0].Path)
   222  }
   223  
   224  func TestServiceEntryNotPresent(t *testing.T) {
   225  	assert := assert.New(t)
   226  
   227  	serviceEntry := data.CreateExternalServiceEntry()
   228  	vals, valid := NoHostChecker{
   229  		AuthorizationPolicy: authPolicyWithHost([]string{"wrong.org"}),
   230  		Namespaces:          models.Namespaces{models.Namespace{Name: "outside"}, models.Namespace{Name: "bookinfo"}},
   231  		ServiceEntries:      kubernetes.ServiceEntryHostnames([]*networking_v1beta1.ServiceEntry{serviceEntry}),
   232  	}.Check()
   233  
   234  	// Wrong.org host is not present
   235  	assert.False(valid)
   236  	assert.NotEmpty(vals)
   237  	assert.Len(vals, 1)
   238  	assert.Equal(models.ErrorSeverity, vals[0].Severity)
   239  	assert.NoError(validations.ConfirmIstioCheckMessage("authorizationpolicy.nodest.matchingregistry", vals[0]))
   240  	assert.Equal("spec/rules[0]/to[0]/operation/hosts[0]", vals[0].Path)
   241  }
   242  
   243  func TestExportedInternalServiceEntryNotPresent(t *testing.T) {
   244  	assert := assert.New(t)
   245  
   246  	serviceEntry := data.CreateEmptyMeshInternalServiceEntry("details-se", "bookinfo3", []string{"details.bookinfo2.svc.cluster.local"})
   247  	vals, valid := NoHostChecker{
   248  		AuthorizationPolicy: authPolicyWithHost([]string{"wrong.bookinfo2.svc.cluster.local"}),
   249  		Namespaces:          models.Namespaces{models.Namespace{Name: "bookinfo"}, models.Namespace{Name: "bookinfo2"}, models.Namespace{Name: "bookinfo3"}},
   250  		ServiceEntries:      kubernetes.ServiceEntryHostnames([]*networking_v1beta1.ServiceEntry{serviceEntry}),
   251  	}.Check()
   252  
   253  	// Wrong.org host is not present
   254  	assert.False(valid)
   255  	assert.NotEmpty(vals)
   256  	assert.Len(vals, 1)
   257  	assert.Equal(models.ErrorSeverity, vals[0].Severity)
   258  	assert.NoError(validations.ConfirmIstioCheckMessage("authorizationpolicy.nodest.matchingregistry", vals[0]))
   259  	assert.Equal("spec/rules[0]/to[0]/operation/hosts[0]", vals[0].Path)
   260  }
   261  
   262  func TestVirtualServicePresent(t *testing.T) {
   263  	assert := assert.New(t)
   264  
   265  	virtualService := *data.CreateEmptyVirtualService("foo-dev", "foo", []string{"foo-dev.example.com"})
   266  	validations, valid := NoHostChecker{
   267  		AuthorizationPolicy: authPolicyWithHost([]string{"foo-dev.example.com"}),
   268  		Namespaces:          models.Namespaces{models.Namespace{Name: "outside"}, models.Namespace{Name: "bookinfo"}},
   269  		ServiceEntries:      map[string][]string{},
   270  		VirtualServices:     []*networking_v1beta1.VirtualService{&virtualService},
   271  	}.Check()
   272  
   273  	assert.True(valid)
   274  	assert.Empty(validations)
   275  }
   276  
   277  func TestVirtualServiceNotPresent(t *testing.T) {
   278  	assert := assert.New(t)
   279  
   280  	virtualService := *data.CreateEmptyVirtualService("foo-dev", "foo", []string{"foo-dev.example.com"})
   281  	vals, valid := NoHostChecker{
   282  		AuthorizationPolicy: authPolicyWithHost([]string{"foo-bogus.example.com"}),
   283  		Namespaces:          models.Namespaces{models.Namespace{Name: "outside"}, models.Namespace{Name: "bookinfo"}},
   284  		ServiceEntries:      map[string][]string{},
   285  		VirtualServices:     []*networking_v1beta1.VirtualService{&virtualService},
   286  	}.Check()
   287  
   288  	// Wrong.org host is not present
   289  	assert.False(valid)
   290  	assert.NotEmpty(vals)
   291  	assert.Len(vals, 1)
   292  	assert.Equal(models.ErrorSeverity, vals[0].Severity)
   293  	assert.NoError(validations.ConfirmIstioCheckMessage("authorizationpolicy.nodest.matchingregistry", vals[0]))
   294  	assert.Equal("spec/rules[0]/to[0]/operation/hosts[0]", vals[0].Path)
   295  }
   296  
   297  func TestWildcardServiceEntryHost(t *testing.T) {
   298  	assert := assert.New(t)
   299  
   300  	serviceEntry := *data.CreateEmptyMeshExternalServiceEntry("googlecard", "google", []string{"*.google.com"})
   301  
   302  	vals, valid := NoHostChecker{
   303  		AuthorizationPolicy: authPolicyWithHost([]string{"maps.google.com"}),
   304  		Namespaces:          models.Namespaces{models.Namespace{Name: "outside"}, models.Namespace{Name: "bookinfo"}},
   305  		ServiceEntries:      kubernetes.ServiceEntryHostnames([]*networking_v1beta1.ServiceEntry{&serviceEntry}),
   306  	}.Check()
   307  
   308  	// Well configured object
   309  	assert.True(valid)
   310  	assert.Empty(vals)
   311  
   312  	// Not matching
   313  	vals, valid = NoHostChecker{
   314  		AuthorizationPolicy: authPolicyWithHost([]string{"maps.apple.com"}),
   315  		Namespaces:          models.Namespaces{models.Namespace{Name: "outside"}, models.Namespace{Name: "bookinfo"}},
   316  		ServiceEntries:      kubernetes.ServiceEntryHostnames([]*networking_v1beta1.ServiceEntry{&serviceEntry}),
   317  		PolicyAllowAny:      true,
   318  	}.Check()
   319  
   320  	// apple.com host is not present
   321  	assert.False(valid)
   322  	assert.NotEmpty(vals)
   323  	assert.Len(vals, 1)
   324  	assert.Equal(models.WarningSeverity, vals[0].Severity)
   325  	assert.NoError(validations.ConfirmIstioCheckMessage("authorizationpolicy.nodest.matchingregistry", vals[0]))
   326  	assert.Equal("spec/rules[0]/to[0]/operation/hosts[0]", vals[0].Path)
   327  }
   328  
   329  func authPolicyWithHost(hostList []string) *security_v1beta.AuthorizationPolicy {
   330  	methods := []string{"GET", "PUT", "PATCH"}
   331  	nss := []string{"bookinfo"}
   332  	selector := map[string]string{"app": "details", "version": "v1"}
   333  	return data.CreateAuthorizationPolicy(nss, methods, hostList, selector)
   334  }
   335  
   336  func TestValidServiceRegistry(t *testing.T) {
   337  	assert := assert.New(t)
   338  
   339  	validations, valid := NoHostChecker{
   340  		AuthorizationPolicy: authPolicyWithHost([]string{"ratings.mesh2-bookinfo.svc.mesh1-imports.local"}),
   341  		Namespaces:          models.Namespaces{models.Namespace{Name: "outside"}, models.Namespace{Name: "bookinfo"}},
   342  	}.Check()
   343  
   344  	assert.False(valid)
   345  	assert.NotEmpty(validations)
   346  
   347  	registryService := data.CreateFakeRegistryServices("ratings.mesh2-bookinfo.svc.mesh1-imports.local", "bookinfo", "*")
   348  
   349  	validations, valid = NoHostChecker{
   350  		AuthorizationPolicy: authPolicyWithHost([]string{"ratings.mesh2-bookinfo.svc.mesh1-imports.local"}),
   351  		Namespaces:          models.Namespaces{models.Namespace{Name: "outside"}, models.Namespace{Name: "bookinfo"}},
   352  		RegistryServices:    registryService,
   353  	}.Check()
   354  
   355  	assert.True(valid)
   356  	assert.Empty(validations)
   357  
   358  	registryService = data.CreateFakeRegistryServices("ratings2.mesh2-bookinfo.svc.mesh1-imports.local", "bookinfo", "*")
   359  
   360  	validations, valid = NoHostChecker{
   361  		AuthorizationPolicy: authPolicyWithHost([]string{"ratings.mesh2-bookinfo.svc.mesh1-imports.local"}),
   362  		Namespaces:          models.Namespaces{models.Namespace{Name: "outside"}, models.Namespace{Name: "bookinfo"}},
   363  		RegistryServices:    registryService,
   364  	}.Check()
   365  
   366  	assert.False(valid)
   367  	assert.NotEmpty(validations)
   368  
   369  	registryService = data.CreateFakeRegistryServices("ratings.bookinfo.svc.cluster.local", "bookinfo", "*")
   370  
   371  	validations, valid = NoHostChecker{
   372  		AuthorizationPolicy: authPolicyWithHost([]string{"ratings.bookinfo.svc.cluster.local"}),
   373  		Namespaces:          models.Namespaces{models.Namespace{Name: "outside"}, models.Namespace{Name: "bookinfo"}},
   374  		RegistryServices:    registryService,
   375  	}.Check()
   376  
   377  	assert.True(valid)
   378  	assert.Empty(validations)
   379  
   380  	registryService = data.CreateFakeRegistryServices("ratings.bookinfo.svc.cluster.local", "bookinfo", "*")
   381  
   382  	validations, valid = NoHostChecker{
   383  		AuthorizationPolicy: authPolicyWithHost([]string{"ratings2.bookinfo.svc.cluster.local"}),
   384  		Namespaces:          models.Namespaces{models.Namespace{Name: "outside"}, models.Namespace{Name: "bookinfo"}},
   385  		RegistryServices:    registryService,
   386  	}.Check()
   387  
   388  	assert.False(valid)
   389  	assert.NotEmpty(validations)
   390  }