Skip to content

Commit 92ead05

Browse files
authored
Add feature flag to disallow custom S3 endpoints (#2442)
* Add new realm-level flag: `ALLOW_SETTING_S3_ENDPOINTS` (default: true) * Enforce in `PolarisServiceImpl.validateStorageConfig()` Fixes #2436
1 parent ad450d4 commit 92ead05

File tree

4 files changed

+87
-0
lines changed

4 files changed

+87
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ request adding CHANGELOG notes for breaking (!) changes and possibly other secti
5151
### New Features
5252

5353
- Added Catalog configuration for S3 and STS endpoints. This also allows using non-AWS S3 implementations.
54+
The realm-level feature flag `ALLOW_SETTING_S3_ENDPOINTS` (default: true) may be used to disable this
55+
functionality.
5456

5557
- The `IMPLICIT` authentication type enables users to create federated catalogs without explicitly
5658
providing authentication parameters to Polaris. When the authentication type is set to `IMPLICIT`,

polaris-core/src/main/java/org/apache/polaris/core/config/FeatureConfiguration.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,16 @@ public static void enforceFeatureEnabledOrThrow(
7979
.defaultValue(false)
8080
.buildFeatureConfiguration();
8181

82+
public static final FeatureConfiguration<Boolean> ALLOW_SETTING_S3_ENDPOINTS =
83+
PolarisConfiguration.<Boolean>builder()
84+
.key("ALLOW_SETTING_S3_ENDPOINTS")
85+
.description(
86+
"If set to true (default), Polaris will permit S3 storage configurations to have custom endpoints.\n"
87+
+ "If set to false, Polaris will not accept catalog create and update requests that contain \n"
88+
+ "S3 endpoint properties.")
89+
.defaultValue(true)
90+
.buildFeatureConfiguration();
91+
8292
@SuppressWarnings("deprecation")
8393
public static final FeatureConfiguration<Boolean> ALLOW_TABLE_LOCATION_OVERLAP =
8494
PolarisConfiguration.<Boolean>builder()

runtime/service/src/main/java/org/apache/polaris/service/admin/PolarisServiceImpl.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.apache.iceberg.rest.responses.ErrorResponse;
3232
import org.apache.polaris.core.admin.model.AddGrantRequest;
3333
import org.apache.polaris.core.admin.model.AuthenticationParameters;
34+
import org.apache.polaris.core.admin.model.AwsStorageConfigInfo;
3435
import org.apache.polaris.core.admin.model.Catalog;
3536
import org.apache.polaris.core.admin.model.CatalogGrant;
3637
import org.apache.polaris.core.admin.model.CatalogRole;
@@ -180,6 +181,16 @@ private void validateStorageConfig(StorageConfigInfo storageConfigInfo) {
180181
throw new IllegalArgumentException(
181182
"Unsupported storage type: " + storageConfigInfo.getStorageType());
182183
}
184+
185+
if (!realmConfig.getConfig(FeatureConfiguration.ALLOW_SETTING_S3_ENDPOINTS)) {
186+
if (storageConfigInfo instanceof AwsStorageConfigInfo s3Config) {
187+
if (s3Config.getEndpoint() != null
188+
|| s3Config.getStsEndpoint() != null
189+
|| s3Config.getEndpointInternal() != null) {
190+
throw new IllegalArgumentException("Explicitly setting S3 endpoints is not allowed.");
191+
}
192+
}
193+
}
183194
}
184195

185196
private void validateExternalCatalog(Catalog catalog) {

runtime/service/src/test/java/org/apache/polaris/service/admin/ManagementServiceTest.java

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.List;
2929
import java.util.Map;
3030
import java.util.Set;
31+
import java.util.function.Supplier;
3132
import org.apache.iceberg.exceptions.ValidationException;
3233
import org.apache.polaris.core.PolarisCallContext;
3334
import org.apache.polaris.core.admin.model.AwsStorageConfigInfo;
@@ -68,6 +69,7 @@ public void setup() {
6869
services =
6970
TestServices.builder()
7071
.config(Map.of("SUPPORTED_CATALOG_STORAGE_TYPES", List.of("S3", "GCS", "AZURE")))
72+
.config(Map.of("ALLOW_SETTING_S3_ENDPOINTS", Boolean.FALSE))
7173
.build();
7274
}
7375

@@ -97,6 +99,51 @@ public void testCreateCatalogWithDisallowedStorageConfig() {
9799
.hasMessage("Unsupported storage type: FILE");
98100
}
99101

102+
@Test
103+
public void testCreateCatalogWithDisallowedS3Endpoints() {
104+
AwsStorageConfigInfo.Builder storageConfig =
105+
AwsStorageConfigInfo.builder()
106+
.setRoleArn("arn:aws:iam::123456789012:role/my-role")
107+
.setExternalId("externalId")
108+
.setUserArn("userArn")
109+
.setStorageType(StorageConfigInfo.StorageTypeEnum.S3)
110+
.setAllowedLocations(List.of("s3://my-old-bucket/path/to/data"));
111+
String catalogName = "test-catalog";
112+
Supplier<Catalog> catalog =
113+
() ->
114+
PolarisCatalog.builder()
115+
.setType(Catalog.TypeEnum.INTERNAL)
116+
.setName(catalogName)
117+
.setProperties(new CatalogProperties("s3://bucket/path/to/data"))
118+
.setStorageConfigInfo(storageConfig.build())
119+
.build();
120+
Supplier<Response> createCatalog =
121+
() ->
122+
services
123+
.catalogsApi()
124+
.createCatalog(
125+
new CreateCatalogRequest(catalog.get()),
126+
services.realmContext(),
127+
services.securityContext());
128+
129+
storageConfig.setEndpoint("http://example.com");
130+
assertThatThrownBy(createCatalog::get)
131+
.isInstanceOf(IllegalArgumentException.class)
132+
.hasMessage("Explicitly setting S3 endpoints is not allowed.");
133+
134+
storageConfig.setEndpoint(null);
135+
storageConfig.setStsEndpoint("http://example.com");
136+
assertThatThrownBy(createCatalog::get)
137+
.isInstanceOf(IllegalArgumentException.class)
138+
.hasMessage("Explicitly setting S3 endpoints is not allowed.");
139+
140+
storageConfig.setStsEndpoint(null);
141+
storageConfig.setEndpointInternal("http://example.com");
142+
assertThatThrownBy(createCatalog::get)
143+
.isInstanceOf(IllegalArgumentException.class)
144+
.hasMessage("Explicitly setting S3 endpoints is not allowed.");
145+
}
146+
100147
@Test
101148
public void testUpdateCatalogWithDisallowedStorageConfig() {
102149
AwsStorageConfigInfo awsConfigModel =
@@ -162,6 +209,23 @@ public void testUpdateCatalogWithDisallowedStorageConfig() {
162209
services.securityContext()))
163210
.isInstanceOf(IllegalArgumentException.class)
164211
.hasMessage("Unsupported storage type: FILE");
212+
213+
UpdateCatalogRequest update2 =
214+
new UpdateCatalogRequest(
215+
fetchedCatalog.getEntityVersion(),
216+
Map.of(),
217+
AwsStorageConfigInfo.builder(StorageConfigInfo.StorageTypeEnum.S3)
218+
.setRoleArn("arn:aws:iam::123456789012:role/my-role")
219+
.setEndpoint("http://example.com")
220+
.build());
221+
assertThatThrownBy(
222+
() ->
223+
services
224+
.catalogsApi()
225+
.updateCatalog(
226+
catalogName, update2, services.realmContext(), services.securityContext()))
227+
.isInstanceOf(IllegalArgumentException.class)
228+
.hasMessage("Explicitly setting S3 endpoints is not allowed.");
165229
}
166230

167231
private PolarisMetaStoreManager setupMetaStoreManager() {

0 commit comments

Comments
 (0)