blob: 04fd3a499aa0a2220634d7087c60acdc67ca6477 [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.hadoop.ozone;
import org.apache.commons.io.FileUtils;
import org.apache.hadoop.fs.FileUtil;
import org.apache.hadoop.hdds.HddsConfigKeys;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.security.x509.SecurityConfig;
import org.apache.hadoop.hdds.security.x509.certificate.client.CertificateClient;
import org.apache.hadoop.hdds.security.x509.certificate.client.DNCertificateClient;
import org.apache.hadoop.hdds.security.x509.certificate.utils.CertificateCodec;
import org.apache.hadoop.hdds.security.x509.keys.KeyCodec;
import org.apache.hadoop.hdfs.DFSConfigKeys;
import org.apache.hadoop.security.ssl.KeyStoreTestUtil;
import org.apache.hadoop.test.GenericTestUtils;
import org.apache.hadoop.test.LambdaTestUtils;
import org.apache.hadoop.util.ServicePlugin;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
import java.nio.file.Paths;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.concurrent.Callable;
import static org.apache.hadoop.ozone.HddsDatanodeService.getLogger;
import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_SECURITY_ENABLED_KEY;
/**
* Test class for {@link HddsDatanodeService}.
*/
public class TestHddsSecureDatanodeInit {
private static File testDir;
private static OzoneConfiguration conf;
private static HddsDatanodeService service;
private static String[] args = new String[]{};
private static PrivateKey privateKey;
private static PublicKey publicKey;
private static GenericTestUtils.LogCapturer dnLogs;
private static CertificateClient client;
private static SecurityConfig securityConfig;
private static KeyCodec keyCodec;
private static CertificateCodec certCodec;
private static X509CertificateHolder certHolder;
private final static String DN_COMPONENT = DNCertificateClient.COMPONENT_NAME;
@BeforeClass
public static void setUp() throws Exception {
testDir = GenericTestUtils.getRandomizedTestDir();
conf = new OzoneConfiguration();
conf.setBoolean(OzoneConfigKeys.OZONE_ENABLED, true);
conf.set(HddsConfigKeys.OZONE_METADATA_DIRS, testDir.getPath());
//conf.set(ScmConfigKeys.OZONE_SCM_NAMES, "localhost");
String volumeDir = testDir + "/disk1";
conf.set(DFSConfigKeys.DFS_DATANODE_DATA_DIR_KEY, volumeDir);
conf.setBoolean(OZONE_SECURITY_ENABLED_KEY, true);
conf.setClass(OzoneConfigKeys.HDDS_DATANODE_PLUGINS_KEY,
TestHddsDatanodeService.MockService.class,
ServicePlugin.class);
securityConfig = new SecurityConfig(conf);
service = HddsDatanodeService.createHddsDatanodeService(args);
dnLogs = GenericTestUtils.LogCapturer.captureLogs(getLogger());
callQuietly(() -> {
service.start(conf);
return null;
});
callQuietly(() -> {
service.initializeCertificateClient(conf);
return null;
});
certCodec = new CertificateCodec(securityConfig, DN_COMPONENT);
keyCodec = new KeyCodec(securityConfig, DN_COMPONENT);
dnLogs.clearOutput();
privateKey = service.getCertificateClient().getPrivateKey();
publicKey = service.getCertificateClient().getPublicKey();
X509Certificate x509Certificate = null;
x509Certificate = KeyStoreTestUtil.generateCertificate(
"CN=Test", new KeyPair(publicKey, privateKey), 10,
securityConfig.getSignatureAlgo());
certHolder = new X509CertificateHolder(x509Certificate.getEncoded());
}
@AfterClass
public static void tearDown() {
FileUtil.fullyDelete(testDir);
}
@Before
public void setUpDNCertClient(){
FileUtils.deleteQuietly(Paths.get(
securityConfig.getKeyLocation(DN_COMPONENT).toString(),
securityConfig.getPrivateKeyFileName()).toFile());
FileUtils.deleteQuietly(Paths.get(
securityConfig.getKeyLocation(DN_COMPONENT).toString(),
securityConfig.getPublicKeyFileName()).toFile());
FileUtils.deleteQuietly(Paths.get(securityConfig
.getCertificateLocation(DN_COMPONENT).toString(),
securityConfig.getCertificateFileName()).toFile());
dnLogs.clearOutput();
client = new DNCertificateClient(securityConfig,
certHolder.getSerialNumber().toString());
service.setCertificateClient(client);
}
@Test
public void testSecureDnStartupCase0() throws Exception {
// Case 0: When keypair as well as certificate is missing. Initial keypair
// boot-up. Get certificate will fail as no SCM is not running.
LambdaTestUtils.intercept(Exception.class, "",
() -> service.initializeCertificateClient(conf));
Assert.assertNotNull(client.getPrivateKey());
Assert.assertNotNull(client.getPublicKey());
Assert.assertNull(client.getCertificate());
Assert.assertTrue(dnLogs.getOutput().contains("Init response: GETCERT"));
}
@Test
public void testSecureDnStartupCase1() throws Exception {
// Case 1: When only certificate is present.
certCodec.writeCertificate(certHolder);
LambdaTestUtils.intercept(RuntimeException.class, "DN security" +
" initialization failed",
() -> service.initializeCertificateClient(conf));
Assert.assertNull(client.getPrivateKey());
Assert.assertNull(client.getPublicKey());
Assert.assertNotNull(client.getCertificate());
Assert.assertTrue(dnLogs.getOutput().contains("Init response: FAILURE"));
}
@Test
public void testSecureDnStartupCase2() throws Exception {
// Case 2: When private key and certificate is missing.
keyCodec.writePublicKey(publicKey);
LambdaTestUtils.intercept(RuntimeException.class, "DN security" +
" initialization failed",
() -> service.initializeCertificateClient(conf));
Assert.assertNull(client.getPrivateKey());
Assert.assertNotNull(client.getPublicKey());
Assert.assertNull(client.getCertificate());
Assert.assertTrue(dnLogs.getOutput().contains("Init response: FAILURE"));
}
@Test
public void testSecureDnStartupCase3() throws Exception {
// Case 3: When only public key and certificate is present.
keyCodec.writePublicKey(publicKey);
certCodec.writeCertificate(certHolder);
LambdaTestUtils.intercept(RuntimeException.class, "DN security" +
" initialization failed",
() -> service.initializeCertificateClient(conf));
Assert.assertNull(client.getPrivateKey());
Assert.assertNotNull(client.getPublicKey());
Assert.assertNotNull(client.getCertificate());
Assert.assertTrue(dnLogs.getOutput().contains("Init response: FAILURE"));
}
@Test
public void testSecureDnStartupCase4() throws Exception {
// Case 4: When public key as well as certificate is missing.
keyCodec.writePrivateKey(privateKey);
LambdaTestUtils.intercept(RuntimeException.class, " DN security" +
" initialization failed",
() -> service.initializeCertificateClient(conf));
Assert.assertNotNull(client.getPrivateKey());
Assert.assertNull(client.getPublicKey());
Assert.assertNull(client.getCertificate());
Assert.assertTrue(dnLogs.getOutput().contains("Init response: FAILURE"));
dnLogs.clearOutput();
}
@Test
public void testSecureDnStartupCase5() throws Exception {
// Case 5: If private key and certificate is present.
certCodec.writeCertificate(certHolder);
keyCodec.writePrivateKey(privateKey);
service.initializeCertificateClient(conf);
Assert.assertNotNull(client.getPrivateKey());
Assert.assertNotNull(client.getPublicKey());
Assert.assertNotNull(client.getCertificate());
Assert.assertTrue(dnLogs.getOutput().contains("Init response: SUCCESS"));
}
@Test
public void testSecureDnStartupCase6() throws Exception {
// Case 6: If key pair already exist than response should be GETCERT.
keyCodec.writePublicKey(publicKey);
keyCodec.writePrivateKey(privateKey);
LambdaTestUtils.intercept(Exception.class, "",
() -> service.initializeCertificateClient(conf));
Assert.assertNotNull(client.getPrivateKey());
Assert.assertNotNull(client.getPublicKey());
Assert.assertNull(client.getCertificate());
Assert.assertTrue(dnLogs.getOutput().contains("Init response: GETCERT"));
}
@Test
public void testSecureDnStartupCase7() throws Exception {
// Case 7 When keypair and certificate is present.
keyCodec.writePublicKey(publicKey);
keyCodec.writePrivateKey(privateKey);
certCodec.writeCertificate(certHolder);
service.initializeCertificateClient(conf);
Assert.assertNotNull(client.getPrivateKey());
Assert.assertNotNull(client.getPublicKey());
Assert.assertNotNull(client.getCertificate());
Assert.assertTrue(dnLogs.getOutput().contains("Init response: SUCCESS"));
}
/**
* Invoke a callable; Ignore all exception.
* @param closure closure to execute
* @return
*/
public static void callQuietly(Callable closure) {
try {
closure.call();
} catch (Throwable e) {
// Ignore all Throwable,
}
}
@Test
public void testGetCSR() throws Exception {
keyCodec.writePublicKey(publicKey);
keyCodec.writePrivateKey(privateKey);
service.setCertificateClient(client);
PKCS10CertificationRequest csr =
service.getCSR(conf);
Assert.assertNotNull(csr);
csr = service.getCSR(conf);
Assert.assertNotNull(csr);
csr = service.getCSR(conf);
Assert.assertNotNull(csr);
csr = service.getCSR(conf);
Assert.assertNotNull(csr);
}
}