Skip to content

Commit 3ce458e

Browse files
steveiliop56contre95claudecodex
authored
refactor: use modular approach for k8s extractors (#1154)
Co-authored-by: Contre <lucascontre@proton.me> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Codex <noreply@openai.com>
1 parent 4341445 commit 3ce458e

8 files changed

Lines changed: 641 additions & 814 deletions

‎.coderabbit.yaml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# yaml-language-server: $schema=https://www.coderabbit.ai/integrations/schema.v2.json
12
issue_enrichment:
23
auto_enrich:
34
enabled: false
5+
reviews:
6+
profile: chill

‎go.mod‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ require (
2828
golang.org/x/oauth2 v0.36.0
2929
golang.org/x/tools v0.49.0
3030
gopkg.in/yaml.v3 v3.0.1
31+
k8s.io/api v0.37.0
3132
k8s.io/apimachinery v0.37.0
3233
k8s.io/client-go v0.37.0
3334
modernc.org/sqlite v1.58.0
@@ -81,6 +82,7 @@ require (
8182
github.com/go-playground/validator/v10 v10.30.1 // indirect
8283
github.com/goccy/go-json v0.10.5 // indirect
8384
github.com/goccy/go-yaml v1.19.2 // indirect
85+
github.com/google/gnostic-models v0.7.1 // indirect
8486
github.com/huandu/xstrings v1.5.0 // indirect
8587
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa // indirect
8688
github.com/jackc/pgpassfile v1.0.0 // indirect

‎go.sum‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,8 @@ github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63Y
168168
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
169169
github.com/golang-migrate/migrate/v4 v4.19.1 h1:OCyb44lFuQfYXYLx1SCxPZQGU7mcaZ7gH9yH4jSFbBA=
170170
github.com/golang-migrate/migrate/v4 v4.19.1/go.mod h1:CTcgfjxhaUtsLipnLoQRWCrjYXycRz/g5+RWDuYgPrE=
171-
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
172-
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
171+
github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c=
172+
github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
173173
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
174174
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
175175
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=

‎internal/service/access_controls_service.go‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func NewAccessControlsService(i AccessControlServiceInput) *AccessControlsServic
4242
}
4343
}
4444

45-
func (service *AccessControlsService) ensureAscii(str string) bool {
45+
func ensureAscii(str string) bool {
4646
for i := 0; i < len(str); i++ {
4747
if str[i] > unicode.MaxASCII {
4848
return false
@@ -51,7 +51,7 @@ func (service *AccessControlsService) ensureAscii(str string) bool {
5151
return true
5252
}
5353

54-
func (service *AccessControlsService) normalizeDomain(domain string) string {
54+
func normalizeDomain(domain string) string {
5555
if host, _, err := net.SplitHostPort(domain); err == nil {
5656
domain = host
5757
}
@@ -60,11 +60,11 @@ func (service *AccessControlsService) normalizeDomain(domain string) string {
6060
}
6161

6262
func (service *AccessControlsService) getACLs(domain string, lookup func(locator func(name string, app *model.App) bool) error) (*model.App, error) {
63-
if !service.ensureAscii(domain) {
63+
if !ensureAscii(domain) {
6464
return nil, errors.New("domain contains non-ascii characters")
6565
}
6666

67-
normalizedDomain := service.normalizeDomain(domain)
67+
normalizedDomain := normalizeDomain(domain)
6868

6969
if !strings.HasSuffix(normalizedDomain, "."+service.runtime.CookieDomain) && normalizedDomain != service.runtime.CookieDomain {
7070
return nil, fmt.Errorf("domain does not match cookie domain, expected %s (or a subdomain), got %s", service.runtime.CookieDomain, domain)
@@ -76,11 +76,11 @@ func (service *AccessControlsService) getACLs(domain string, lookup func(locator
7676

7777
locatorFunc := func(name string, app *model.App) bool {
7878
if app.Config.Domain != "" {
79-
if !service.ensureAscii(app.Config.Domain) {
79+
if !ensureAscii(app.Config.Domain) {
8080
service.log.App.Warn().Str("name", name).Str("domain", app.Config.Domain).Msg("Domain contains non-ascii characters, skipping")
8181
return false
8282
}
83-
if normalizedDomain == service.normalizeDomain(app.Config.Domain) {
83+
if normalizedDomain == normalizeDomain(app.Config.Domain) {
8484
service.log.App.Debug().Str("name", name).Msg("Found matching container by domain")
8585
domainMatch = app
8686
return true

‎internal/service/access_controls_service_test.go‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,10 @@ func TestAccessControlsService(t *testing.T) {
186186
// get acls should return an error when the provider fails
187187
mock := newMockProvider(map[string]model.App{}, true)
188188
acls := NewAccessControlsService(AccessControlServiceInput{
189-
Log: log,
190-
Runtime: &runtime,
191-
Config: &model.Config{},
189+
Log: log,
190+
Runtime: &runtime,
191+
Config: &model.Config{},
192+
LabelProvider: mock,
192193
})
193194
_, err := acls.getACLs("example.com", mock.Lookup)
194195
assert.Error(t, err)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package service
2+
3+
import (
4+
"slices"
5+
6+
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
7+
networking "k8s.io/api/networking/v1"
8+
)
9+
10+
type KubernetesIngressExtractor struct {
11+
log *logger.Logger
12+
}
13+
14+
type KubernetesIngressExtractorInput struct {
15+
Log *logger.Logger
16+
}
17+
18+
func NewKubernetesIngressExtractor(i KubernetesIngressExtractorInput) *KubernetesIngressExtractor {
19+
return &KubernetesIngressExtractor{
20+
log: i.Log,
21+
}
22+
}
23+
24+
func (k *KubernetesIngressExtractor) getPaths(rule networking.IngressRule) []string {
25+
var paths []string
26+
27+
if rule.HTTP == nil {
28+
return paths
29+
}
30+
31+
for _, path := range rule.HTTP.Paths {
32+
paths = append(paths, path.Path)
33+
}
34+
35+
return paths
36+
}
37+
38+
func (k *KubernetesIngressExtractor) getHosts(rules []networking.IngressRule) []string {
39+
var hosts []string
40+
41+
for _, rule := range rules {
42+
hosts = append(hosts, rule.Host)
43+
paths := k.getPaths(rule)
44+
45+
if len(paths) == 0 {
46+
continue
47+
}
48+
49+
if !slices.Contains(paths, "/") {
50+
k.log.App.Warn().Strs("hosts", hosts).Strs("paths", paths).Msg("Ingress rule does not contain a catch-all path, another ingress may be able to bypass auth checks if it routes the same host with a different path. Consider adding a catch-all path to this rule to ensure auth checks are applied to all paths for this host.")
51+
}
52+
}
53+
54+
return hosts
55+
}
56+
57+
func (k *KubernetesIngressExtractor) Extract(ingress *networking.Ingress) *ExtractionResult {
58+
annotations := ingress.GetAnnotations()
59+
hosts := k.getHosts(ingress.Spec.Rules)
60+
61+
return &ExtractionResult{
62+
typ: ResourceTypeIngress,
63+
name: ingress.GetName(),
64+
namespace: ingress.GetNamespace(),
65+
hosts: hosts,
66+
annotations: annotations,
67+
}
68+
}

0 commit comments

Comments
 (0)