Skip to content

Commit

Permalink
docker: changes to direct auth filter in zipkin-eureka test image (#3699
Browse files Browse the repository at this point in the history
)

Formerly, we didn't test the authenticated side, so some review drift
passed even though authenticating would crash on startup:

```
Parameter 0 of method filterChain in zipkin.test.EurekaSecurity required a bean of type 'org.springframework.security.config.annotation.web.builders.HttpSecurity' that could not be found.
```

This uses a simple auth filter instead of spring-security which also causes
csrf and cors handling as well special handling of actuator used int the
health check.

Signed-off-by: Adrian Cole <adrian@tetrate.io>
  • Loading branch information
codefromthecrypt authored Jan 23, 2024
1 parent 5ff651f commit 10e0458
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 39 deletions.
32 changes: 32 additions & 0 deletions build-bin/docker-compose-zipkin-eureka.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#
# Copyright 2015-2024 The OpenZipkin Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
# or implied. See the License for the specific language governing permissions and limitations under
# the License.
#

# uses 2.4 so we can use condition: service_healthy
version: "2.4"

# Test both authenticated and unauthenticated, as if there is a Spring problem,
# the latter will crash. We only need to use HEALTHCHECK for this.
services:
eureka:
image: openzipkin/zipkin-eureka:test
container_name: eureka
sut:
image: openzipkin/zipkin-eureka:test
container_name: sut
environment:
EUREKA_USERNAME: testuser
EUREKA_PASSWORD: testpassword
depends_on:
eureka:
condition: service_healthy
9 changes: 7 additions & 2 deletions docker/test-images/zipkin-eureka/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
The `zipkin-eureka` testing image runs Eureka Server for service discovery
integration of the Zipkin server. This listens on port 8761.

Besides norms defined in [docker-java](https://github.com/openzipkin/docker-java), this accepts the
following environment variables:

* `EUREKA_USERNAME`: username for authenticating endpoints under "/eureka".
* `EUREKA_PASSWORD`: password for authenticating endpoints under "/eureka".
* `JAVA_OPTS`: to change settings such as heap size for Eureka.

To build `openzipkin/zipkin-eureka:test`, from the top-level of the repository, run:
```bash
$ DOCKER_FILE=docker/test-images/zipkin-eureka/Dockerfile build-bin/docker/docker_build openzipkin/zipkin-eureka:test
$ docker run -p 8761:8761 --rm openzipkin/zipkin-eureka:test
```

You can use the env variable `JAVA_OPTS` to change settings such as heap size for Eureka.
9 changes: 0 additions & 9 deletions docker/test-images/zipkin-eureka/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,6 @@
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>

<!-- Get rid of log warning saying to use Caffeine -->
<dependency>
<groupId>org.springframework.boot</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,46 +13,52 @@
*/
package zipkin.test;

import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Base64;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.filter.OncePerRequestFilter;

import static org.springframework.security.crypto.factory.PasswordEncoderFactories.createDelegatingPasswordEncoder;
import static java.nio.charset.StandardCharsets.UTF_8;

/** This enables security, particularly only BASIC auth, when {@code EUREKA_USERNAME} is set. */
@Configuration
@ConditionalOnProperty("eureka.username")
@EnableConfigurationProperties(EurekaProperties.class)
@ImportAutoConfiguration(SecurityAutoConfiguration.class)
public class EurekaSecurity {
@Bean InMemoryUserDetailsManager userDetailsService(EurekaProperties props) {
PasswordEncoder encoder = createDelegatingPasswordEncoder();
UserDetails user = User.withUsername(props.getUsername())
.password(encoder.encode(props.getPassword()))
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user);
@Bean FilterRegistrationBean<BasicAuthFilter> authFilter(EurekaProperties props) {
FilterRegistrationBean<BasicAuthFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new BasicAuthFilter(props.getUsername(), props.getPassword()));
registrationBean.addUrlPatterns("/eureka/*"); // Auth /eureka, though only v2 is valid
registrationBean.setOrder(2);
return registrationBean;
}

/**
* You have to disable CSRF to allow BASIC authenticating Eureka clients to operate.
* <p>
* See <a href="https://cloud.spring.io/spring-cloud-netflix/reference/html/#securing-the-eureka-server">Securing The Eureka Server</a>
*/
@Bean SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.ignoringRequestMatchers("/eureka/**"));
http.authorizeHttpRequests(authz -> authz.requestMatchers("/eureka/**").authenticated())
.httpBasic(Customizer.withDefaults());
return http.build();
/** Implements BASIC instead of spring-security + CORS, CSRF and management exclusions. */
static final class BasicAuthFilter extends OncePerRequestFilter {
final String expectedAuthorization;

BasicAuthFilter(String username, String password) {
expectedAuthorization =
"Basic " + Base64.getEncoder().encodeToString((username + ':' + password).getBytes(UTF_8));
}

@Override protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res,
FilterChain chain) throws ServletException, IOException {
String authHeader = req.getHeader("Authorization");
if (expectedAuthorization.equals(authHeader)) {
chain.doFilter(req, res); // Pass on the supplied credentials
return;
}
res.setHeader("WWW-Authenticate", "Basic realm=\"Realm'\"");
res.sendError(HttpServletResponse.SC_UNAUTHORIZED); // Return 401 otherwise.
}
}
}

0 comments on commit 10e0458

Please sign in to comment.