package com.sequenceiq.periscope.service.security;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import javax.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.sequenceiq.cloudbreak.client.CloudbreakClient;
import com.sequenceiq.periscope.domain.Cluster;
import com.sequenceiq.periscope.domain.SecurityConfig;
import com.sequenceiq.periscope.model.TlsConfiguration;
import com.sequenceiq.periscope.repository.SecurityConfigRepository;
@Service
public class TlsSecurityService {
private static final Logger LOGGER = LoggerFactory.getLogger(TlsSecurityService.class);
private static final String CERT_DIR = "/certs";
private static final String KEY_FILE = "key.pem";
private static final String CERT_FILE = "cert.pem";
private static final String SERVER_CERT_FILE = "ca.pem";
private static final String DIR_PREFIX = "/stack-";
@Value("${periscope.cert.dir:" + CERT_DIR + "}")
private String certDir;
@Value("#{'${periscope.cert.dir:" + CERT_DIR + "}' + '/' + '${periscope.tls.cert.file:client.pem}'}")
private String clientCertName;
@Value("#{'${periscope.cert.dir:" + CERT_DIR + "}' + '/' + '${periscope.tls.private.key.file:client-key.pem}'}")
private String clientPrivateKeyName;
@Inject
private CloudbreakClient cloudbreakClient;
@Inject
private SecurityConfigRepository securityConfigRepository;
public SecurityConfig prepareSecurityConfig(Long stackId) {
Path stackCertDir = getCertDir(stackId);
if (!Files.exists(stackCertDir)) {
try {
LOGGER.info("Creating directory for the certificates: {}", stackCertDir);
Files.createDirectory(stackCertDir);
} catch (IOException e) {
throw new TlsConfigurationException("Failed to create directory " + stackCertDir, e);
}
}
Path clientKeyDst = stackCertDir.resolve(KEY_FILE);
Path clientCertDst = stackCertDir.resolve(CERT_FILE);
try {
Files.copy(Paths.get(clientPrivateKeyName), clientKeyDst, StandardCopyOption.REPLACE_EXISTING);
Files.copy(Paths.get(clientCertName), clientCertDst, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
throw new TlsConfigurationException("Failed to copy client certificate to " + stackCertDir, e);
}
byte[] serverCert;
try {
serverCert = cloudbreakClient.stackEndpoint().getCertificate(stackId).getCertificate();
Files.write(stackCertDir.resolve(SERVER_CERT_FILE), serverCert, StandardOpenOption.CREATE);
} catch (Exception e) {
throw new TlsConfigurationException("Failed to write server certificate to " + stackCertDir, e);
}
byte[] clientKey;
byte[] clientCert;
try {
clientKey = Files.readAllBytes(clientKeyDst);
clientCert = Files.readAllBytes(clientCertDst);
} catch (IOException e) {
throw new TlsConfigurationException("Failed to read client certificate file from " + stackCertDir, e);
}
return new SecurityConfig(clientKey, clientCert, serverCert);
}
public TlsConfiguration getConfiguration(Cluster cluster) {
Path certDir = getCertDir(cluster.getStackId());
Path clientKeyPath = certDir.resolve(KEY_FILE);
Path clientCertPath = certDir.resolve(CERT_FILE);
Path serverCertPath = certDir.resolve(SERVER_CERT_FILE);
try {
if (!Files.exists(certDir)) {
LOGGER.info("Recreating certificate directory [{}] because it doesn't exist.", certDir);
Files.createDirectory(certDir);
}
if (!Files.exists(clientKeyPath) || !Files.exists(clientCertPath) || !Files.exists(serverCertPath)) {
LOGGER.info("Recreating certificate files in {} because they don't exist.", certDir);
SecurityConfig securityConfig = securityConfigRepository.findByClusterId(cluster.getId());
Files.write(clientKeyPath, securityConfig.getClientKey(), StandardOpenOption.CREATE);
Files.write(clientCertPath, securityConfig.getClientCert(), StandardOpenOption.CREATE);
Files.write(serverCertPath, securityConfig.getServerCert(), StandardOpenOption.CREATE);
}
} catch (IOException e) {
throw new TlsConfigurationException("Failed to write certificates to file.", e);
}
return new TlsConfiguration(clientKeyPath.toString(), clientCertPath.toString(), serverCertPath.toString());
}
private Path getCertDir(Long stackId) {
return Paths.get(certDir + DIR_PREFIX + stackId);
}
}