diff --git a/zipkin-server/README.md b/zipkin-server/README.md index 9d0826a2c11..c311c21dd61 100644 --- a/zipkin-server/README.md +++ b/zipkin-server/README.md @@ -231,6 +231,7 @@ and applies when `STORAGE_TYPE` is set to `cassandra3`: * `CASSANDRA_ENSURE_SCHEMA`: Ensuring cassandra has the latest schema. If enabled tries to execute scripts in the classpath prefixed with `cassandra-schema-cql3`. Defaults to true * `CASSANDRA_USERNAME` and `CASSANDRA_PASSWORD`: Cassandra authentication. Will throw an exception on startup if authentication fails. No default * `CASSANDRA_USE_SSL`: Requires `javax.net.ssl.trustStore` and `javax.net.ssl.trustStorePassword`, defaults to false. + * `CASSANDRA_SSL_HOSTNAME_VALIDATION`: Controls validation of Cassandra server hostname. defaults to true. The following are tuning parameters which may not concern all users: diff --git a/zipkin-server/src/main/java/zipkin2/server/internal/cassandra3/ZipkinCassandra3StorageConfiguration.java b/zipkin-server/src/main/java/zipkin2/server/internal/cassandra3/ZipkinCassandra3StorageConfiguration.java index 97557d0813b..6aff7aa14cc 100644 --- a/zipkin-server/src/main/java/zipkin2/server/internal/cassandra3/ZipkinCassandra3StorageConfiguration.java +++ b/zipkin-server/src/main/java/zipkin2/server/internal/cassandra3/ZipkinCassandra3StorageConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 The OpenZipkin Authors + * 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 @@ -43,14 +43,10 @@ // This component is named .*Cassandra3.* even though the package already says cassandra3 because // Spring Boot configuration endpoints only printout the simple name of the class public class ZipkinCassandra3StorageConfiguration { - @Bean SessionFactory sessionFactory() { return SessionFactory.DEFAULT; } - - @Bean - @ConditionalOnMissingBean - StorageComponent storage( + @Bean @ConditionalOnMissingBean StorageComponent storage( ZipkinCassandra3StorageProperties properties, SessionFactory sessionFactory, @Value("${zipkin.storage.strict-trace-id:true}") boolean strictTraceId, diff --git a/zipkin-server/src/main/java/zipkin2/server/internal/cassandra3/ZipkinCassandra3StorageProperties.java b/zipkin-server/src/main/java/zipkin2/server/internal/cassandra3/ZipkinCassandra3StorageProperties.java index 6cc9aaf3d4b..3221d6ad1f4 100644 --- a/zipkin-server/src/main/java/zipkin2/server/internal/cassandra3/ZipkinCassandra3StorageProperties.java +++ b/zipkin-server/src/main/java/zipkin2/server/internal/cassandra3/ZipkinCassandra3StorageProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 The OpenZipkin Authors + * 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 @@ -27,6 +27,7 @@ class ZipkinCassandra3StorageProperties implements Serializable { // for Spark j private int maxConnections = 8; private boolean ensureSchema = true; private boolean useSsl = false; + private boolean sslHostnameValidation = true; private String username; private String password; /** See {@link CassandraStorage.Builder#indexFetchMultiplier(int)} */ @@ -80,6 +81,14 @@ public void setUseSsl(boolean useSsl) { this.useSsl = useSsl; } + public boolean isSslHostnameValidation() { + return sslHostnameValidation; + } + + public void setSslHostnameValidation(boolean sslHostnameValidation) { + this.sslHostnameValidation = sslHostnameValidation; + } + public String getUsername() { return username; } @@ -112,6 +121,7 @@ public CassandraStorage.Builder toBuilder() { .maxConnections(maxConnections) .ensureSchema(ensureSchema) .useSsl(useSsl) + .sslHostnameValidation(sslHostnameValidation) .username(username) .password(password) .indexFetchMultiplier(indexFetchMultiplier); diff --git a/zipkin-server/src/main/resources/zipkin-server-shared.yml b/zipkin-server/src/main/resources/zipkin-server-shared.yml index b704e13af29..4b3fd060459 100644 --- a/zipkin-server/src/main/resources/zipkin-server-shared.yml +++ b/zipkin-server/src/main/resources/zipkin-server-shared.yml @@ -115,31 +115,6 @@ zipkin: mem: # Maximum number of spans to keep in memory. When exceeded, oldest traces (and their spans) will be purged. max-spans: ${MEM_MAX_SPANS:500000} - cassandra: - # Comma separated list of host addresses part of Cassandra cluster. Ports default to 9042 but you can also specify a custom port with 'host:port'. - contact-points: ${CASSANDRA_CONTACT_POINTS:localhost} - # Name of the datacenter that will be considered "local" for load balancing. - local-dc: ${CASSANDRA_LOCAL_DC:datacenter1} - # Will throw an exception on startup if authentication fails. - username: ${CASSANDRA_USERNAME:} - password: ${CASSANDRA_PASSWORD:} - keyspace: ${CASSANDRA_KEYSPACE:zipkin} - # Max pooled connections per datacenter-local host. - max-connections: ${CASSANDRA_MAX_CONNECTIONS:8} - # Ensuring that schema exists, if enabled tries to execute script /zipkin-cassandra-core/resources/cassandra-schema.cql. - ensure-schema: ${CASSANDRA_ENSURE_SCHEMA:true} - # 7 days in seconds - span-ttl: ${CASSANDRA_SPAN_TTL:604800} - # 3 days in seconds - index-ttl: ${CASSANDRA_INDEX_TTL:259200} - # the maximum trace index metadata entries to cache - index-cache-max: ${CASSANDRA_INDEX_CACHE_MAX:100000} - # how long to cache index metadata about a trace. 1 minute in seconds - index-cache-ttl: ${CASSANDRA_INDEX_CACHE_TTL:60} - # how many more index rows to fetch than the user-supplied query limit - index-fetch-multiplier: ${CASSANDRA_INDEX_FETCH_MULTIPLIER:3} - # Using ssl for connection, rely on Keystore - use-ssl: ${CASSANDRA_USE_SSL:false} cassandra3: # Comma separated list of host addresses part of Cassandra cluster. Ports default to 9042 but you can also specify a custom port with 'host:port'. contact-points: ${CASSANDRA_CONTACT_POINTS:localhost} @@ -157,6 +132,8 @@ zipkin: index-fetch-multiplier: ${CASSANDRA_INDEX_FETCH_MULTIPLIER:3} # Using ssl for connection, rely on Keystore use-ssl: ${CASSANDRA_USE_SSL:false} + # Controls validation of Cassandra server hostname + ssl-hostname-validation: ${CASSANDRA_SSL_HOSTNAME_VALIDATION:true} elasticsearch: # host is left unset intentionally, to defer the decision hosts: ${ES_HOSTS:} diff --git a/zipkin-server/src/test/java/zipkin2/server/internal/eureka/BaseITZipkinEureka.java b/zipkin-server/src/test/java/zipkin2/server/internal/eureka/BaseITZipkinEureka.java index 216a390aa8a..7ddafdfd493 100644 --- a/zipkin-server/src/test/java/zipkin2/server/internal/eureka/BaseITZipkinEureka.java +++ b/zipkin-server/src/test/java/zipkin2/server/internal/eureka/BaseITZipkinEureka.java @@ -81,12 +81,10 @@ abstract class BaseITZipkinEureka { this.serviceUrl = serviceUrl; } - @BeforeEach void awaitRegistration() { + @Test @Order(1) void registersInEureka() throws IOException { // The zipkin server may start before Eureka processes the registration await().until(this::getEurekaZipkinAppAsString, (s) -> true); - } - @Test @Order(1) void registersInEureka() throws IOException { String json = getEurekaZipkinAppAsString(); // Make sure the health status is OK diff --git a/zipkin-server/src/test/java/zipkin2/storage/cassandra/ZipkinCassandraStorageAutoConfigurationTest.java b/zipkin-server/src/test/java/zipkin2/storage/cassandra/ZipkinCassandraStorageAutoConfigurationTest.java index 9be52c555be..6e483ee112b 100644 --- a/zipkin-server/src/test/java/zipkin2/storage/cassandra/ZipkinCassandraStorageAutoConfigurationTest.java +++ b/zipkin-server/src/test/java/zipkin2/storage/cassandra/ZipkinCassandraStorageAutoConfigurationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 The OpenZipkin Authors + * 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 @@ -124,4 +124,30 @@ class ZipkinCassandraStorageAutoConfigurationTest { assertThat(context.getBean(CassandraStorage.class).autocompleteCardinality) .isEqualTo(5000); } + + @Test void useSsl() { + TestPropertyValues.of( + "zipkin.storage.type:cassandra3", + "zipkin.storage.cassandra3.use-ssl:true") + .applyTo(context); + Access.registerCassandra3(context); + context.refresh(); + + assertThat(context.getBean(CassandraStorage.class).useSsl).isTrue(); + assertThat(context.getBean(CassandraStorage.class).sslHostnameValidation).isTrue(); + } + + @Test void sslHostnameValidation_canSetToFalse() { + TestPropertyValues.of( + "zipkin.storage.type:cassandra3", + "zipkin.storage.cassandra3.use-ssl:true", + "zipkin.storage.cassandra3.ssl-hostname-validation:false" + ) + .applyTo(context); + Access.registerCassandra3(context); + context.refresh(); + + assertThat(context.getBean(CassandraStorage.class).useSsl).isTrue(); + assertThat(context.getBean(CassandraStorage.class).sslHostnameValidation).isFalse(); + } } diff --git a/zipkin-storage/cassandra/src/main/java/zipkin2/storage/cassandra/CassandraStorage.java b/zipkin-storage/cassandra/src/main/java/zipkin2/storage/cassandra/CassandraStorage.java index c36403a4e80..deeb61c28c9 100644 --- a/zipkin-storage/cassandra/src/main/java/zipkin2/storage/cassandra/CassandraStorage.java +++ b/zipkin-storage/cassandra/src/main/java/zipkin2/storage/cassandra/CassandraStorage.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 The OpenZipkin Authors + * 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 @@ -93,7 +93,8 @@ public Builder sessionFactory(SessionFactory sessionFactory) { } return new CassandraStorage(strictTraceId, searchEnabled, autocompleteKeys, autocompleteTtl, autocompleteCardinality, contactPoints, localDc, poolingOptions(), authProvider, useSsl, - sessionFactory, keyspace, ensureSchema, maxTraceCols, indexFetchMultiplier); + sslHostnameValidation, sessionFactory, keyspace, ensureSchema, maxTraceCols, + indexFetchMultiplier); } } @@ -105,6 +106,7 @@ autocompleteCardinality, contactPoints, localDc, poolingOptions(), authProvider, final Map poolingOptions; @Nullable final AuthProvider authProvider; final boolean useSsl; + final boolean sslHostnameValidation; final String keyspace; final boolean ensureSchema; @@ -115,8 +117,8 @@ autocompleteCardinality, contactPoints, localDc, poolingOptions(), authProvider, CassandraStorage(boolean strictTraceId, boolean searchEnabled, Set autocompleteKeys, int autocompleteTtl, int autocompleteCardinality, String contactPoints, String localDc, Map poolingOptions, AuthProvider authProvider, boolean useSsl, - SessionFactory sessionFactory, String keyspace, boolean ensureSchema, int maxTraceCols, - int indexFetchMultiplier) { + boolean sslHostnameValidation, SessionFactory sessionFactory, String keyspace, + boolean ensureSchema, int maxTraceCols, int indexFetchMultiplier) { // Assign generic configuration for all storage components this.strictTraceId = strictTraceId; this.searchEnabled = searchEnabled; @@ -130,6 +132,7 @@ autocompleteCardinality, contactPoints, localDc, poolingOptions(), authProvider, this.poolingOptions = poolingOptions; this.authProvider = authProvider; this.useSsl = useSsl; + this.sslHostnameValidation = sslHostnameValidation; this.ensureSchema = ensureSchema; this.keyspace = keyspace; diff --git a/zipkin-storage/cassandra/src/main/java/zipkin2/storage/cassandra/DefaultSessionFactory.java b/zipkin-storage/cassandra/src/main/java/zipkin2/storage/cassandra/DefaultSessionFactory.java index 76db1994464..519f0315e1b 100644 --- a/zipkin-storage/cassandra/src/main/java/zipkin2/storage/cassandra/DefaultSessionFactory.java +++ b/zipkin-storage/cassandra/src/main/java/zipkin2/storage/cassandra/DefaultSessionFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2023 The OpenZipkin Authors + * 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 @@ -64,7 +64,8 @@ static CqlSession buildSession(CassandraStorage cassandra) { cassandra.localDc, cassandra.poolingOptions, cassandra.authProvider, - cassandra.useSsl + cassandra.useSsl, + cassandra.sslHostnameValidation ); } diff --git a/zipkin-storage/cassandra/src/main/java/zipkin2/storage/cassandra/internal/CassandraStorageBuilder.java b/zipkin-storage/cassandra/src/main/java/zipkin2/storage/cassandra/internal/CassandraStorageBuilder.java index 7ca61937841..3235ef2ccaf 100644 --- a/zipkin-storage/cassandra/src/main/java/zipkin2/storage/cassandra/internal/CassandraStorageBuilder.java +++ b/zipkin-storage/cassandra/src/main/java/zipkin2/storage/cassandra/internal/CassandraStorageBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 The OpenZipkin Authors + * 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 @@ -41,6 +41,7 @@ public abstract class CassandraStorageBuilder poolingOptions, @Nullable AuthProvider authProvider, - boolean useSsl + boolean useSsl, + boolean sslHostnameValidation ) { // Some options aren't supported by builder methods. In these cases, we use driver config // See https://groups.google.com/a/lists.datastax.com/forum/#!topic/java-driver-user/Z8HrCDX47Q0 @@ -76,7 +78,10 @@ public static CqlSession buildSession( // All Zipkin CQL writes are idempotent config = config.withBoolean(REQUEST_DEFAULT_IDEMPOTENCE, true); - if (useSsl) config = config.withClass(SSL_ENGINE_FACTORY_CLASS, DefaultSslEngineFactory.class); + if (useSsl) { + config = config.withClass(SSL_ENGINE_FACTORY_CLASS, DefaultSslEngineFactory.class); + config = config.withBoolean(SSL_HOSTNAME_VALIDATION, sslHostnameValidation); + } // Log categories can enable query logging Logger requestLogger = LoggerFactory.getLogger(SessionBuilder.class);