blob: 1ee5e21ba02da288d110303c6250eddce63dfcba [file] [log] [blame]
/*
* 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.ranger.authentication;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import org.apache.log4j.Logger;
import org.apache.ranger.credentialapi.CredentialReader;
import org.apache.ranger.plugin.util.XMLUtils;
import org.apache.ranger.usergroupsync.UserGroupSync;
public class UnixAuthenticationService {
private static final Logger LOG = Logger.getLogger(UnixAuthenticationService.class);
private static final String serviceName = "UnixAuthenticationService";
private static final String SSL_ALGORITHM = "TLS";
private static final String REMOTE_LOGIN_AUTH_SERVICE_PORT_PARAM = "ranger.usersync.port";
private static final String SSL_KEYSTORE_PATH_PARAM = "ranger.usersync.keystore.file";
private static final String SSL_TRUSTSTORE_PATH_PARAM = "ranger.usersync.truststore.file";
private static final String SSL_KEYSTORE_PATH_PASSWORD_ALIAS = "usersync.ssl.key.password";
private static final String SSL_TRUSTSTORE_PATH_PASSWORD_ALIAS = "usersync.ssl.truststore.password";
private static final String CRED_VALIDATOR_PROG = "ranger.usersync.passwordvalidator.path";
private static final String ADMIN_USER_LIST_PARAM = "admin.users";
private static final String ADMIN_ROLE_LIST_PARAM = "admin.roleNames";
private static final String SSL_ENABLED_PARAM = "ranger.usersync.ssl";
private static final String CREDSTORE_FILENAME_PARAM = "ranger.usersync.credstore.filename";
private String keyStorePath;
private List<String> enabledProtocolsList;
private String keyStorePathPassword;
private String trustStorePath;
private String trustStorePathPassword;
private List<String> adminUserList = new ArrayList<String>();
private String adminRoleNames;
private int portNum;
private boolean SSLEnabled = false;
static private boolean enableUnixAuth = false;
private static final String[] UGSYNC_CONFIG_XML_FILES = { "ranger-ugsync-default.xml", "ranger-ugsync-site.xml" };
public static void main(String[] args) {
if (args.length > 0) {
for (String s : args) {
if ("-enableUnixAuth".equalsIgnoreCase(s)) {
enableUnixAuth = true;
break;
}
}
}
UnixAuthenticationService service = new UnixAuthenticationService();
service.run();
}
public UnixAuthenticationService() {
}
public void run() {
try {
LOG.info("Starting User Sync Service!");
startUnixUserGroupSyncProcess();
Thread.sleep(5000);
if (enableUnixAuth) {
LOG.info("Enabling Unix Auth Service!");
init();
startService();
} else {
LOG.info("Unix Auth Service Disabled!");
}
}
catch(Throwable t) {
LOG.error("ERROR: Service: " + serviceName , t);
}
finally {
LOG.info("Service: " + serviceName + " - STOPPED.");
}
}
private void startUnixUserGroupSyncProcess() {
//
// Start the synchronization service ...
//
UserGroupSync syncProc = new UserGroupSync();
Thread newSyncProcThread = new Thread(syncProc);
newSyncProcThread.setName("UnixUserSyncThread");
// If this thread is set as daemon, then the entire process will terminate if enableUnixAuth is false
// Therefore this is marked as non-daemon thread. Don't change the following line
newSyncProcThread.setDaemon(false);
newSyncProcThread.start();
}
//TODO: add more validation code
private void init() throws Throwable {
Properties prop = new Properties();
for (String fn : UGSYNC_CONFIG_XML_FILES ) {
XMLUtils.loadConfig(fn, prop);
}
String credStoreFileName = prop.getProperty(CREDSTORE_FILENAME_PARAM);
keyStorePath = prop.getProperty(SSL_KEYSTORE_PATH_PARAM);
if (credStoreFileName == null) {
throw new RuntimeException("Credential file is not defined. param = [" + CREDSTORE_FILENAME_PARAM + "]");
}
File credFile = new File(credStoreFileName);
if (! credFile.exists()) {
throw new RuntimeException("Credential file [" + credStoreFileName + "]: does not exists." );
}
if ( ! credFile.canRead() ) {
throw new RuntimeException("Credential file [" + credStoreFileName + "]: can not be read." );
}
keyStorePathPassword = CredentialReader.getDecryptedString(credStoreFileName, SSL_KEYSTORE_PATH_PASSWORD_ALIAS);
trustStorePathPassword = CredentialReader.getDecryptedString(credStoreFileName,SSL_TRUSTSTORE_PATH_PASSWORD_ALIAS);
trustStorePath = prop.getProperty(SSL_TRUSTSTORE_PATH_PARAM);
portNum = Integer.parseInt(prop.getProperty(REMOTE_LOGIN_AUTH_SERVICE_PORT_PARAM));
String validatorProg = prop.getProperty(CRED_VALIDATOR_PROG);
if (validatorProg != null) {
PasswordValidator.setValidatorProgram(validatorProg);
}
String adminUsers = prop.getProperty(ADMIN_USER_LIST_PARAM);
if (adminUsers != null && adminUsers.trim().length() > 0) {
for(String u : adminUsers.split(",")) {
LOG.info("Adding Admin User:" + u.trim());
adminUserList.add(u.trim());
}
PasswordValidator.setAdminUserList(adminUserList);
}
adminRoleNames = prop.getProperty(ADMIN_ROLE_LIST_PARAM);
if (adminRoleNames != null) {
LOG.info("Adding Admin Group:" + adminRoleNames);
PasswordValidator.setAdminRoleNames(adminRoleNames);
}
String SSLEnabledProp = prop.getProperty(SSL_ENABLED_PARAM);
SSLEnabled = (SSLEnabledProp != null && (SSLEnabledProp.equalsIgnoreCase("true")));
String defaultEnabledProtocols = "SSLv2Hello, TLSv1, TLSv1.1, TLSv1.2";
String enabledProtocols = prop.getProperty("ranger.usersync.https.ssl.enabled.protocols", defaultEnabledProtocols);
enabledProtocolsList=new ArrayList<String>(Arrays.asList(enabledProtocols.toUpperCase().trim().split("\\s*,\\s*")));
// LOG.info("Key:" + keyStorePath);
// LOG.info("KeyPassword:" + keyStorePathPassword);
// LOG.info("TrustStore:" + trustStorePath);
// LOG.info("PortNum:" + portNum);
// LOG.info("ValidatorProg:" + validatorProg);
}
public void startService() throws Throwable {
SSLContext context = SSLContext.getInstance(SSL_ALGORITHM);
KeyManager[] km = null;
if (keyStorePath != null && ! keyStorePath.isEmpty()) {
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream in = null;
in = getFileInputStream(keyStorePath);
try {
if (keyStorePathPassword == null) {
keyStorePathPassword = "";
}
ks.load(in, keyStorePathPassword.toCharArray());
}
finally {
if (in != null) {
in.close();
}
}
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, keyStorePathPassword.toCharArray());
km = kmf.getKeyManagers();
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore trustStoreKeyStore = null;
if (trustStorePath != null && ! trustStorePath.isEmpty()) {
trustStoreKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream in = null;
in = getFileInputStream(trustStorePath);
try {
if (trustStorePathPassword == null) {
trustStorePathPassword = "";
}
trustStoreKeyStore.load(in, trustStorePathPassword.toCharArray());
}
finally {
if (in != null) {
in.close();
}
}
}
trustManagerFactory.init(trustStoreKeyStore);
TrustManager[] tm = trustManagerFactory.getTrustManagers();
SecureRandom random = new SecureRandom();
context.init(km, tm, random);
SSLServerSocketFactory sf = context.getServerSocketFactory();
ServerSocket socket = (SSLEnabled ? sf.createServerSocket(portNum) : new ServerSocket(portNum) );
if (SSLEnabled) {
SSLServerSocket secureSocket = (SSLServerSocket) socket;
String[] protocols = secureSocket.getEnabledProtocols();
Set<String> allowedProtocols = new HashSet<String>();
for(String ep : protocols) {
if (enabledProtocolsList.contains(ep.toUpperCase())){
LOG.info("Enabling Protocol: [" + ep + "]");
allowedProtocols.add(ep);
}
else {
LOG.info("Disabling Protocol: [" + ep + "]");
}
}
if (!allowedProtocols.isEmpty()) {
secureSocket.setEnabledProtocols(allowedProtocols.toArray(new String[0]));
}
}
Socket client = null;
try {
while ( (client = socket.accept()) != null ) {
Thread clientValidatorThread = new Thread(new PasswordValidator(client));
clientValidatorThread.start();
}
} catch (IOException e) {
socket.close();
throw(e);
}
}
private InputStream getFileInputStream(String path) throws FileNotFoundException {
InputStream ret = null;
File f = new File(path);
if (f.exists()) {
ret = new FileInputStream(f);
}
else {
ret = getClass().getResourceAsStream(path);
if (ret == null) {
ret = getClass().getResourceAsStream(File.separator + path);
}
}
return ret;
}
}