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:
Igor Motov 2015-05-20 16:26:37 -04:00
parent b9b13cd130
commit 26aeeda310
4 changed files with 103 additions and 4 deletions

View File

@ -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();

View File

@ -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 {

View File

@ -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 {

View File

@ -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");
}
}
}