/* * Copyright (C) 2011-2012 Intel Corporation * All rights reserved. */ package security; import com.intel.mtwilson.ApiClient; import com.intel.mtwilson.api.*; import com.intel.mtwilson.KeystoreUtil; import com.intel.mtwilson.My; import com.intel.mtwilson.MyConfiguration; import com.intel.dcsg.cpg.crypto.CryptographyException; import com.intel.dcsg.cpg.crypto.RsaUtil; import com.intel.mtwilson.datatypes.ApiClientCreateRequest; import com.intel.mtwilson.datatypes.ApiClientStatus; import com.intel.mtwilson.datatypes.ApiClientUpdateRequest; import com.intel.mtwilson.datatypes.Role; import com.intel.dcsg.cpg.crypto.RsaCredential; import com.intel.dcsg.cpg.crypto.RsaCredentialX509; import java.io.*; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URL; import java.security.*; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Properties; import java.util.concurrent.TimeUnit; import net.schmizz.sshj.SSHClient; import net.schmizz.sshj.connection.ConnectionException; import net.schmizz.sshj.connection.channel.direct.Session; import net.schmizz.sshj.connection.channel.direct.Session.Command; import net.schmizz.sshj.transport.TransportException; import net.schmizz.sshj.transport.verification.HostKeyVerifier; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.ArrayUtils; //import org.codehaus.plexus.util.StringUtils; import org.junit.BeforeClass; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author jbuhacoff */ public class RegisterApiClientTest { private static final Logger log = LoggerFactory.getLogger(RegisterApiClientTest.class); private static URL baseurl; private static String serviceRootPassword; @BeforeClass public static void configure() throws IOException { // look for the properties file in our java classpath since this is a test class not production code // there should be one properties file for each environment being tested. // DO NOT change the configuration of an existing properties file without coordinating with the team String filename = "/mtwilson.properties"; InputStream in = RegisterApiClientTest.class.getResourceAsStream(filename); if( in == null ) { throw new FileNotFoundException("Cannot find properties: "+filename); } Properties config = new Properties(); config.load(in); baseurl = new URL(config.getProperty("mtwilson.api.baseurl")); } @Test public void testRegisterNewApiClient() throws ClientException, IOException, ApiException, NoSuchAlgorithmException, GeneralSecurityException, CryptographyException { // create a new private key and certificate KeyPair keypair = RsaUtil.generateRsaKeyPair(RsaUtil.MINIMUM_RSA_KEY_SIZE); X509Certificate certificate = RsaUtil.generateX509Certificate("jonathan"/*CN=jonathan, OU=IASI, O=Intel, L=Folsom, ST=CA, C=US"*/, keypair, 365); RsaCredentialX509 credential = new RsaCredentialX509(keypair.getPrivate(), certificate); // create a new keystore and save the new key into it KeyStore keystore = KeyStore.getInstance("JKS"); keystore.load(null, null); keystore.setKeyEntry("mykey", keypair.getPrivate(), "changeit".toCharArray(), new X509Certificate[] { certificate }); File tmp = File.createTempFile("keystore", ".jks"); // IOException. // creates a temporary file KeystoreUtil.save(keystore, "changeit", tmp); System.out.println("Keystore is in "+tmp.getAbsolutePath()); // register the new key with Mt Wilson Properties p = new Properties(); p.setProperty("mtwilson.api.ssl.requireTrustedCertificate", "false"); p.setProperty("mtwilson.api.ssl.verifyHostname", "false"); ApiClient c = new ApiClient(baseurl, credential, p); //ApiClient c = new ApiClient("mtwilson.properties"); ApiClientCreateRequest me = new ApiClientCreateRequest(); me.setCertificate(credential.identity()); me.setRoles(new String[] { Role.Attestation.toString(), Role.Whitelist.toString() }); c.register(me); } @Test public void testRegisterExistingApiClient() throws ClientException, NoSuchAlgorithmException, MalformedURLException, KeyManagementException, KeyStoreException, IOException, CertificateException, UnrecoverableEntryException, GeneralSecurityException, ApiException, CryptographyException { File keystoreFile = new File("C:\\Users\\jbuhacoff\\AppData\\Local\\Temp\\keystore5757222749157892628.jks"); // file automatically generated by testRegisterNewApiClinet KeyStore keystore = KeystoreUtil.open(new FileInputStream(keystoreFile), "changeit"); // KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, [FileNotFoundException] RsaCredentialX509 credential = KeystoreUtil.loadX509(keystore, "mykey", "changeit"); // UnrecoverableEntryException, [CertificateEncodingException] // api client Properties p = new Properties(); p.setProperty("mtwilson.api.ssl.requireTrustedCertificate", "false"); p.setProperty("mtwilson.api.ssl.verifyHostname", "false"); ApiClient c = new ApiClient(baseurl, credential, p); // KeyManagementException, [MalformedURLException], [UnsupportedEncodingException] // duplicate registration request ApiClientCreateRequest me = new ApiClientCreateRequest(); me.setCertificate(credential.getCertificate().getEncoded()); me.setRoles(new String[] { Role.Attestation.toString(), Role.Whitelist.toString() }); c.register(me); } @Test public void testUpdateApiClient() throws ClientException, KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableEntryException, KeyManagementException, ApiException, SignatureException, CryptographyException { // grant access to a given key by using the trusted ip feature on the server to trust all requests from this ip File keystoreFile = new File("C:\\Users\\jbuhacoff\\AppData\\Local\\Temp\\keystore5757222749157892628.jks"); // file automatically generated by testRegisterNewApiClinet KeyStore keystore = KeystoreUtil.open(new FileInputStream(keystoreFile), "changeit"); // KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, [FileNotFoundException] RsaCredential credential = KeystoreUtil.loadX509(keystore, "mykey", "changeit"); // UnrecoverableEntryException, [CertificateEncodingException] ApiClientUpdateRequest update = new ApiClientUpdateRequest(); update.fingerprint = credential.identity(); update.enabled = true; update.roles = new String[] { Role.Attestation.toString(), Role.Whitelist.toString() }; update.status = ApiClientStatus.APPROVED.toString(); update.comment = "Bootstrap approval sample code in JavaIntegrationTests project"; Properties p = new Properties(); p.setProperty("mtwilson.api.ssl.requireTrustedCertificate", "false"); p.setProperty("mtwilson.api.ssl.verifyHostname", "false"); ApiClient c = new ApiClient(baseurl, credential, p); // KeyManagementException, [MalformedURLException], [UnsupportedEncodingException] c.updateApiClient(update); // ApiException, SignatureException } @Test public void testApproveMyApiClient() throws ClientException, KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableEntryException, KeyManagementException, ApiException, SignatureException, CryptographyException { // grant access to a given key by using the trusted ip feature on the server to trust all requests from this ip KeyStore keystore = KeystoreUtil.open(new FileInputStream(My.configuration().getKeystoreFile()), "changeit"); // KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, [FileNotFoundException] RsaCredential credential = KeystoreUtil.loadX509(keystore, My.configuration().getKeystoreUsername(), My.configuration().getKeystorePassword()); // UnrecoverableEntryException, [CertificateEncodingException] ApiClientUpdateRequest update = new ApiClientUpdateRequest(); update.fingerprint = credential.identity(); update.enabled = true; update.roles = new String[] { Role.Attestation.toString(), Role.Whitelist.toString() }; update.status = ApiClientStatus.APPROVED.toString(); update.comment = "Bootstrap approval sample code in JavaIntegrationTests project"; Properties p = new Properties(); p.setProperty("mtwilson.api.ssl.requireTrustedCertificate", "false"); p.setProperty("mtwilson.api.ssl.verifyHostname", "false"); ApiClient c = new ApiClient(baseurl, credential, p); // KeyManagementException, [MalformedURLException], [UnsupportedEncodingException] c.updateApiClient(update); // ApiException, SignatureException } /** * Executes a remote command with no timeout * * @param ssh * @param command * @return * @throws ConnectionException * @throws TransportException * @throws IOException */ private String remote(SSHClient ssh, String command) throws ConnectionException, TransportException, IOException { return remote(ssh, command, null); } /** * The sshj client is designed to permit one command per "session". But * you can start multiple sessions per connection so this is ok. * * @param ssh the ssh client * @param command string to execute on the remote shell * @param timeoutSeconds or null to wait indefinitely for the command to complete * @return * @throws ConnectionException * @throws TransportException * @throws IOException */ private String remote(SSHClient ssh, String command, Integer timeoutSeconds) throws ConnectionException, TransportException, IOException { Session session = ssh.startSession(); try { Command cmd = session.exec(command); // ConnectionException, TransportException if( timeoutSeconds == null ) { cmd.join(); } else { cmd.join(timeoutSeconds, TimeUnit.SECONDS); // the parameters are the timeout. if you want to wait indefinitely call join() } log.debug("Command exit status: {}", cmd.getExitStatus()); String output = IOUtils.toString(cmd.getInputStream()); // IOException return output; } finally { session.close(); } } private void bootstrapFirstApiClient(SSHClient ssh) throws ConnectionException, TransportException, KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableEntryException, KeyManagementException, ApiException, SignatureException, ClientException, CryptographyException { // find out what is the previous list of trusted hosts String previousWhitelistString = remote(ssh, "asctl show mtwilson.api.trust"); String[] previousWhitelist = previousWhitelistString.trim().split(","); // trim is required to remove the newline at the end of the output log.debug("Previous trusted clients network address list: {}", previousWhitelistString); // get local ip address and add it to the list InetAddress addr = InetAddress.getLocalHost(); String[] updatedWhitelist = (String[]) ArrayUtils.add(previousWhitelist, addr.getHostAddress()); // Removing the dependency on codehaus.plexus // String updatedWhitelistString = StringUtils.join(updatedWhitelist, ","); String updatedWhitelistString = ""; for (String temp : updatedWhitelist) { updatedWhitelistString = updatedWhitelistString + temp + ","; } // need to remove the last occurance of the "," updatedWhitelistString.substring(0, (updatedWhitelistString.lastIndexOf(",")-1)); log.debug("Updated trusted clients network address list: {}", updatedWhitelistString); // set the new list on the server and restart the application remote(ssh, String.format("asctl edit mtwilson.api.trust \"%s\"", updatedWhitelistString)); remote(ssh, "asctl restart"); // grant privileges to ourselves testUpdateApiClient(); log.info("Granted privileges to self"); // now restore the original trusted hosts whitelist remote(ssh, String.format("asctl edit mtwilson.api.trust \"%s\"", previousWhitelistString)); log.info("Restored previous trusted clients network address list"); } /** * This should not be a @Test method because it requires the root password * of the server and we should not store that. So invoke it by running this * class via the main() method which will prompt for the root password. * * @throws KeyStoreException * @throws IOException * @throws NoSuchAlgorithmException * @throws CertificateException * @throws UnrecoverableEntryException * @throws KeyManagementException * @throws ApiException * @throws SignatureException */ private void testAuthorizeApiClientFromTrustedHostViaSsh() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableEntryException, KeyManagementException, ApiException, SignatureException, ConnectionException, TransportException, TransportException, ClientException, CryptographyException { SSHClient ssh = new SSHClient(); //ssh.loadKnownHosts(); // this is only if we have a known_hosts file... //ssh.addHostKeyVerifier("..."); // this is only if we know the fingerprint of the remote host we're connecting to ssh.addHostKeyVerifier(new HostKeyVerifier() {@Override public boolean verify(String arg0, int arg1, PublicKey arg2) { return true; } }); // this accepts all remote public keys ssh.connect("10.1.71.81"); try { ssh.authPassword("root", serviceRootPassword); bootstrapFirstApiClient(ssh); } finally { ssh.disconnect(); } } public static void main(String[] args) throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException, UnrecoverableEntryException, KeyManagementException, ApiException, SignatureException, ConnectionException, TransportException, TransportException, ClientException, CryptographyException { configure(); System.out.println("URL: "+baseurl.toExternalForm()); System.out.print("Root password: "); BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); serviceRootPassword = in.readLine().trim(); RegisterApiClientTest test = new RegisterApiClientTest(); test.testAuthorizeApiClientFromTrustedHostViaSsh(); } }