@@ -23,6 +23,7 @@ import (
2323
2424 "github.com/prometheus/client_golang/prometheus"
2525 "github.com/prometheus/client_golang/prometheus/testutil"
26+ "github.com/spf13/viper"
2627 "github.com/stretchr/testify/assert"
2728 "github.com/stretchr/testify/require"
2829 corev1 "k8s.io/api/core/v1"
@@ -134,3 +135,77 @@ func TestNewVaultClientMetrics(t *testing.T) {
134135 })
135136 }
136137}
138+
139+ func TestNewVaultClientRejectsObjectAddr (t * testing.T ) {
140+ logger := slog .New (slog .DiscardHandler )
141+ require .NoError (t , os .Setenv ("KUBERNETES_NAMESPACE" , "test-namespace" ))
142+
143+ tests := []struct {
144+ name string
145+ addr string
146+ allowlist string
147+ wantErr bool
148+ }{
149+ {
150+ name : "PoC IMDS address from object annotation is rejected" ,
151+ addr : "http://169.254.169.254/latest/meta-data/" ,
152+ wantErr : true ,
153+ },
154+ {
155+ name : "non-allowlisted external address from object annotation is rejected" ,
156+ addr : "https://evil.attacker.com" ,
157+ wantErr : true ,
158+ },
159+ {
160+ name : "userinfo-bearing address from object annotation is rejected" ,
161+ addr : "https://attacker:pw@vault.prod.svc:8200" ,
162+ allowlist : "https://vault.prod.svc:8200" ,
163+ wantErr : true ,
164+ },
165+ {
166+ name : "allowlisted address from object annotation passes validation" ,
167+ addr : "https://vault.prod.svc:8200" ,
168+ allowlist : "https://vault.prod.svc:8200" ,
169+ wantErr : false ,
170+ },
171+ }
172+
173+ for _ , tt := range tests {
174+ t .Run (tt .name , func (t * testing.T ) {
175+ vaultAuthAttemptsCount .Reset ()
176+ vaultAuthAttemptsErrorsCount .Reset ()
177+ viper .Set ("vault_addr_allowlist" , tt .allowlist )
178+ t .Cleanup (viper .Reset )
179+
180+ mw , err := NewMutatingWebhook (logger , fake .NewClientset ())
181+ require .NoError (t , err )
182+
183+ vaultConfig := VaultConfig {
184+ Addr : tt .addr ,
185+ AddrFromObject : true ,
186+ SkipVerify : true ,
187+ Role : "test-role" ,
188+ Path : "kubernetes" ,
189+ VaultServiceAccount : "high-priv-sa" ,
190+ ObjectNamespace : "test-namespace" ,
191+ }
192+
193+ _ , err = mw .newVaultClient (t .Context (), vaultConfig )
194+
195+ if tt .wantErr {
196+ require .Error (t , err )
197+ assert .Contains (t , err .Error (), "rejected Vault address from object annotation" )
198+ assert .Equal (t , float64 (1 ), testutil .ToFloat64 (vaultAuthAttemptsErrorsCount .WithLabelValues ("config_error" )),
199+ "address rejection must record a config_error" )
200+ assert .Equal (t , float64 (0 ), testutil .ToFloat64 (vaultAuthAttemptsErrorsCount .WithLabelValues ("kubernetes_error" )),
201+ "rejection must happen before the ServiceAccount token path" )
202+ } else {
203+ if err != nil {
204+ assert .NotContains (t , err .Error (), "rejected Vault address from object annotation" )
205+ }
206+ assert .Equal (t , float64 (0 ), testutil .ToFloat64 (vaultAuthAttemptsErrorsCount .WithLabelValues ("config_error" )),
207+ "a valid allowlisted address must not record a config_error" )
208+ }
209+ })
210+ }
211+ }
0 commit comments