Snapshot/Restore: Add support for applying setting filters when displaying repository settings
Currently all settings that were specified during repository creation are displayed. This commit enables plugins such as cloud-aws to filter out sensitive settings from the repository. Closes #11265
This commit is contained in:
parent
b9b13cd130
commit
26aeeda310
|
@ -202,9 +202,7 @@ public class RepositoriesMetaData extends AbstractDiffable<Custom> implements Me
|
|||
builder.startObject(repository.name(), XContentBuilder.FieldCaseConversion.NONE);
|
||||
builder.field("type", repository.type());
|
||||
builder.startObject("settings");
|
||||
for (Map.Entry<String, String> settingEntry : repository.settings().getAsMap().entrySet()) {
|
||||
builder.field(settingEntry.getKey(), settingEntry.getValue());
|
||||
}
|
||||
repository.settings().toXContent(builder, params);
|
||||
builder.endObject();
|
||||
|
||||
builder.endObject();
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.elasticsearch.cluster.metadata.RepositoryMetaData;
|
|||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.settings.SettingsFilter;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.rest.*;
|
||||
import org.elasticsearch.rest.action.support.RestBuilderListener;
|
||||
|
@ -40,11 +41,14 @@ import static org.elasticsearch.rest.RestStatus.OK;
|
|||
*/
|
||||
public class RestGetRepositoriesAction extends BaseRestHandler {
|
||||
|
||||
private final SettingsFilter settingsFilter;
|
||||
|
||||
@Inject
|
||||
public RestGetRepositoriesAction(Settings settings, RestController controller, Client client) {
|
||||
public RestGetRepositoriesAction(Settings settings, RestController controller, Client client, SettingsFilter settingsFilter) {
|
||||
super(settings, controller, client);
|
||||
controller.registerHandler(GET, "/_snapshot", this);
|
||||
controller.registerHandler(GET, "/_snapshot/{repository}", this);
|
||||
this.settingsFilter = settingsFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -53,6 +57,7 @@ public class RestGetRepositoriesAction extends BaseRestHandler {
|
|||
GetRepositoriesRequest getRepositoriesRequest = getRepositoryRequest(repositories);
|
||||
getRepositoriesRequest.masterNodeTimeout(request.paramAsTime("master_timeout", getRepositoriesRequest.masterNodeTimeout()));
|
||||
getRepositoriesRequest.local(request.paramAsBoolean("local", getRepositoriesRequest.local()));
|
||||
settingsFilter.addFilterSettingParams(request);
|
||||
client.admin().cluster().getRepositories(getRepositoriesRequest, new RestBuilderListener<GetRepositoriesResponse>(channel) {
|
||||
@Override
|
||||
public RestResponse buildResponse(GetRepositoriesResponse response, XContentBuilder builder) throws Exception {
|
||||
|
|
|
@ -56,9 +56,15 @@ import org.elasticsearch.discovery.zen.elect.ElectMasterService;
|
|||
import org.elasticsearch.index.store.IndexStore;
|
||||
import org.elasticsearch.indices.ttl.IndicesTTLService;
|
||||
import org.elasticsearch.repositories.RepositoryMissingException;
|
||||
import org.elasticsearch.rest.RestChannel;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.rest.RestResponse;
|
||||
import org.elasticsearch.rest.action.admin.cluster.repositories.get.RestGetRepositoriesAction;
|
||||
import org.elasticsearch.rest.action.admin.cluster.state.RestClusterStateAction;
|
||||
import org.elasticsearch.snapshots.mockstore.MockRepositoryModule;
|
||||
import org.elasticsearch.snapshots.mockstore.MockRepositoryPlugin;
|
||||
import org.elasticsearch.test.InternalTestCluster;
|
||||
import org.elasticsearch.test.rest.FakeRestRequest;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -70,6 +76,7 @@ import java.util.List;
|
|||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static com.google.common.collect.Lists.newArrayList;
|
||||
import static org.elasticsearch.common.settings.Settings.settingsBuilder;
|
||||
|
@ -622,6 +629,64 @@ public class DedicatedClusterSnapshotRestoreTests extends AbstractSnapshotTests
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatSensitiveRepositorySettingsAreNotExposed() throws Exception {
|
||||
Settings nodeSettings = settingsBuilder().put("plugin.types", MockRepositoryPlugin.class.getName()).build();
|
||||
logger.info("--> start two nodes");
|
||||
internalCluster().startNodesAsync(2, nodeSettings).get();
|
||||
// Register mock repositories
|
||||
client().admin().cluster().preparePutRepository("test-repo")
|
||||
.setType("mock").setSettings(Settings.settingsBuilder()
|
||||
.put("location", randomRepoPath())
|
||||
.put("secret.mock.username", "notsecretusername")
|
||||
.put("secret.mock.password", "verysecretpassword")
|
||||
).get();
|
||||
|
||||
RestGetRepositoriesAction getRepoAction = internalCluster().getInstance(RestGetRepositoriesAction.class);
|
||||
RestRequest getRepoRequest = new FakeRestRequest();
|
||||
getRepoRequest.params().put("repository", "test-repo");
|
||||
final CountDownLatch getRepoLatch = new CountDownLatch(1);
|
||||
final AtomicReference<AssertionError> getRepoError = new AtomicReference<>();
|
||||
getRepoAction.handleRequest(getRepoRequest, new RestChannel(getRepoRequest, true) {
|
||||
@Override
|
||||
public void sendResponse(RestResponse response) {
|
||||
try {
|
||||
assertThat(response.content().toUtf8(), containsString("notsecretusername"));
|
||||
assertThat(response.content().toUtf8(), not(containsString("verysecretpassword")));
|
||||
} catch (AssertionError ex) {
|
||||
getRepoError.set(ex);
|
||||
}
|
||||
getRepoLatch.countDown();
|
||||
}
|
||||
});
|
||||
assertTrue(getRepoLatch.await(1, TimeUnit.SECONDS));
|
||||
if (getRepoError.get() != null) {
|
||||
throw getRepoError.get();
|
||||
}
|
||||
|
||||
RestClusterStateAction clusterStateAction = internalCluster().getInstance(RestClusterStateAction.class);
|
||||
RestRequest clusterStateRequest = new FakeRestRequest();
|
||||
final CountDownLatch clusterStateLatch = new CountDownLatch(1);
|
||||
final AtomicReference<AssertionError> clusterStateError = new AtomicReference<>();
|
||||
clusterStateAction.handleRequest(clusterStateRequest, new RestChannel(clusterStateRequest, true) {
|
||||
@Override
|
||||
public void sendResponse(RestResponse response) {
|
||||
try {
|
||||
assertThat(response.content().toUtf8(), containsString("notsecretusername"));
|
||||
assertThat(response.content().toUtf8(), not(containsString("verysecretpassword")));
|
||||
} catch (AssertionError ex) {
|
||||
clusterStateError.set(ex);
|
||||
}
|
||||
clusterStateLatch.countDown();
|
||||
}
|
||||
});
|
||||
assertTrue(clusterStateLatch.await(1, TimeUnit.SECONDS));
|
||||
if (clusterStateError.get() != null) {
|
||||
throw clusterStateError.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void chaosSnapshotTest() throws Exception {
|
||||
|
|
|
@ -19,9 +19,17 @@
|
|||
|
||||
package org.elasticsearch.snapshots.mockstore;
|
||||
|
||||
import org.elasticsearch.common.inject.AbstractModule;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.inject.Module;
|
||||
import org.elasticsearch.common.settings.SettingsFilter;
|
||||
import org.elasticsearch.plugins.AbstractPlugin;
|
||||
import org.elasticsearch.repositories.RepositoriesModule;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import static com.google.common.collect.Lists.newArrayList;
|
||||
|
||||
public class MockRepositoryPlugin extends AbstractPlugin {
|
||||
|
||||
@Override
|
||||
|
@ -37,4 +45,27 @@ public class MockRepositoryPlugin extends AbstractPlugin {
|
|||
public void onModule(RepositoriesModule repositoriesModule) {
|
||||
repositoriesModule.registerRepository("mock", MockRepositoryModule.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends Module>> modules() {
|
||||
Collection<Class<? extends Module>> modules = newArrayList();
|
||||
modules.add(SettingsFilteringModule.class);
|
||||
return modules;
|
||||
}
|
||||
|
||||
public static class SettingsFilteringModule extends AbstractModule {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(SettingsFilteringService.class).asEagerSingleton();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SettingsFilteringService {
|
||||
@Inject
|
||||
public SettingsFilteringService(SettingsFilter settingsFilter) {
|
||||
settingsFilter.addFilter("secret.mock.password");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue