Skip to content

Commit

Permalink
KNOX-2990 - Using DerbyDatabaseTSS instead of AliasBasedTSS by default (
Browse files Browse the repository at this point in the history
#826)

In addition to the new implementation I deprecated the AliasBased, Zookeeper and JournalBased TSS implementations in 2.1.0.
  • Loading branch information
smolnar82 authored Feb 2, 2024
1 parent b855e0f commit afdb4cc
Show file tree
Hide file tree
Showing 31 changed files with 814 additions and 107 deletions.
7 changes: 4 additions & 3 deletions gateway-server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,10 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
Expand Down Expand Up @@ -467,19 +471,16 @@
<dependency>
<groupId>org.apache.knox</groupId>
<artifactId>gateway-shell</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derbynet</artifactId>
<scope>test</scope>
</dependency>

<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,14 @@ public class GatewayConfigImpl extends Configuration implements GatewayConfig {
/* property that specifies list of services for which we need to append service name to the X-Forward-Context header */
public static final String X_FORWARD_CONTEXT_HEADER_APPEND_SERVICES = GATEWAY_CONFIG_FILE_PREFIX + ".xforwarded.header.context.append.servicename";

private static final String TOKEN_STATE_SERVER_MANAGED = GATEWAY_CONFIG_FILE_PREFIX + ".knox.token.exp.server-managed";
private static final String USERS_CAN_SEE_ALL_TOKENS = GATEWAY_CONFIG_FILE_PREFIX + ".knox.token.management.users.can.see.all.tokens";
private static final String KNOX_TOKEN_PREFIX = GATEWAY_CONFIG_FILE_PREFIX + ".knox.token";
private static final String TOKEN_STATE_SERVER_MANAGED = KNOX_TOKEN_PREFIX + ".exp.server-managed";
private static final String USERS_CAN_SEE_ALL_TOKENS = KNOX_TOKEN_PREFIX + ".management.users.can.see.all.tokens";
private static final String SKIP_TOKEN_MIGRATION= KNOX_TOKEN_PREFIX + ".migration.skip";
private static final String ARCHIVE_MIGRATED_TOKENS= KNOX_TOKEN_PREFIX + ".migration.archive.tokens";
private static final String MIGRATE_EXPIRED_TOKENS= KNOX_TOKEN_PREFIX + ".migration.include.expired.tokens";
private static final String TOKEN_MIGRATION_PRINTS_VERBOSE_MESSAGES= KNOX_TOKEN_PREFIX + ".migration.verbose";
private static final String TOKEN_MIGRATION_PROGRESS_COUNT= KNOX_TOKEN_PREFIX + ".migration.progress.count";

private static final String CLOUDERA_MANAGER_DESCRIPTORS_MONITOR_INTERVAL = GATEWAY_CONFIG_FILE_PREFIX + ".cloudera.manager.descriptors.monitor.interval";
private static final String CLOUDERA_MANAGER_ADVANCED_SERVICE_DISCOVERY_CONF_MONITOR_INTERVAL = GATEWAY_CONFIG_FILE_PREFIX + ".cloudera.manager.advanced.service.discovery.config.monitor.interval";
Expand All @@ -295,12 +301,12 @@ public class GatewayConfigImpl extends Configuration implements GatewayConfig {
private static final long CLOUDERA_MANAGER_SERVICE_DISCOVERY_READ_TIMEOUT_DEFAULT = 10000;
private static final long CLOUDERA_MANAGER_SERVICE_DISCOVERY_WRITE_TIMEOUT_DEFAULT = 10000;

private static final String KNOX_TOKEN_EVICTION_INTERVAL = GATEWAY_CONFIG_FILE_PREFIX + ".knox.token.eviction.interval";
private static final String KNOX_TOKEN_EVICTION_GRACE_PERIOD = GATEWAY_CONFIG_FILE_PREFIX + ".knox.token.eviction.grace.period";
private static final String KNOX_TOKEN_ALIAS_PERSISTENCE_INTERVAL = GATEWAY_CONFIG_FILE_PREFIX + ".knox.token.state.alias.persistence.interval";
private static final String KNOX_TOKEN_PERMISSIVE_VALIDATION_ENABLED = GATEWAY_CONFIG_FILE_PREFIX + ".knox.token.permissive.validation";
private static final String KNOX_TOKEN_HASH_ALGORITHM = GATEWAY_CONFIG_FILE_PREFIX + ".knox.token.hash.algorithm";
public static final String KNOX_TOKEN_USER_LIMIT = GATEWAY_CONFIG_FILE_PREFIX + ".knox.token.limit.per.user";
private static final String KNOX_TOKEN_EVICTION_INTERVAL = KNOX_TOKEN_PREFIX + ".eviction.interval";
private static final String KNOX_TOKEN_EVICTION_GRACE_PERIOD = KNOX_TOKEN_PREFIX + ".eviction.grace.period";
private static final String KNOX_TOKEN_ALIAS_PERSISTENCE_INTERVAL = KNOX_TOKEN_PREFIX + ".state.alias.persistence.interval";
private static final String KNOX_TOKEN_PERMISSIVE_VALIDATION_ENABLED = KNOX_TOKEN_PREFIX + ".permissive.validation";
private static final String KNOX_TOKEN_HASH_ALGORITHM = KNOX_TOKEN_PREFIX + ".hash.algorithm";
public static final String KNOX_TOKEN_USER_LIMIT = KNOX_TOKEN_PREFIX + ".limit.per.user";
private static final long KNOX_TOKEN_EVICTION_INTERVAL_DEFAULT = TimeUnit.MINUTES.toSeconds(5);
private static final long KNOX_TOKEN_EVICTION_GRACE_PERIOD_DEFAULT = TimeUnit.HOURS.toSeconds(24);
private static final long KNOX_TOKEN_ALIAS_PERSISTENCE_INTERVAL_DEFAULT = TimeUnit.SECONDS.toSeconds(15);
Expand All @@ -318,11 +324,11 @@ public class GatewayConfigImpl extends Configuration implements GatewayConfig {
private static final String KNOX_INCOMING_XFORWARDED_ENABLED = "gateway.incoming.xforwarded.enabled";

//Gateway Database related properties
private static final String GATEWAY_DATABASE_TYPE = GATEWAY_CONFIG_FILE_PREFIX + ".database.type";
private static final String GATEWAY_DATABASE_CONN_URL = GATEWAY_CONFIG_FILE_PREFIX + ".database.connection.url";
private static final String GATEWAY_DATABASE_HOST = GATEWAY_CONFIG_FILE_PREFIX + ".database.host";
private static final String GATEWAY_DATABASE_PORT = GATEWAY_CONFIG_FILE_PREFIX + ".database.port";
private static final String GATEWAY_DATABASE_NAME = GATEWAY_CONFIG_FILE_PREFIX + ".database.name";
public static final String GATEWAY_DATABASE_TYPE = GATEWAY_CONFIG_FILE_PREFIX + ".database.type";
public static final String GATEWAY_DATABASE_CONN_URL = GATEWAY_CONFIG_FILE_PREFIX + ".database.connection.url";
public static final String GATEWAY_DATABASE_HOST = GATEWAY_CONFIG_FILE_PREFIX + ".database.host";
public static final String GATEWAY_DATABASE_PORT = GATEWAY_CONFIG_FILE_PREFIX + ".database.port";
public static final String GATEWAY_DATABASE_NAME = GATEWAY_CONFIG_FILE_PREFIX + ".database.name";
private static final String GATEWAY_DATABASE_SSL_ENABLED = GATEWAY_CONFIG_FILE_PREFIX + ".database.ssl.enabled";
private static final String GATEWAY_DATABASE_VERIFY_SERVER_CERT = GATEWAY_CONFIG_FILE_PREFIX + ".database.ssl.verify.server.cert";
private static final String GATEWAY_DATABASE_TRUSTSTORE_FILE = GATEWAY_CONFIG_FILE_PREFIX + ".database.ssl.truststore.file";
Expand Down Expand Up @@ -1534,4 +1540,29 @@ private Map<String, Collection<String>> getPathAliases(String qualifier) {
return pathAliases;
}

@Override
public boolean skipTokenMigration() {
return getBoolean(SKIP_TOKEN_MIGRATION, false);
}

@Override
public boolean archiveMigratedTokens() {
return getBoolean(ARCHIVE_MIGRATED_TOKENS, false);
}

@Override
public boolean migrateExpiredTokens() {
return getBoolean(MIGRATE_EXPIRED_TOKENS, false);
}

@Override
public boolean printVerboseTokenMigrationMessages() {
return getBoolean(TOKEN_MIGRATION_PRINTS_VERBOSE_MESSAGES, true);
}

@Override
public int getTokenMigrationProgressCount() {
return getInt(TOKEN_MIGRATION_PROGRESS_COUNT, 10);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ public void init(GatewayConfig config, Map<String,String> options) throws Servic
addService(ServiceType.CRYPTO_SERVICE, gatewayServiceFactory.create(this, ServiceType.CRYPTO_SERVICE, config, options));

addService(ServiceType.TOPOLOGY_SERVICE, gatewayServiceFactory.create(this, ServiceType.TOPOLOGY_SERVICE, config, options));

addService(ServiceType.TOKEN_STATE_SERVICE, gatewayServiceFactory.create(this, ServiceType.TOKEN_STATE_SERVICE, config, options));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.apache.knox.gateway.services.ServiceType;
import org.apache.knox.gateway.services.token.impl.AliasBasedTokenStateService;
import org.apache.knox.gateway.services.token.impl.DefaultTokenStateService;
import org.apache.knox.gateway.services.token.impl.DerbyDBTokenStateService;
import org.apache.knox.gateway.services.token.impl.JDBCTokenStateService;
import org.apache.knox.gateway.services.token.impl.JournalBasedTokenStateService;
import org.apache.knox.gateway.services.token.impl.ZookeeperTokenStateService;
Expand All @@ -45,9 +46,11 @@ protected Service createService(GatewayServices gatewayServices, ServiceType ser
throws ServiceLifecycleException {
Service service = null;
if (shouldCreateService(implementation)) {
if (matchesImplementation(implementation, DefaultTokenStateService.class)) {
if (matchesImplementation(implementation, DerbyDBTokenStateService.class, true)) {
service = useDerbyDatabaseTokenStateService(gatewayServices, gatewayConfig, options);
} else if (matchesImplementation(implementation, DefaultTokenStateService.class)) {
service = new DefaultTokenStateService();
} else if (matchesImplementation(implementation, AliasBasedTokenStateService.class, true)) {
} else if (matchesImplementation(implementation, AliasBasedTokenStateService.class)) {
service = new AliasBasedTokenStateService();
((AliasBasedTokenStateService) service).setAliasService(getAliasService(gatewayServices));
} else if (matchesImplementation(implementation, JournalBasedTokenStateService.class)) {
Expand All @@ -61,17 +64,30 @@ protected Service createService(GatewayServices gatewayServices, ServiceType ser
service.init(gatewayConfig, options);
} catch (ServiceLifecycleException e) {
LOG.errorInitializingService(implementation, e.getMessage(), e);
service = new AliasBasedTokenStateService();
((AliasBasedTokenStateService) service).setAliasService(getAliasService(gatewayServices));
service = useDerbyDatabaseTokenStateService(gatewayServices, gatewayConfig, options);
}
}

logServiceUsage(isEmptyDefaultImplementation(implementation) ? AliasBasedTokenStateService.class.getName() : implementation, serviceType);
logServiceUsage(service.getClass().getName(), serviceType);
}

return service;
}

private Service useDerbyDatabaseTokenStateService(GatewayServices gatewayServices, GatewayConfig gatewayConfig, Map<String, String> options) {
Service service;
try {
service = new DerbyDBTokenStateService();
((DerbyDBTokenStateService) service).setAliasService(getAliasService(gatewayServices));
((DerbyDBTokenStateService) service).setMasterService(getMasterService(gatewayServices));
service.init(gatewayConfig, options);
} catch (ServiceLifecycleException e) {
LOG.errorInitializingService(DerbyDBTokenStateService.class.getName(), e.getMessage(), e);
service = new DefaultTokenStateService();
}
return service;
}

@Override
protected ServiceType getServiceType() {
return ServiceType.TOKEN_STATE_SERVICE;
Expand All @@ -80,6 +96,6 @@ protected ServiceType getServiceType() {
@Override
protected Collection<String> getKnownImplementations() {
return unmodifiableList(asList(DefaultTokenStateService.class.getName(), AliasBasedTokenStateService.class.getName(), JournalBasedTokenStateService.class.getName(),
ZookeeperTokenStateService.class.getName(), JDBCTokenStateService.class.getName()));
ZookeeperTokenStateService.class.getName(), JDBCTokenStateService.class.getName(), DerbyDBTokenStateService.class.getName()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,15 @@

/**
* A TokenStateService implementation based on the AliasService.
*
* @deprecated Since 2.1.0
*/
public class AliasBasedTokenStateService extends AbstractPersistentTokenStateService implements TokenStatePeristerMonitorListener {

static final String TOKEN_ALIAS_SUFFIX_DELIM = "--";
static final String TOKEN_ISSUE_TIME_POSTFIX = TOKEN_ALIAS_SUFFIX_DELIM + "iss";
static final String TOKEN_MAX_LIFETIME_POSTFIX = TOKEN_ALIAS_SUFFIX_DELIM + "max";
static final String TOKEN_META_POSTFIX = TOKEN_ALIAS_SUFFIX_DELIM + "meta";
public static final String TOKEN_ISSUE_TIME_POSTFIX = TOKEN_ALIAS_SUFFIX_DELIM + "iss";
public static final String TOKEN_MAX_LIFETIME_POSTFIX = TOKEN_ALIAS_SUFFIX_DELIM + "max";
public static final String TOKEN_META_POSTFIX = TOKEN_ALIAS_SUFFIX_DELIM + "meta";

protected AliasService aliasService;

Expand All @@ -80,6 +82,7 @@ public void setAliasService(AliasService aliasService) {

@Override
public void init(final GatewayConfig config, final Map<String, String> options) throws ServiceLifecycleException {
log.deprecatedServiceUsage(this.getClass().getCanonicalName());
super.init(config, options);
if (aliasService == null) {
throw new ServiceLifecycleException("The required AliasService reference has not been set.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -457,4 +457,5 @@ private Collection<KnoxToken> fetchTokens(String userName, boolean createdBy) {
});
return tokens;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.knox.gateway.services.token.impl;

import static org.apache.knox.gateway.config.impl.GatewayConfigImpl.GATEWAY_DATABASE_NAME;
import static org.apache.knox.gateway.config.impl.GatewayConfigImpl.GATEWAY_DATABASE_TYPE;
import static org.apache.knox.gateway.services.security.AliasService.NO_CLUSTER_NAME;
import static org.apache.knox.gateway.util.JDBCUtils.DATABASE_PASSWORD_ALIAS_NAME;
import static org.apache.knox.gateway.util.JDBCUtils.DATABASE_USER_ALIAS_NAME;
import static org.apache.knox.gateway.util.JDBCUtils.DERBY_DB_TYPE;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.apache.hadoop.conf.Configuration;
import org.apache.knox.gateway.config.GatewayConfig;
import org.apache.knox.gateway.services.ServiceLifecycleException;
import org.apache.knox.gateway.services.security.MasterService;
import org.apache.knox.gateway.shell.jdbc.derby.DerbyDatabase;
import org.apache.knox.gateway.util.FileUtils;

public class DerbyDBTokenStateService extends JDBCTokenStateService {

public static final String DEFAULT_TOKEN_DB_USER_NAME = "knox";
public static final String DB_NAME = "tokens";

private DerbyDatabase derbyDatabase;
private Path derbyDatabaseFolder;
private MasterService masterService;

public void setMasterService(MasterService masterService) {
this.masterService = masterService;
}

@Override
public void init(GatewayConfig config, Map<String, String> options) throws ServiceLifecycleException {
try {
derbyDatabaseFolder = Paths.get(config.getGatewaySecurityDir(), DB_NAME);
startDerby();
((Configuration) config).set(GATEWAY_DATABASE_TYPE, DERBY_DB_TYPE);
((Configuration) config).set(GATEWAY_DATABASE_NAME, derbyDatabaseFolder.toString());
getAliasService().addAliasForCluster(NO_CLUSTER_NAME, DATABASE_USER_ALIAS_NAME, getDatabaseUserName());
getAliasService().addAliasForCluster(NO_CLUSTER_NAME, DATABASE_PASSWORD_ALIAS_NAME, getDatabasePassword());
super.init(config, options);

// we need the "x" permission too to be able to browse that folder (600 is not enough)
if (Files.exists(derbyDatabaseFolder)) {
FileUtils.chmod("700", derbyDatabaseFolder.toFile());
}
} catch (Exception e) {
throw new ServiceLifecycleException("Error while initiating DerbyDBTokenStateService: " + e, e);
}
}

private void startDerby() throws Exception {
derbyDatabase = new DerbyDatabase(derbyDatabaseFolder.toString());
derbyDatabase.create();
TimeUnit.SECONDS.sleep(1); // give a bit of time for the server to start
}

private String getDatabasePassword() throws Exception {
final char[] dbPasswordAliasValue = getAliasService().getPasswordFromAliasForGateway(DATABASE_PASSWORD_ALIAS_NAME);
return dbPasswordAliasValue != null ? new String(dbPasswordAliasValue) : new String(masterService.getMasterSecret());
}

private String getDatabaseUserName() throws Exception {
final char[] dbUserAliasValue = getAliasService().getPasswordFromAliasForGateway(DATABASE_USER_ALIAS_NAME);
return dbUserAliasValue != null ? new String(dbUserAliasValue) : DEFAULT_TOKEN_DB_USER_NAME;
}

@Override
public void stop() throws ServiceLifecycleException {
try {
if (derbyDatabase != null) {
derbyDatabase.shutdown();
}
} catch (Exception e) {
throw new ServiceLifecycleException("Error while shutting down Derby Database", e);
}
}

}
Loading

0 comments on commit afdb4cc

Please sign in to comment.