use of org.apache.thrift.transport.TSaslClientTransport in project accumulo by apache.
the class KerberosProxyIT method startMac.
@Before
public void startMac() throws Exception {
MiniClusterHarness harness = new MiniClusterHarness();
mac = harness.create(getClass().getName(), testName.getMethodName(), new PasswordToken("unused"), new MiniClusterConfigurationCallback() {
@Override
public void configureMiniCluster(MiniAccumuloConfigImpl cfg, Configuration coreSite) {
cfg.setNumTservers(1);
Map<String, String> siteCfg = cfg.getSiteConfig();
// Allow the proxy to impersonate the "root" Accumulo user and our one special user.
siteCfg.put(Property.INSTANCE_RPC_SASL_ALLOWED_USER_IMPERSONATION.getKey(), proxyPrincipal + ":" + kdc.getRootUser().getPrincipal() + "," + kdc.qualifyUser(PROXIED_USER1) + "," + kdc.qualifyUser(PROXIED_USER2));
siteCfg.put(Property.INSTANCE_RPC_SASL_ALLOWED_HOST_IMPERSONATION.getKey(), "*");
cfg.setSiteConfig(siteCfg);
}
}, kdc);
mac.start();
MiniAccumuloConfigImpl cfg = mac.getConfig();
// Generate Proxy configuration and start the proxy
proxyProcess = startProxy(cfg);
// Enabled kerberos auth
Configuration conf = new Configuration(false);
conf.set(CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION, "kerberos");
UserGroupInformation.setConfiguration(conf);
boolean success = false;
ClusterUser rootUser = kdc.getRootUser();
// Rely on the junit timeout rule
while (!success) {
UserGroupInformation ugi;
try {
ugi = UserGroupInformation.loginUserFromKeytabAndReturnUGI(rootUser.getPrincipal(), rootUser.getKeytab().getAbsolutePath());
} catch (IOException ex) {
log.info("Login as root is failing", ex);
Thread.sleep(3000);
continue;
}
TSocket socket = new TSocket(hostname, proxyPort);
log.info("Connecting to proxy with server primary '{}' running on {}", proxyPrimary, hostname);
TSaslClientTransport transport = new TSaslClientTransport("GSSAPI", null, proxyPrimary, hostname, Collections.singletonMap("javax.security.sasl.qop", "auth"), null, socket);
final UGIAssumingTransport ugiTransport = new UGIAssumingTransport(transport, ugi);
try {
// UGI transport will perform the doAs for us
ugiTransport.open();
success = true;
} catch (TTransportException e) {
Throwable cause = e.getCause();
if (null != cause && cause instanceof ConnectException) {
log.info("Proxy not yet up, waiting");
Thread.sleep(3000);
proxyProcess = checkProxyAndRestart(proxyProcess, cfg);
continue;
}
} finally {
if (null != ugiTransport) {
ugiTransport.close();
}
}
}
assertTrue("Failed to connect to the proxy repeatedly", success);
}
use of org.apache.thrift.transport.TSaslClientTransport in project accumulo by apache.
the class KerberosProxyIT method testProxyClient.
@Test
public void testProxyClient() throws Exception {
ClusterUser rootUser = kdc.getRootUser();
UserGroupInformation ugi = UserGroupInformation.loginUserFromKeytabAndReturnUGI(rootUser.getPrincipal(), rootUser.getKeytab().getAbsolutePath());
TSocket socket = new TSocket(hostname, proxyPort);
log.info("Connecting to proxy with server primary '{}' running on {}", proxyPrimary, hostname);
TSaslClientTransport transport = new TSaslClientTransport("GSSAPI", null, proxyPrimary, hostname, Collections.singletonMap("javax.security.sasl.qop", "auth"), null, socket);
final UGIAssumingTransport ugiTransport = new UGIAssumingTransport(transport, ugi);
// UGI transport will perform the doAs for us
ugiTransport.open();
AccumuloProxy.Client.Factory factory = new AccumuloProxy.Client.Factory();
Client client = factory.getClient(new TCompactProtocol(ugiTransport), new TCompactProtocol(ugiTransport));
// Will fail if the proxy can impersonate the client
ByteBuffer login = client.login(rootUser.getPrincipal(), Collections.<String, String>emptyMap());
// For all of the below actions, the proxy user doesn't have permission to do any of them, but the client user does.
// The fact that any of them actually run tells us that impersonation is working.
// Create a table
String table = "table";
if (!client.tableExists(login, table)) {
client.createTable(login, table, true, TimeType.MILLIS);
}
// Write two records to the table
String writer = client.createWriter(login, table, new WriterOptions());
Map<ByteBuffer, List<ColumnUpdate>> updates = new HashMap<>();
ColumnUpdate update = new ColumnUpdate(ByteBuffer.wrap("cf1".getBytes(UTF_8)), ByteBuffer.wrap("cq1".getBytes(UTF_8)));
update.setValue(ByteBuffer.wrap("value1".getBytes(UTF_8)));
updates.put(ByteBuffer.wrap("row1".getBytes(UTF_8)), Collections.singletonList(update));
update = new ColumnUpdate(ByteBuffer.wrap("cf2".getBytes(UTF_8)), ByteBuffer.wrap("cq2".getBytes(UTF_8)));
update.setValue(ByteBuffer.wrap("value2".getBytes(UTF_8)));
updates.put(ByteBuffer.wrap("row2".getBytes(UTF_8)), Collections.singletonList(update));
client.update(writer, updates);
// Flush and close the writer
client.flush(writer);
client.closeWriter(writer);
// Open a scanner to the table
String scanner = client.createScanner(login, table, new ScanOptions());
ScanResult results = client.nextK(scanner, 10);
assertEquals(2, results.getResults().size());
// Check the first key-value
KeyValue kv = results.getResults().get(0);
Key k = kv.key;
ByteBuffer v = kv.value;
assertEquals(ByteBuffer.wrap("row1".getBytes(UTF_8)), k.row);
assertEquals(ByteBuffer.wrap("cf1".getBytes(UTF_8)), k.colFamily);
assertEquals(ByteBuffer.wrap("cq1".getBytes(UTF_8)), k.colQualifier);
assertEquals(ByteBuffer.wrap(new byte[0]), k.colVisibility);
assertEquals(ByteBuffer.wrap("value1".getBytes(UTF_8)), v);
// And then the second
kv = results.getResults().get(1);
k = kv.key;
v = kv.value;
assertEquals(ByteBuffer.wrap("row2".getBytes(UTF_8)), k.row);
assertEquals(ByteBuffer.wrap("cf2".getBytes(UTF_8)), k.colFamily);
assertEquals(ByteBuffer.wrap("cq2".getBytes(UTF_8)), k.colQualifier);
assertEquals(ByteBuffer.wrap(new byte[0]), k.colVisibility);
assertEquals(ByteBuffer.wrap("value2".getBytes(UTF_8)), v);
// Close the scanner
client.closeScanner(scanner);
ugiTransport.close();
}
use of org.apache.thrift.transport.TSaslClientTransport in project accumulo by apache.
the class KerberosProxyIT method testDisallowedClientForImpersonation.
@Test
public void testDisallowedClientForImpersonation() throws Exception {
String user = testName.getMethodName();
File keytab = new File(kdc.getKeytabDir(), user + ".keytab");
kdc.createPrincipal(keytab, user);
// Login as the new user
UserGroupInformation ugi = UserGroupInformation.loginUserFromKeytabAndReturnUGI(user, keytab.getAbsolutePath());
log.info("Logged in as {}", ugi);
// Expect an AccumuloSecurityException
thrown.expect(AccumuloSecurityException.class);
// Error msg would look like:
//
// org.apache.accumulo.core.client.AccumuloSecurityException: Error BAD_CREDENTIALS for user Principal in credentials object should match kerberos
// principal.
// Expected 'proxy/hw10447.local@EXAMPLE.COM' but was 'testDisallowedClientForImpersonation@EXAMPLE.COM' - Username or Password is Invalid)
thrown.expect(new ThriftExceptionMatchesPattern(".*Error BAD_CREDENTIALS.*"));
thrown.expect(new ThriftExceptionMatchesPattern(".*Expected '" + proxyPrincipal + "' but was '" + kdc.qualifyUser(user) + "'.*"));
TSocket socket = new TSocket(hostname, proxyPort);
log.info("Connecting to proxy with server primary '{}' running on {}", proxyPrimary, hostname);
// Should fail to open the tran
TSaslClientTransport transport = new TSaslClientTransport("GSSAPI", null, proxyPrimary, hostname, Collections.singletonMap("javax.security.sasl.qop", "auth"), null, socket);
final UGIAssumingTransport ugiTransport = new UGIAssumingTransport(transport, ugi);
// UGI transport will perform the doAs for us
ugiTransport.open();
AccumuloProxy.Client.Factory factory = new AccumuloProxy.Client.Factory();
Client client = factory.getClient(new TCompactProtocol(ugiTransport), new TCompactProtocol(ugiTransport));
// Will fail because the proxy can't impersonate this user (per the site configuration)
try {
client.login(kdc.qualifyUser(user), Collections.<String, String>emptyMap());
} finally {
if (null != ugiTransport) {
ugiTransport.close();
}
}
}
use of org.apache.thrift.transport.TSaslClientTransport in project accumulo by apache.
the class KerberosProxyIT method testMismatchPrincipals.
@Test
public void testMismatchPrincipals() throws Exception {
ClusterUser rootUser = kdc.getRootUser();
// Should get an AccumuloSecurityException and the given message
thrown.expect(AccumuloSecurityException.class);
thrown.expect(new ThriftExceptionMatchesPattern(ProxyServer.RPC_ACCUMULO_PRINCIPAL_MISMATCH_MSG));
// Make a new user
String user = testName.getMethodName();
File keytab = new File(kdc.getKeytabDir(), user + ".keytab");
kdc.createPrincipal(keytab, user);
// Login as the new user
UserGroupInformation ugi = UserGroupInformation.loginUserFromKeytabAndReturnUGI(user, keytab.getAbsolutePath());
log.info("Logged in as {}", ugi);
TSocket socket = new TSocket(hostname, proxyPort);
log.info("Connecting to proxy with server primary '{}' running on {}", proxyPrimary, hostname);
// Should fail to open the tran
TSaslClientTransport transport = new TSaslClientTransport("GSSAPI", null, proxyPrimary, hostname, Collections.singletonMap("javax.security.sasl.qop", "auth"), null, socket);
final UGIAssumingTransport ugiTransport = new UGIAssumingTransport(transport, ugi);
// UGI transport will perform the doAs for us
ugiTransport.open();
AccumuloProxy.Client.Factory factory = new AccumuloProxy.Client.Factory();
Client client = factory.getClient(new TCompactProtocol(ugiTransport), new TCompactProtocol(ugiTransport));
// Accumulo should let this through -- we need to rely on the proxy to dump me before talking to accumulo
try {
client.login(rootUser.getPrincipal(), Collections.<String, String>emptyMap());
} finally {
if (null != ugiTransport) {
ugiTransport.close();
}
}
}
use of org.apache.thrift.transport.TSaslClientTransport in project accumulo by apache.
the class ThriftUtil method createClientTransport.
/**
* Create a TTransport for clients to the given address with the provided socket timeout and session-layer configuration
*
* @param address
* Server address to connect to
* @param timeout
* Client socket timeout
* @param sslParams
* RPC options for SSL servers
* @param saslParams
* RPC options for SASL servers
* @return An open TTransport which must be closed when finished
*/
public static TTransport createClientTransport(HostAndPort address, int timeout, SslConnectionParams sslParams, SaslConnectionParams saslParams) throws TTransportException {
boolean success = false;
TTransport transport = null;
try {
if (sslParams != null) {
// The check in AccumuloServerContext ensures that servers are brought up with sane configurations, but we also want to validate clients
if (null != saslParams) {
throw new IllegalStateException("Cannot use both SSL and SASL");
}
log.trace("Creating SSL client transport");
// TSSLTransportFactory handles timeout 0 -> forever natively
if (sslParams.useJsse()) {
transport = TSSLTransportFactory.getClientSocket(address.getHost(), address.getPort(), timeout);
} else {
// JDK6's factory doesn't appear to pass the protocol onto the Socket properly so we have
// to do some magic to make sure that happens. Not an issue in JDK7
// Taken from thrift-0.9.1 to make the SSLContext
SSLContext sslContext = createSSLContext(sslParams);
// Create the factory from it
SSLSocketFactory sslSockFactory = sslContext.getSocketFactory();
// Wrap the real factory with our own that will set the protocol on the Socket before returning it
ProtocolOverridingSSLSocketFactory wrappingSslSockFactory = new ProtocolOverridingSSLSocketFactory(sslSockFactory, new String[] { sslParams.getClientProtocol() });
// Create the TSocket from that
transport = createClient(wrappingSslSockFactory, address.getHost(), address.getPort(), timeout);
// TSSLTransportFactory leaves transports open, so no need to open here
}
transport = ThriftUtil.transportFactory().getTransport(transport);
} else if (null != saslParams) {
if (!UserGroupInformation.isSecurityEnabled()) {
throw new IllegalStateException("Expected Kerberos security to be enabled if SASL is in use");
}
log.trace("Creating SASL connection to {}:{}", address.getHost(), address.getPort());
// Make sure a timeout is set
try {
transport = TTimeoutTransport.create(address, timeout);
} catch (IOException e) {
log.warn("Failed to open transport to {}", address);
throw new TTransportException(e);
}
try {
// Log in via UGI, ensures we have logged in with our KRB credentials
final UserGroupInformation currentUser = UserGroupInformation.getCurrentUser();
final UserGroupInformation userForRpc;
if (AuthenticationMethod.PROXY == currentUser.getAuthenticationMethod()) {
// that the current user is the user that has some credentials.
if (currentUser.getRealUser() != null) {
userForRpc = currentUser.getRealUser();
log.trace("{} is a proxy user, using real user instead {}", currentUser, userForRpc);
} else {
// The current user has no credentials, let it fail naturally at the RPC layer (no ticket)
// We know this won't work, but we can't do anything else
log.warn("The current user is a proxy user but there is no underlying real user (likely that RPCs will fail): {}", currentUser);
userForRpc = currentUser;
}
} else {
// The normal case: the current user has its own ticket
userForRpc = currentUser;
}
// Is this pricey enough that we want to cache it?
final String hostname = InetAddress.getByName(address.getHost()).getCanonicalHostName();
final SaslMechanism mechanism = saslParams.getMechanism();
log.trace("Opening transport to server as {} to {}/{} using {}", userForRpc, saslParams.getKerberosServerPrimary(), hostname, mechanism);
// Create the client SASL transport using the information for the server
// Despite the 'protocol' argument seeming to be useless, it *must* be the primary of the server being connected to
transport = new TSaslClientTransport(mechanism.getMechanismName(), null, saslParams.getKerberosServerPrimary(), hostname, saslParams.getSaslProperties(), saslParams.getCallbackHandler(), transport);
// Wrap it all in a processor which will run with a doAs the current user
transport = new UGIAssumingTransport(transport, userForRpc);
// Open the transport
transport.open();
} catch (TTransportException e) {
log.warn("Failed to open SASL transport", e);
// We might have had a valid ticket, but it expired. We'll let the caller retry, but we will attempt to re-login to make the next attempt work.
// Sadly, we have no way to determine the actual reason we got this TTransportException other than inspecting the exception msg.
log.debug("Caught TTransportException opening SASL transport, checking if re-login is necessary before propagating the exception.");
attemptClientReLogin();
throw e;
} catch (IOException e) {
log.warn("Failed to open SASL transport", e);
throw new TTransportException(e);
}
} else {
log.trace("Opening normal transport");
if (timeout == 0) {
transport = new TSocket(address.getHost(), address.getPort());
transport.open();
} else {
try {
transport = TTimeoutTransport.create(address, timeout);
} catch (IOException ex) {
log.warn("Failed to open transport to {}", address);
throw new TTransportException(ex);
}
// Open the transport
transport.open();
}
transport = ThriftUtil.transportFactory().getTransport(transport);
}
success = true;
} finally {
if (!success && transport != null) {
transport.close();
}
}
return transport;
}
Aggregations