[← Where the filter runs](04-where-the-filter-runs.md) · [Module README](../README.md) · [Operating it →](06-operating-it.md) # 5. Wiring it up, and the three ways it silently does nothing ## The bean ```java @Configuration(proxyBeanMethods = false) public class OutboundConfiguration { @Bean InetAddressFilter httpClientInetAddressFilter() { return InetAddressFilter.externalAddresses(); } } ``` `HttpClientAutoConfiguration.httpClientSettings` reads it and folds it into the shared `HttpClientSettings`: ```java InetAddressFilter filter = inetAddressFilter.getIfAvailable(); HttpClientSettings settings = (filter != null) ? HttpClientSettings.defaults().withInetAddressFilter(filter) : HttpClientSettings.defaults(); ``` Note that `HttpClientSettings.defaults()` is the all-null record — the default filter is `null`, not `all()`. ## There is no property for it `HttpClientSettingsProperties` carries `redirects`, `connectTimeout`, `readTimeout`, `cookieHandling` and `ssl`. There is no `spring.http.clients.inet-address-filter`. Configuration is a bean or an explicit `HttpClientSettings`, and nothing else — so it cannot be turned on per environment from a config server, and it cannot be turned off in an incident without a deploy. Plan for that: put the filter behind a `@Profile` or a `@ConditionalOnProperty` yourself if you need a switch. ## Failure 1 — the starter does not bring it `spring-boot-starter-web` alone does **not** put `InetAddressFilter` on the classpath, and does not give you an auto-configured `RestClient.Builder` either. Boot 4 split the HTTP client modules apart. The compile error is the good outcome: ``` cannot find symbol symbol: class FilteredHostException ``` Add `spring-boot-starter-restclient` (or `-webclient`), which pulls in `spring-boot-restclient` and through it `spring-boot-http-client`. This module's [`pom.xml`](../pom.xml) does exactly that. ## Failure 2 — a client you built yourself The filter reaches auto-configured builders. A `RestClient.create()` or a `new RestTemplate()` written inside your own class is not one, and no bean will change it. That is why [`LinkPreviewController`](../src/main/java/com/ankurm/ssrf/LinkPreviewController.java) takes `RestClient.Builder` in its constructor. For a hand-built client, apply the filter yourself: ```java HttpClientSettings settings = HttpClientSettings.defaults() .withInetAddressFilter(InetAddressFilter.externalAddresses()); ClientHttpRequestFactory factory = ClientHttpRequestFactoryBuilder.jdk().build(settings); ``` And note what is still not covered: anything that opens a socket without going through a Spring HTTP client. A JDBC URL, a raw `URL.openStream()`, an SDK with its own transport, a `ProcessBuilder` running `curl`. `InetAddressFilter` is a control on Spring's HTTP clients, not an egress policy for the JVM. If you need the latter, it belongs in the network. ## Failure 3 — two beans, and a diagnostic that blames the wrong thing `getIfAvailable()` is not "pick one". Two `InetAddressFilter` beans and the context does not start — see [`docs/output/two-filter-beans.txt`](output/two-filter-beans.txt): ``` No qualifying bean of type 'org.springframework.boot.http.client.InetAddressFilter' available: expected single matching bean but found 2: firstFilter,secondFilter ``` but the framed message Boot prints underneath names something four levels away: ``` Description: Parameter 0 of method restClientBuilder in ...RestClientAutoConfiguration required a single bean, but 2 were found: ``` The words `InetAddressFilter` do not appear in the part everyone reads. If you are merging two starters or two shared config modules, this failure will look like a `RestClient` problem. [Operating it →](06-operating-it.md)