Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docker: fixes spring config in zipkin-eureka test image #3699

Merged
merged 6 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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:
Copy link
Member Author

@codefromthecrypt codefromthecrypt Jan 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

without a compose, it effectively only runs this part. I daisy-chained the two paths since we are using spring security for username/password stuff. Maybe someone can convert this to armeria and use normal simple basic auth filter one day, but I went with the spring-security route initially.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry missed that

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no worries I should have checked. Anyway the prior one failed on health check for frustrating reasons.

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>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note this didn't change the image size as even if the eureka layer is 55mb the current classpath requires modules not in our JRE, so we use the JDK image as noted in the Dockerfile. The image sum with the JDK layer ends up as 430MB. Maybe a trimmer eureka will happen one day, but it isn't terribly important.

<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.
}
}
}
Loading