| /** |
| * 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.slider.server.services.security; |
| |
| import com.google.inject.Singleton; |
| import org.apache.commons.io.FileUtils; |
| import org.apache.slider.common.SliderKeys; |
| import org.apache.slider.core.conf.MapOperations; |
| import org.apache.slider.core.exceptions.SliderException; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.nio.charset.Charset; |
| import java.text.MessageFormat; |
| |
| @Singleton |
| public class CertificateManager { |
| |
| private static final Logger LOG = |
| LoggerFactory.getLogger(CertificateManager.class); |
| |
| private static final String GEN_SRVR_KEY = "openssl genrsa -des3 " + |
| "-passout pass:{0} -out {1}/{2} 4096 "; |
| private static final String GEN_SRVR_REQ = "openssl req -passin pass:{0} " + |
| "-new -key {1}/{2} -out {1}/{5} -batch"; |
| private static final String SIGN_SRVR_CRT = "openssl ca -create_serial " + |
| "-out {1}/{3} -days 365 -keyfile {1}/{2} -key {0} -selfsign " + |
| "-extensions jdk7_ca -config {1}/ca.config -batch " + |
| "-infiles {1}/{5}"; |
| private static final String EXPRT_KSTR = "openssl pkcs12 -export" + |
| " -in {1}/{3} -inkey {1}/{2} -certfile {1}/{3} -out {1}/{4} " + |
| "-password pass:{0} -passin pass:{0} \n"; |
| private static final String REVOKE_AGENT_CRT = "openssl ca " + |
| "-config {0}/ca.config -keyfile {0}/{4} -revoke {0}/{2} -batch " + |
| "-passin pass:{3} -cert {0}/{5}"; |
| private static final String SIGN_AGENT_CRT = "openssl ca -config " + |
| "{0}/ca.config -in {0}/{1} -out {0}/{2} -batch -passin pass:{3} " + |
| "-keyfile {0}/{4} -cert {0}/{5}"; |
| private static final String GEN_AGENT_KEY="openssl req -new -newkey " + |
| "rsa:1024 -nodes -keyout {0}/{2}.key -subj /OU={1}/CN={2} -out {0}/{2}.csr"; |
| private String passphrase; |
| |
| /** |
| * Verify that root certificate exists, generate it otherwise. |
| */ |
| public void initialize(MapOperations compOperations) { |
| SecurityUtils.initializeSecurityParameters(compOperations); |
| |
| LOG.info("Initialization of root certificate"); |
| boolean certExists = isCertExists(); |
| LOG.info("Certificate exists:" + certExists); |
| |
| if (!certExists) { |
| generateServerCertificate(); |
| } |
| |
| } |
| |
| /** |
| * Checks root certificate state. |
| * @return "true" if certificate exists |
| */ |
| private boolean isCertExists() { |
| |
| String srvrKstrDir = SecurityUtils.getSecurityDir(); |
| String srvrCrtName = SliderKeys.CRT_FILE_NAME; |
| File certFile = new File(srvrKstrDir + File.separator + srvrCrtName); |
| LOG.debug("srvrKstrDir = " + srvrKstrDir); |
| LOG.debug("srvrCrtName = " + srvrCrtName); |
| LOG.debug("certFile = " + certFile.getAbsolutePath()); |
| |
| return certFile.exists(); |
| } |
| |
| public void setPassphrase(String passphrase) { |
| this.passphrase = passphrase; |
| } |
| |
| class StreamConsumer extends Thread |
| { |
| InputStream is; |
| boolean logOutput; |
| |
| StreamConsumer(InputStream is, boolean logOutput) |
| { |
| this.is = is; |
| this.logOutput = logOutput; |
| } |
| |
| StreamConsumer(InputStream is) |
| { |
| this(is, false); |
| } |
| |
| public void run() |
| { |
| try |
| { |
| InputStreamReader isr = new InputStreamReader(is, |
| Charset.forName("UTF8")); |
| BufferedReader br = new BufferedReader(isr); |
| String line; |
| while ( (line = br.readLine()) != null) |
| if (logOutput) { |
| LOG.info(line); |
| } |
| } catch (IOException e) |
| { |
| LOG.error("Error during processing of process stream", e); |
| } |
| } |
| } |
| |
| |
| /** |
| * Runs os command |
| * |
| * @return command execution exit code |
| */ |
| private int runCommand(String command) throws SliderException { |
| int exitCode = -1; |
| String line = null; |
| Process process = null; |
| BufferedReader br= null; |
| try { |
| process = Runtime.getRuntime().exec(command); |
| StreamConsumer outputConsumer = |
| new StreamConsumer(process.getInputStream(), true); |
| StreamConsumer errorConsumer = |
| new StreamConsumer(process.getErrorStream()); |
| |
| outputConsumer.start(); |
| errorConsumer.start(); |
| |
| try { |
| process.waitFor(); |
| SecurityUtils.logOpenSslExitCode(command, process.exitValue()); |
| exitCode = process.exitValue(); |
| if (exitCode != 0) { |
| throw new SliderException(exitCode, "Error running command %s", command); |
| } |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| } |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } finally { |
| if (br != null) { |
| try { |
| br.close(); |
| } catch (IOException ioe) { |
| ioe.printStackTrace(); |
| } |
| } |
| } |
| |
| return exitCode;//some exception occurred |
| |
| } |
| |
| public synchronized void generateAgentCertificate(String agentHostname, String containerId) { |
| LOG.info("Generation of agent certificate for {}", agentHostname); |
| |
| String srvrKstrDir = SecurityUtils.getSecurityDir(); |
| Object[] scriptArgs = {srvrKstrDir, agentHostname, containerId}; |
| |
| try { |
| String command = MessageFormat.format(GEN_AGENT_KEY, scriptArgs); |
| runCommand(command); |
| |
| signAgentCertificate(containerId); |
| |
| } catch (SliderException e) { |
| LOG.error("Error generating the agent certificate", e); |
| } |
| } |
| |
| private void generateServerCertificate(){ |
| LOG.info("Generation of server certificate"); |
| |
| String srvrKstrDir = SecurityUtils.getSecurityDir(); |
| String srvrCrtName = SliderKeys.CRT_FILE_NAME; |
| String srvrCsrName = SliderKeys.CSR_FILE_NAME; |
| String srvrKeyName = SliderKeys.KEY_FILE_NAME; |
| String kstrName = SliderKeys.KEYSTORE_FILE_NAME; |
| String srvrCrtPass = SecurityUtils.getKeystorePass(); |
| |
| Object[] scriptArgs = {srvrCrtPass, srvrKstrDir, srvrKeyName, |
| srvrCrtName, kstrName, srvrCsrName}; |
| |
| try { |
| String command = MessageFormat.format(GEN_SRVR_KEY,scriptArgs); |
| runCommand(command); |
| |
| command = MessageFormat.format(GEN_SRVR_REQ,scriptArgs); |
| runCommand(command); |
| |
| command = MessageFormat.format(SIGN_SRVR_CRT,scriptArgs); |
| runCommand(command); |
| |
| command = MessageFormat.format(EXPRT_KSTR,scriptArgs); |
| runCommand(command); |
| } catch (SliderException e) { |
| LOG.error("Error generating the server certificate", e); |
| } |
| |
| } |
| |
| /** |
| * Returns server certificate content |
| * @return string with server certificate content |
| */ |
| public String getServerCert() { |
| File certFile = getServerCertficateFilePath(); |
| String srvrCrtContent = null; |
| try { |
| srvrCrtContent = FileUtils.readFileToString(certFile); |
| } catch (IOException e) { |
| LOG.error(e.getMessage()); |
| } |
| return srvrCrtContent; |
| } |
| |
| public static File getServerCertficateFilePath() { |
| return new File(SecurityUtils.getSecurityDir() + |
| File.separator + SliderKeys.CRT_FILE_NAME); |
| } |
| |
| public static File getAgentCertficateFilePath(String containerId) { |
| return new File(SecurityUtils.getSecurityDir() + |
| File.separator + containerId + ".crt"); |
| } |
| |
| public static File getAgentKeyFilePath(String containerId) { |
| return new File(SecurityUtils.getSecurityDir() + |
| File.separator + containerId + ".key"); |
| } |
| |
| /** |
| * Signs agent certificate |
| * Adds agent certificate to server keystore |
| * @return string with agent signed certificate content |
| */ |
| public synchronized SignCertResponse signAgentCrt(String agentHostname, |
| String agentCrtReqContent, |
| String passphraseAgent) { |
| SignCertResponse response = new SignCertResponse(); |
| LOG.info("Signing of agent certificate"); |
| LOG.info("Verifying passphrase"); |
| |
| if (!this.passphrase.equals(passphraseAgent.trim())) { |
| LOG.warn("Incorrect passphrase from the agent"); |
| response.setResult(SignCertResponse.ERROR_STATUS); |
| response.setMessage("Incorrect passphrase from the agent"); |
| return response; |
| } |
| |
| String srvrKstrDir = SecurityUtils.getSecurityDir(); |
| String srvrCrtPass = SecurityUtils.getKeystorePass(); |
| String srvrCrtName = SliderKeys.CRT_FILE_NAME; |
| String srvrKeyName = SliderKeys.KEY_FILE_NAME; |
| String agentCrtReqName = agentHostname + ".csr"; |
| String agentCrtName = agentHostname + ".crt"; |
| |
| Object[] scriptArgs = {srvrKstrDir, agentCrtReqName, agentCrtName, |
| srvrCrtPass, srvrKeyName, srvrCrtName}; |
| |
| //Revoke previous agent certificate if exists |
| File agentCrtFile = new File(srvrKstrDir + File.separator + agentCrtName); |
| |
| String command = null; |
| if (agentCrtFile.exists()) { |
| LOG.info("Revoking of " + agentHostname + " certificate."); |
| command = MessageFormat.format(REVOKE_AGENT_CRT, scriptArgs); |
| try { |
| runCommand(command); |
| } catch (SliderException e) { |
| int commandExitCode = e.getExitCode(); |
| response.setResult(SignCertResponse.ERROR_STATUS); |
| response.setMessage( |
| SecurityUtils.getOpenSslCommandResult(command, commandExitCode)); |
| return response; |
| } |
| } |
| |
| File agentCrtReqFile = new File(srvrKstrDir + File.separator + |
| agentCrtReqName); |
| try { |
| FileUtils.writeStringToFile(agentCrtReqFile, agentCrtReqContent); |
| } catch (IOException e1) { |
| // TODO Auto-generated catch block |
| e1.printStackTrace(); |
| } |
| |
| command = MessageFormat.format(SIGN_AGENT_CRT, scriptArgs); |
| |
| LOG.debug(SecurityUtils.hideOpenSslPassword(command)); |
| try { |
| runCommand(command); |
| } catch (SliderException e) { |
| int commandExitCode = e.getExitCode(); |
| response.setResult(SignCertResponse.ERROR_STATUS); |
| response.setMessage( |
| SecurityUtils.getOpenSslCommandResult(command, commandExitCode)); |
| return response; |
| } |
| |
| String agentCrtContent = ""; |
| try { |
| agentCrtContent = FileUtils.readFileToString(agentCrtFile); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| LOG.error("Error reading signed agent certificate"); |
| response.setResult(SignCertResponse.ERROR_STATUS); |
| response.setMessage("Error reading signed agent certificate"); |
| return response; |
| } |
| response.setResult(SignCertResponse.OK_STATUS); |
| response.setSignedCa(agentCrtContent); |
| //LOG.info(ShellCommandUtil.getOpenSslCommandResult(command, commandExitCode)); |
| return response; |
| } |
| |
| private String signAgentCertificate (String containerId) |
| throws SliderException { |
| String srvrKstrDir = SecurityUtils.getSecurityDir(); |
| String srvrCrtPass = SecurityUtils.getKeystorePass(); |
| String srvrCrtName = SliderKeys.CRT_FILE_NAME; |
| String srvrKeyName = SliderKeys.KEY_FILE_NAME; |
| String agentCrtReqName = containerId + ".csr"; |
| String agentCrtName = containerId + ".crt"; |
| |
| Object[] scriptArgs = {srvrKstrDir, agentCrtReqName, agentCrtName, |
| srvrCrtPass, srvrKeyName, srvrCrtName}; |
| |
| //Revoke previous agent certificate if exists |
| File agentCrtFile = new File(srvrKstrDir + File.separator + agentCrtName); |
| |
| String command; |
| if (agentCrtFile.exists()) { |
| LOG.info("Revoking of " + containerId + " certificate."); |
| command = MessageFormat.format(REVOKE_AGENT_CRT, scriptArgs); |
| runCommand(command); |
| } |
| |
| command = MessageFormat.format(SIGN_AGENT_CRT, scriptArgs); |
| |
| LOG.debug(SecurityUtils.hideOpenSslPassword(command)); |
| runCommand(command); |
| |
| return agentCrtName; |
| |
| } |
| } |