/*
* JBoss, Home of Professional Open Source.
* Copyright 2014, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.wildfly.core.test.standalone.mgmt;
import java.io.File;
import java.io.IOException;
import javax.inject.Inject;
import org.apache.commons.io.FileUtils;
import org.jboss.as.cli.CommandContext;
import org.jboss.as.controller.client.ModelControllerClient;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.test.integration.management.util.CLITestUtil;
import org.jboss.as.test.integration.management.util.CustomCLIExecutor;
import org.jboss.as.test.integration.security.PicketBoxModuleUtil;
import org.jboss.as.test.integration.security.common.AbstractBaseSecurityRealmsServerSetupTask;
import org.jboss.as.test.integration.security.common.CoreUtils;
import org.jboss.as.test.integration.security.common.SecurityTestConstants;
import org.jboss.as.test.integration.security.common.config.realm.Authentication;
import org.jboss.as.test.integration.security.common.config.realm.RealmKeystore;
import org.jboss.as.test.integration.security.common.config.realm.SecurityRealm;
import org.jboss.as.test.integration.security.common.config.realm.ServerIdentity;
import org.jboss.as.test.module.util.TestModule;
import org.jboss.as.test.shared.TestSuiteEnvironment;
import org.jboss.dmr.ModelNode;
import org.jboss.logging.Logger;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.wildfly.core.testrunner.ManagementClient;
import org.wildfly.core.testrunner.ServerControl;
import org.wildfly.core.testrunner.ServerController;
import org.wildfly.core.testrunner.ServerSetupTask;
import org.wildfly.core.testrunner.WildflyTestRunner;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.NAME;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.VALUE;
import static org.jboss.as.test.integration.management.util.CustomCLIExecutor.HTTPS_CONTROLLER;
import static org.jboss.as.test.integration.management.util.CustomCLIExecutor.MANAGEMENT_NATIVE_PORT;
import static org.jboss.as.test.integration.management.util.ModelUtil.createOpNode;
import static org.junit.Assert.assertThat;
/**
* Testing https connection to the http management interface with cli console
* with configured two-way SSL. CLI client uses client truststore with accepted
* server's certificate and vice-versa. Keystores and truststores have valid
* certificates until 25 Octover 2033.
*
* @author Filip Bogyai
*/
@RunWith(WildflyTestRunner.class)
@ServerControl(manual = true)
@Ignore("[WFCORE-2068] Test failure during clean up.")
public class HTTPSConnectionWithCLITestCase {
private static Logger LOGGER = Logger.getLogger(HTTPSConnectionWithCLITestCase.class);
private static final File WORK_DIR = new File("native-if-workdir");
public static final File SERVER_KEYSTORE_FILE = new File(WORK_DIR, SecurityTestConstants.SERVER_KEYSTORE);
public static final File SERVER_TRUSTSTORE_FILE = new File(WORK_DIR, SecurityTestConstants.SERVER_TRUSTSTORE);
public static final File CLIENT_KEYSTORE_FILE = new File(WORK_DIR, SecurityTestConstants.CLIENT_KEYSTORE);
public static final File CLIENT_TRUSTSTORE_FILE = new File(WORK_DIR, SecurityTestConstants.CLIENT_TRUSTSTORE);
public static final File UNTRUSTED_KEYSTORE_FILE = new File(WORK_DIR, SecurityTestConstants.UNTRUSTED_KEYSTORE);
private static final String MANAGEMENT_NATIVE_REALM = "ManagementNativeRealm";
private static final String JBOSS_CLI_FILE = "jboss-cli.xml";
private static final File TRUSTED_JBOSS_CLI_FILE = new File(WORK_DIR, "trusted-jboss-cli.xml");
private static final File UNTRUSTED_JBOSS_CLI_FILE = new File(WORK_DIR, "untrusted-jboss-cli.xml");
private static final String TESTING_OPERATION = "/core-service=management/management-interface=http-interface:read-resource";
private static final ServerResourcesSetup keystoreFilesSetup = new ServerResourcesSetup();
private static final ManagementNativeRealmSetup managementNativeRealmSetup = new ManagementNativeRealmSetup();
private static TestModule picketLinkModule;
@Inject
private static ServerController containerController;
@BeforeClass
public static void prepareServer() throws Exception {
containerController.startInAdminMode();
ManagementClient mgmtClient = containerController.getClient();
//final ModelControllerClient client = mgmtClient.getControllerClient();
keystoreFilesSetup.setup(mgmtClient);
managementNativeRealmSetup.setup(mgmtClient);
picketLinkModule = PicketBoxModuleUtil.createTestModule();
// To apply new security realm settings for http interface reload of server is required
reloadServer();
}
/**
* Testing connection to server http interface with default CLI settings.
* Client doesn't have servers certificate in truststore and therefore it rejects
* connection.
*/
@Test
public void testDefaultCLIConfiguration() throws InterruptedException, IOException {
String cliOutput = CustomCLIExecutor.execute(null, TESTING_OPERATION, HTTPS_CONTROLLER, true, "N");
assertThat("Untrusted client should not be authenticated.", cliOutput, not(containsString("\"outcome\" => \"success\"")));
}
/**
* Testing connection to server http interface with CLI using wrong
* certificate. Server doesn't have client certificate in truststore and
* therefore it rejects connection.
*/
@Test
public void testUntrustedCLICertificate() throws InterruptedException, IOException {
String cliOutput = CustomCLIExecutor.execute(UNTRUSTED_JBOSS_CLI_FILE, TESTING_OPERATION, HTTPS_CONTROLLER);
assertThat("Untrusted client should not be authenticated.", cliOutput, not(containsString("\"outcome\" => \"success\"")));
}
/**
* Testing connection to server http interface with CLI using trusted
* certificate. Client has server certificate in truststore, and also
* server has certificate from client, so client can successfully connect.
*/
@Test
public void testTrustedCLICertificate() throws InterruptedException, IOException {
String cliOutput = CustomCLIExecutor.execute(TRUSTED_JBOSS_CLI_FILE, TESTING_OPERATION, HTTPS_CONTROLLER);
assertThat("Client with valid certificate should be authenticated.", cliOutput, containsString("\"outcome\" => \"success\""));
}
@AfterClass
public static void resetTestConfiguration() throws Exception {
ModelControllerClient client = HTTPSManagementInterfaceTestCase.getNativeModelControllerClient();
ManagementClient managementClient = new ManagementClient(client, TestSuiteEnvironment.getServerAddress(),
MANAGEMENT_NATIVE_PORT, "remoting");
HTTPSManagementInterfaceTestCase.resetHttpInterfaceConfiguration(client);
// reload to apply changes
reloadServer();//reload using CLI
keystoreFilesSetup.tearDown(managementClient);
managementNativeRealmSetup.tearDown(managementClient);
containerController.stop();
picketLinkModule.remove();
FileUtils.deleteDirectory(WORK_DIR);
}
public static void reloadServer() throws Exception {
final CommandContext ctx = CLITestUtil.getCommandContext("remoting", TestSuiteEnvironment.getServerAddress(), MANAGEMENT_NATIVE_PORT);
try {
ctx.connectController();
ctx.handle("reload");
} finally {
ctx.terminateSession();
}
}
static class ManagementNativeRealmSetup extends AbstractBaseSecurityRealmsServerSetupTask {
@Override
protected SecurityRealm[] getSecurityRealms() throws Exception {
final ServerIdentity serverIdentity = new ServerIdentity.Builder().ssl(
new RealmKeystore.Builder().keystorePassword(SecurityTestConstants.KEYSTORE_PASSWORD)
.keystorePath(SERVER_KEYSTORE_FILE.getAbsolutePath()).build()).build();
final Authentication authentication = new Authentication.Builder().truststore(
new RealmKeystore.Builder().keystorePassword(SecurityTestConstants.KEYSTORE_PASSWORD)
.keystorePath(SERVER_TRUSTSTORE_FILE.getAbsolutePath()).build()).build();
final SecurityRealm realm = new SecurityRealm.Builder().name(MANAGEMENT_NATIVE_REALM).serverIdentity(serverIdentity)
.authentication(authentication).build();
return new SecurityRealm[]{realm};
}
}
static class ServerResourcesSetup implements ServerSetupTask {
public void setup(ManagementClient managementClient) throws Exception {
// create key and trust stores with imported certificates from opposing sides
FileUtils.deleteDirectory(WORK_DIR);
WORK_DIR.mkdirs();
CoreUtils.createKeyMaterial(WORK_DIR);
// create jboss-cli.xml files with valid/invalid keystore certificates
FileUtils.write(TRUSTED_JBOSS_CLI_FILE, CoreUtils.propertiesReplacer(JBOSS_CLI_FILE, CLIENT_KEYSTORE_FILE, CLIENT_TRUSTSTORE_FILE,
SecurityTestConstants.KEYSTORE_PASSWORD));
FileUtils.write(UNTRUSTED_JBOSS_CLI_FILE, CoreUtils.propertiesReplacer(JBOSS_CLI_FILE, UNTRUSTED_KEYSTORE_FILE,
CLIENT_TRUSTSTORE_FILE, SecurityTestConstants.KEYSTORE_PASSWORD));
final ModelControllerClient client = managementClient.getControllerClient();
// secure http interface
ModelNode operation = createOpNode("core-service=management/management-interface=http-interface",
ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION);
operation.get(NAME).set("security-realm");
operation.get(VALUE).set(MANAGEMENT_NATIVE_REALM);
CoreUtils.applyUpdate(operation, client);
operation = createOpNode("core-service=management/management-interface=http-interface",
ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION);
operation.get(NAME).set("secure-socket-binding");
operation.get(VALUE).set("management-https");
CoreUtils.applyUpdate(operation, client);
// add native socket binding
operation = createOpNode("socket-binding-group=standard-sockets/socket-binding=management-native",ModelDescriptionConstants.ADD);
operation.get("port").set(MANAGEMENT_NATIVE_PORT);
operation.get("interface").set("management");
CoreUtils.applyUpdate(operation, client);
// create native interface to control server while http interface
// will be secured
operation = createOpNode("core-service=management/management-interface=native-interface", ModelDescriptionConstants.ADD);
operation.get("security-realm").set("ManagementRealm");
operation.get("socket-binding").set("management-native");
CoreUtils.applyUpdate(operation, client);
}
@Override
public void tearDown(ManagementClient managementClient) throws Exception {
final ModelControllerClient client = managementClient.getControllerClient();
ModelNode operation = createOpNode("core-service=management/management-interface=native-interface", ModelDescriptionConstants.REMOVE);
CoreUtils.applyUpdate(operation, client);
operation = createOpNode("socket-binding-group=standard-sockets/socket-binding=management-native", ModelDescriptionConstants.REMOVE);
CoreUtils.applyUpdate(operation, client);
FileUtils.deleteDirectory(WORK_DIR);
}
}
}