aboutsummaryrefslogtreecommitdiffstats
path: root/controller-server
diff options
context:
space:
mode:
authorMorten Tokle <mortent@verizonmedia.com>2021-08-05 15:15:25 +0200
committerGitHub <noreply@github.com>2021-08-05 15:15:25 +0200
commit76e45b948ce0cb1d6a20e5c5063229f4e9a2d4de (patch)
treef008a1431dc3e992436ead40e92ca5f8ba08d425 /controller-server
parent1738a68e371a70cb9938e2bfc1a99b4c5804f2ad (diff)
Limit apis for service view (#18686)
Diffstat (limited to 'controller-server')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java10
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java26
2 files changed, 36 insertions, 0 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
index 2a06fc76f39..c9d21ea86aa 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
@@ -37,6 +37,8 @@ import com.yahoo.slime.JsonParseException;
import com.yahoo.slime.Slime;
import com.yahoo.slime.SlimeUtils;
import com.yahoo.text.Text;
+import com.yahoo.vespa.flags.Flags;
+import com.yahoo.vespa.flags.ListFlag;
import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.Instance;
@@ -141,6 +143,7 @@ import java.util.OptionalLong;
import java.util.Scanner;
import java.util.StringJoiner;
import java.util.logging.Level;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -166,6 +169,7 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
private final Controller controller;
private final AccessControlRequests accessControlRequests;
private final TestConfigSerializer testConfigSerializer;
+ private final ListFlag<String> allowedServiceViewProxy;
@Inject
public ApplicationApiHandler(LoggingRequestHandler.Context parentCtx,
@@ -175,6 +179,7 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
this.controller = controller;
this.accessControlRequests = accessControlRequests;
this.testConfigSerializer = new TestConfigSerializer(controller.system());
+ allowedServiceViewProxy = Flags.ALLOWED_SERVICE_VIEW_APIS.bindTo(controller.flagSource());
}
@Override
@@ -1685,6 +1690,11 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
return new HtmlResponse(result);
}
+ String normalizedRestPath = URI.create(restPath).normalize().toString();
+ if (allowedServiceViewProxy.value().stream().noneMatch(normalizedRestPath::startsWith)) {
+ return ErrorResponse.forbidden("Access denied");
+ }
+
Map<?,?> result = controller.serviceRegistry().configServer().getServiceApiResponse(deploymentId, serviceName, restPath);
ServiceApiResponse response = new ServiceApiResponse(deploymentId.zoneId(),
deploymentId.applicationId(),
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
index a01097cfcb6..3348454c6af 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
@@ -1583,6 +1583,32 @@ public class ApplicationApiTest extends ControllerContainerTest {
assertEquals(0, activeGrants.size());
}
+ @Test
+ public void testServiceView() throws Exception {
+ createAthenzDomainWithAdmin(ATHENZ_TENANT_DOMAIN, USER_ID);
+ String serviceApi="/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-central-1/instance/instance1/service";
+ // Not allowed to request apis not listed in feature flag allowed-service-view-apis. e.g /document/v1
+ tester.assertResponse(request(serviceApi + "/storagenode-awe3slno6mmq2fye191y324jl/document/v1/", GET)
+ .userIdentity(USER_ID)
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
+ "{\"error-code\":\"FORBIDDEN\",\"message\":\"Access denied\"}",
+ 403);
+
+ // Test path traversal
+ tester.assertResponse(request(serviceApi + "/storagenode-awe3slno6mmq2fye191y324jl/state/v1/../../document/v1/", GET)
+ .userIdentity(USER_ID)
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
+ "{\"error-code\":\"FORBIDDEN\",\"message\":\"Access denied\"}",
+ 403);
+
+ // Test urlencoded path traversal
+ tester.assertResponse(request(serviceApi + "/storagenode-awe3slno6mmq2fye191y324jl/state%2Fv1%2F..%2F..%2Fdocument%2Fv1%2F", GET)
+ .userIdentity(USER_ID)
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
+ "{\"error-code\":\"FORBIDDEN\",\"message\":\"Access denied\"}",
+ 403);
+ }
+
private static String serializeInstant(Instant i) {
return DateTimeFormatter.ISO_INSTANT.format(i.truncatedTo(ChronoUnit.SECONDS));
}