Enforce limitations on ILM policy names (#35104)

Enforces restrictions on ILM policy names to ensure we don't accept
policy names the system can't handle, or may reserve for future use.
This commit is contained in:
Gordon Brown 2018-11-09 10:11:26 -07:00 committed by GitHub
parent ae2af20ae5
commit 67f9e8fa23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 67 additions and 0 deletions

View File

@ -21,12 +21,14 @@ import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.xpack.core.indexlifecycle.Step.StepKey; import org.elasticsearch.xpack.core.indexlifecycle.Step.StepKey;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -40,6 +42,7 @@ import java.util.stream.Collectors;
public class LifecyclePolicy extends AbstractDiffable<LifecyclePolicy> public class LifecyclePolicy extends AbstractDiffable<LifecyclePolicy>
implements ToXContentObject, Diffable<LifecyclePolicy> { implements ToXContentObject, Diffable<LifecyclePolicy> {
private static final Logger logger = LogManager.getLogger(LifecyclePolicy.class); private static final Logger logger = LogManager.getLogger(LifecyclePolicy.class);
private static final int MAX_INDEX_NAME_BYTES = 255;
public static final ParseField PHASES_FIELD = new ParseField("phases"); public static final ParseField PHASES_FIELD = new ParseField("phases");
@ -241,6 +244,30 @@ public class LifecyclePolicy extends AbstractDiffable<LifecyclePolicy>
} }
} }
/**
* Validate the name for an policy against some static rules. Intended to match
* {@link org.elasticsearch.cluster.metadata.MetaDataCreateIndexService#validateIndexOrAliasName(String, BiFunction)}
* @param policy the policy name to validate
* @throws IllegalArgumentException if the name is invalid
*/
public static void validatePolicyName(String policy) {
if (policy.contains(",")) {
throw new IllegalArgumentException("invalid policy name [" + policy + "]: must not contain ','");
}
if (policy.contains(" ")) {
throw new IllegalArgumentException("invalid policy name [" + policy + "]: must not contain spaces");
}
if (policy.charAt(0) == '_') {
throw new IllegalArgumentException("invalid policy name [" + policy + "]: must not start with '_'");
}
int byteCount = 0;
byteCount = policy.getBytes(StandardCharsets.UTF_8).length;
if (byteCount > MAX_INDEX_NAME_BYTES) {
throw new IllegalArgumentException("invalid policy name [" + policy + "]: name is too long, (" + byteCount + " > " +
MAX_INDEX_NAME_BYTES + ")");
}
}
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(name, phases); return Objects.hash(name, phases);

View File

@ -316,4 +316,20 @@ public class LifecyclePolicyTests extends AbstractSerializingTestCase<LifecycleP
assertTrue(policy.isActionSafe(new StepKey("new", randomAlphaOfLength(10), randomAlphaOfLength(10)))); assertTrue(policy.isActionSafe(new StepKey("new", randomAlphaOfLength(10), randomAlphaOfLength(10))));
} }
public void testValidatePolicyName() {
expectThrows(IllegalArgumentException.class, () -> LifecyclePolicy.validatePolicyName(randomAlphaOfLengthBetween(0,10) +
"," + randomAlphaOfLengthBetween(0,10)));
expectThrows(IllegalArgumentException.class, () -> LifecyclePolicy.validatePolicyName(randomAlphaOfLengthBetween(0,10) +
" " + randomAlphaOfLengthBetween(0,10)));
expectThrows(IllegalArgumentException.class, () -> LifecyclePolicy.validatePolicyName("_" + randomAlphaOfLengthBetween(1, 20)));
expectThrows(IllegalArgumentException.class, () -> LifecyclePolicy.validatePolicyName(randomAlphaOfLengthBetween(256, 1000)));
LifecyclePolicy.validatePolicyName(randomAlphaOfLengthBetween(1,10) + "_" + randomAlphaOfLengthBetween(0,10));
LifecyclePolicy.validatePolicyName(randomAlphaOfLengthBetween(0,10) + "-" + randomAlphaOfLengthBetween(0,10));
LifecyclePolicy.validatePolicyName(randomAlphaOfLengthBetween(0,10) + "+" + randomAlphaOfLengthBetween(0,10));
LifecyclePolicy.validatePolicyName(randomAlphaOfLengthBetween(1,255));
}
} }

