/* * 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.jboss.as.test.integration.domain; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import org.apache.commons.io.FileUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.jboss.as.controller.client.ModelControllerClient; import org.jboss.as.controller.descriptions.ModelDescriptionConstants; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADMIN_ONLY; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.HOST; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.HOST_STATE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.NAME; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OUTCOME; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_ATTRIBUTE_OPERATION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUCCESS; import static org.jboss.as.test.integration.domain.management.util.DomainLifecycleUtil.SLAVE_HOST_PASSWORD; import static org.jboss.as.test.integration.domain.management.util.DomainLifecycleUtil.SLAVE_HOST_USERNAME; import org.jboss.as.test.integration.domain.management.util.DomainTestSupport; import static org.jboss.as.test.integration.management.util.CustomCLIExecutor.MANAGEMENT_HTTP_PORT; import static org.jboss.as.test.integration.management.util.ModelUtil.createOpNode; import org.jboss.as.test.integration.security.common.CoreUtils; import org.jboss.dmr.ModelNode; import org.junit.Assert; /** * Common base for tests of SSL secured communication between master and slave. * * @author Ondrej Kotek <okotek@redhat.com> */ public abstract class AbstractSSLMasterSlaveTestCase { protected static final String MASTER_MANAGEMENT_REALM = "MasterManagementRealm"; private static final String MGMT_CTX = "/management"; private static final int TIMEOUT = 60000; protected static void keyMaterialSetup(File workDir) throws Exception { // create key and trust stores with imported certificates from opposing sides FileUtils.deleteDirectory(workDir); workDir.mkdirs(); Assert.assertTrue(workDir.exists()); Assert.assertTrue(workDir.isDirectory()); CoreUtils.createKeyMaterial(workDir); } protected static void setMasterManagementNativeInterfaceAndCheck(ModelControllerClient client) throws Exception { checkHostStatusOnMaster("slave"); setMasterManagementNativeInterface(client); reloadMaster(); checkHostStatusOnMaster("master"); } protected static void setOriginMasterManagementNativeInterfaceAndCheck() throws Exception { setOriginMasterManagementNativeInterface(); reloadMaster(); checkHostStatusOnMaster("master"); } protected static void reloadMaster() throws Exception { ModelNode op = new ModelNode(); op.get(OP).set("reload"); op.get(OP_ADDR).add(HOST, "master"); op.get(ADMIN_ONLY).set(false); executeOverHttp(getMasterMgmtUri(), op.toJSONString(true)); } protected static void checkHostStatusOnMaster(String host) throws Exception { final URI mgmtURI = getMasterMgmtUri(); final String operation = createHostStateRunningOperationJson(host); final long time = System.currentTimeMillis() + TIMEOUT; do { if (isHostStateRunning(mgmtURI, operation)) { return; } Thread.sleep(100); } while (System.currentTimeMillis() < time); Assert.fail("Cannot validate host '" + host + "' is running"); } private static void setMasterManagementNativeInterface(ModelControllerClient client) throws Exception { ModelNode operation = createOpNode("host=master/core-service=management/management-interface=native-interface", ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION); operation.get("name").set("security-realm"); operation.get("value").set(MASTER_MANAGEMENT_REALM); CoreUtils.applyUpdate(operation, client); } private static void setOriginMasterManagementNativeInterface() throws Exception { ModelNode operation = createOpNode("host=master/core-service=management/management-interface=native-interface", ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION); operation.get("name").set("security-realm"); operation.get("value").set("ManagementRealm"); executeOverHttp(getMasterMgmtUri(), operation.toJSONString(true)); } private static URI getMasterMgmtUri() throws MalformedURLException, URISyntaxException { return new URL("http", DomainTestSupport.masterAddress, MANAGEMENT_HTTP_PORT, MGMT_CTX).toURI(); } private static boolean isHostStateRunning(URI mgmtURI, String operation) throws IOException { ModelNode responseNode; try { responseNode = executeOverHttp(mgmtURI, operation); } catch (IOException ex) { // connection refused, host is not running return false; } if (responseNode != null && responseNode.get(OUTCOME).asString().equals(SUCCESS)) { final ModelNode resultNode = responseNode.require(RESULT); if ("running".equalsIgnoreCase(resultNode.asString())) { return true; } } return false; } private static ModelNode executeOverHttp(URI mgmtURI, String operation) throws IOException { CloseableHttpClient httpClient = createHttpClient(mgmtURI); HttpEntity operationEntity = new StringEntity(operation, ContentType.APPLICATION_JSON); HttpPost httpPost = new HttpPost(mgmtURI); httpPost.setEntity(operationEntity); HttpResponse response; ModelNode responseNode; try { response = httpClient.execute(httpPost); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != 200) { return null; } HttpEntity entity = response.getEntity(); if (entity == null) { return null; } responseNode = ModelNode.fromJSONStream(response.getEntity().getContent()); EntityUtils.consume(entity); } finally { httpClient.close(); } return responseNode; } private static CloseableHttpClient createHttpClient(URI mgmtURI) { UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(SLAVE_HOST_USERNAME, SLAVE_HOST_PASSWORD); AuthScope authScope = new AuthScope(mgmtURI.getHost(), mgmtURI.getPort()); CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(authScope, credentials); return HttpClients.custom() .setDefaultCredentialsProvider(credentialsProvider) .build(); } private static String createHostStateRunningOperationJson(String host) { final ModelNode operation = new ModelNode(); operation.get(OP).set(READ_ATTRIBUTE_OPERATION); operation.get(OP_ADDR).add(HOST, host); operation.get(NAME).set(HOST_STATE); return operation.toJSONString(true); } }