View File

@ -36,6 +36,7 @@ import org.junit.Before;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -45,6 +46,7 @@ import java.util.function.Supplier;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
@ -399,6 +401,26 @@ public class TimeSeriesLifecycleActionsIT extends ESRestTestCase {
} }
public void testInvalidPolicyNames() throws UnsupportedEncodingException {
ResponseException ex;
policy = randomAlphaOfLengthBetween(0,10) + "," + randomAlphaOfLengthBetween(0,10);
ex = expectThrows(ResponseException.class, () -> createNewSingletonPolicy("delete", new DeleteAction()));
assertThat(ex.getCause().getMessage(), containsString("invalid policy name"));
policy = randomAlphaOfLengthBetween(0,10) + "%20" + randomAlphaOfLengthBetween(0,10);
ex = expectThrows(ResponseException.class, () -> createNewSingletonPolicy("delete", new DeleteAction()));
assertThat(ex.getCause().getMessage(), containsString("invalid policy name"));
policy = "_" + randomAlphaOfLengthBetween(1, 20);
ex = expectThrows(ResponseException.class, () -> createNewSingletonPolicy("delete", new DeleteAction()));
assertThat(ex.getMessage(), containsString("invalid policy name"));
policy = randomAlphaOfLengthBetween(256, 1000);
ex = expectThrows(ResponseException.class, () -> createNewSingletonPolicy("delete", new DeleteAction()));
assertThat(ex.getMessage(), containsString("invalid policy name"));
}
private void createFullPolicy(TimeValue hotTime) throws IOException { private void createFullPolicy(TimeValue hotTime) throws IOException {
Map<String, LifecycleAction> warmActions = new HashMap<>(); Map<String, LifecycleAction> warmActions = new HashMap<>();
warmActions.put(ForceMergeAction.NAME, new ForceMergeAction(1)); warmActions.put(ForceMergeAction.NAME, new ForceMergeAction(1));

View File

@ -21,6 +21,7 @@ import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService; import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.core.ClientHelper; import org.elasticsearch.xpack.core.ClientHelper;
import org.elasticsearch.xpack.core.indexlifecycle.IndexLifecycleMetadata; import org.elasticsearch.xpack.core.indexlifecycle.IndexLifecycleMetadata;
import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicy;
import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicyMetadata; import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicyMetadata;
import org.elasticsearch.xpack.core.indexlifecycle.OperationMode; import org.elasticsearch.xpack.core.indexlifecycle.OperationMode;
import org.elasticsearch.xpack.core.indexlifecycle.action.PutLifecycleAction; import org.elasticsearch.xpack.core.indexlifecycle.action.PutLifecycleAction;
@ -65,6 +66,7 @@ public class TransportPutLifecycleAction extends TransportMasterNodeAction<Reque
Map<String, String> filteredHeaders = threadPool.getThreadContext().getHeaders().entrySet().stream() Map<String, String> filteredHeaders = threadPool.getThreadContext().getHeaders().entrySet().stream()
.filter(e -> ClientHelper.SECURITY_HEADER_FILTERS.contains(e.getKey())) .filter(e -> ClientHelper.SECURITY_HEADER_FILTERS.contains(e.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
LifecyclePolicy.validatePolicyName(request.getPolicy().getName());
clusterService.submitStateUpdateTask("put-lifecycle-" + request.getPolicy().getName(), clusterService.submitStateUpdateTask("put-lifecycle-" + request.getPolicy().getName(),
new AckedClusterStateUpdateTask<Response>(request, listener) { new AckedClusterStateUpdateTask<Response>(request, listener) {
@Override @Override