Thursday, October 29, 2015

WLS deployment via Jersey REST client

 

Outline

 

Introduction

For programmatic deployment to Oracle Weblogic Server a very easy approach is to use the Oracle Weblogic Server Management REST API.

 

curl -v \
--user username:password \
-H X-Requested-By:MyClient \
-H Accept:application/json \
-H Content-Type:application/json \
-d "{
name: 'BasicApp',
deploymentPath:
'/deployments/BasicApp/app/BasicApp.ear',
targets: [
'myserver' ]
}
" \
-X POST http://localhost:7001/management/wls/latest/deployments/application

The benefits here are related to:


  • Granular WLS targeting
  • Integration into a CI process via Maven/ANT…

The challenges are related to dealing with SSL connections (importing CA certificates to the REST client). In this initial article we’ll trust all the certificates. Another article will follow that will describe how to import CA certificates to the REST client.


Jersey REST Client


You need to provide the following parameters to your client:


  • WLS username
  • WLS password
  • WLS REST endpoint
  • Deployment path
  • String Model which is a JSON({ name: '${deploymentName}', targets: ['${wls-targets}'] }) containing the:
  • Deployment name
  • Targets

The client code:


 


1 import java.io.File;
2 import java.security.SecureRandom;
3
4 import javax.net.ssl.HostnameVerifier;
5 import javax.net.ssl.HttpsURLConnection;
6 import javax.net.ssl.SSLContext;
7 import javax.net.ssl.SSLSession;
8 import javax.ws.rs.core.MediaType;
9
10 import com.sun.jersey.api.client.Client;
11 import com.sun.jersey.api.client.ClientResponse;
12 import com.sun.jersey.api.client.WebResource;
13 import com.sun.jersey.api.client.config.ClientConfig;
14 import com.sun.jersey.api.client.config.DefaultClientConfig;
15 import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
16 import com.sun.jersey.client.urlconnection.HTTPSProperties;
17 import com.sun.jersey.multipart.FormDataMultiPart;
18 import com.sun.jersey.multipart.file.FileDataBodyPart;
19
20 public class JerseyClientPost {
21
22 public static void main(String[] args) {
23 String proxyHost = "";
24 String proxyPort = "";
25 String username = args[0];
26 String password = args[1];
27 String restURL = args[2];
28 String deploymentPath = args[3];
29 String stringModel = args[4];
30
31 try {
32
33 String model = stringModel;
34
35 Client client = Client.create(configureClient());
36
37 WebResource webResource = client.resource(restURL);
38
39 client.addFilter(new HTTPBasicAuthFilter(username, password));
40 webResource.header("x-requested-by", "WLSMavenDeployClient");
41 webResource.header("accept", "application/json");
42 webResource.header("content-type", "multipart/form-data");
43
44 FileDataBodyPart deployment = new FileDataBodyPart("deployment",
45 new File(deploymentPath),
46 MediaType.APPLICATION_OCTET_STREAM_TYPE);
47
48 FormDataMultiPart multiPart = new FormDataMultiPart();
49 multiPart.bodyPart(deployment);
50 multiPart.field("model", model, MediaType.APPLICATION_JSON_TYPE);
51
52 ClientResponse response = webResource.type(
53 MediaType.MULTIPART_FORM_DATA_TYPE).post(
54 ClientResponse.class, multiPart);
55
56 System.out.println("");
57 System.out.println("response2 : " + response.toString());
58 String output = response.getEntity(String.class);
59 System.out.println(output);
60 System.out.println("");
61
62 if (response.getStatus() != 201) {
63 throw new RuntimeException("Failed : HTTP error code : "
64 + response.getStatus());
65 }
66
67 } catch (Exception e) {
68
69 e.printStackTrace();
70
71 }
72
73 }
74
75 public static ClientConfig configureClient() {
76 System.setProperty("jsse.enableSNIExtension", "false");
77 try {
78 trustAllHttpsCertificates();
79 } catch (Exception e1) {
80 // TODO Auto-generated catch block
81 e1.printStackTrace();
82 }
83
84 SSLContext ctx = null;
85 javax.net.ssl.TrustManager[] trustAllCerts =
86
87 new javax.net.ssl.TrustManager[1];
88
89 javax.net.ssl.TrustManager tm = new miTM();
90
91 trustAllCerts[0] = tm;
92
93 javax.net.ssl.SSLContext sc;
94 try {
95 sc = javax.net.ssl.SSLContext.getInstance("SSL");
96 sc.init(null, trustAllCerts, null);
97 javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(
98
99 sc.getSocketFactory());
100 } catch (Exception e2) {
101 // TODO Auto-generated catch block
102 e2.printStackTrace();
103 }
104
105 try {
106 ctx = SSLContext.getInstance("SSL");
107 ctx.init(null, trustAllCerts, new SecureRandom());
108 } catch (java.security.GeneralSecurityException ex) {
109 }
110 HttpsURLConnection.setDefaultSSLSocketFactory(ctx.getSocketFactory());
111 try {
112 trustAllHttpsCertificates();
113 } catch (Exception e1) {
114 // TODO Auto-generated catch block
115 e1.printStackTrace();
116 }
117 ClientConfig config = new DefaultClientConfig();
118 try {
119 config.getProperties().put(
120 HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,
121 new HTTPSProperties(new HostnameVerifier() {
122
123 @Override
124 public boolean verify(String hostname,
125 SSLSession session) {
126 // TODO Auto-generated method stub
127 return true;
128 }
129 }, ctx));
130
131 } catch (Exception e) {
132 }
133 return config;
134 }
135
136 private static void trustAllHttpsCertificates() throws Exception {
137
138 // Create a trust manager that does not validate certificate chains:
139 System.out
140 .println("\n[WARNING] SSL INSECURE: Trusting all https certificates\n");
141
142 javax.net.ssl.TrustManager[] trustAllCerts =
143
144 new javax.net.ssl.TrustManager[1];
145
146 javax.net.ssl.TrustManager tm = new miTM();
147
148 trustAllCerts[0] = tm;
149
150 javax.net.ssl.SSLContext sc =
151
152 javax.net.ssl.SSLContext.getInstance("SSL");
153
154 sc.init(null, trustAllCerts, null);
155
156 javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(
157
158 sc.getSocketFactory());
159
160 }
161
162 public static class miTM implements javax.net.ssl.TrustManager,
163 javax.net.ssl.X509TrustManager {
164 public java.security.cert.X509Certificate[] getAcceptedIssuers() {
165 return null;
166 }
167
168 public boolean isServerTrusted(
169 java.security.cert.X509Certificate[] certs) {
170 return true;
171 }
172
173 public boolean isClientTrusted(
174 java.security.cert.X509Certificate[] certs) {
175 return true;
176 }
177
178 public void checkServerTrusted(
179 java.security.cert.X509Certificate[] certs, String authType)
180 throws java.security.cert.CertificateException {
181 return;
182 }
183
184 public void checkClientTrusted(
185 java.security.cert.X509Certificate[] certs, String authType)
186 throws java.security.cert.CertificateException {
187 return;
188 }
189 }
190
191

Trusting all the certificates


For simplicity, whenever your WLS server is listening only on https(SSL), we are trusting all the certificates.

THIS IS NOT A RECOMMENDED SECURE APPROACH !

 


1 public static ClientConfig configureClient() {
2 System.setProperty("jsse.enableSNIExtension", "false");
3 try {
4 trustAllHttpsCertificates();
5 } catch (Exception e1) {
6 // TODO Auto-generated catch block
7 e1.printStackTrace();
8 }
9
10 SSLContext ctx = null;
11 javax.net.ssl.TrustManager[] trustAllCerts =
12
13 new javax.net.ssl.TrustManager[1];
14
15 javax.net.ssl.TrustManager tm = new miTM();
16
17 trustAllCerts[0] = tm;
18
19 javax.net.ssl.SSLContext sc;
20 try {
21 sc = javax.net.ssl.SSLContext.getInstance("SSL");
22 sc.init(null, trustAllCerts, null);
23 javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(
24
25 sc.getSocketFactory());
26 } catch (Exception e2) {
27 // TODO Auto-generated catch block
28 e2.printStackTrace();
29 }
30
31 try {
32 ctx = SSLContext.getInstance("SSL");
33 ctx.init(null, trustAllCerts, new SecureRandom());
34 } catch (java.security.GeneralSecurityException ex) {
35 }
36 HttpsURLConnection.setDefaultSSLSocketFactory(ctx.getSocketFactory());
37 try {
38 trustAllHttpsCertificates();
39 } catch (Exception e1) {
40 // TODO Auto-generated catch block
41 e1.printStackTrace();
42 }
43 ClientConfig config = new DefaultClientConfig();
44 try {
45 config.getProperties().put(
46 HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,
47 new HTTPSProperties(new HostnameVerifier() {
48
49 @Override
50 public boolean verify(String hostname,
51 SSLSession session) {
52 // TODO Auto-generated method stub
53 return true;
54 }
55 }, ctx));
56
57 } catch (Exception e) {
58 }
59 return config;
60 }
61
62 private static void trustAllHttpsCertificates() throws Exception {
63
64 // Create a trust manager that does not validate certificate chains:
65 System.out
66 .println("\n[WARNING] SSL INSECURE: Trusting all https certificates\n");
67
68 javax.net.ssl.TrustManager[] trustAllCerts =
69
70 new javax.net.ssl.TrustManager[1];
71
72 javax.net.ssl.TrustManager tm = new miTM();
73
74 trustAllCerts[0] = tm;
75
76 javax.net.ssl.SSLContext sc =
77
78 javax.net.ssl.SSLContext.getInstance("SSL");
79
80 sc.init(null, trustAllCerts, null);
81
82 javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(
83
84 sc.getSocketFactory());
85
86 }
87
88 public static class miTM implements javax.net.ssl.TrustManager,
89 javax.net.ssl.X509TrustManager {
90 public java.security.cert.X509Certificate[] getAcceptedIssuers() {
91 return null;
92 }
93
94 public boolean isServerTrusted(
95 java.security.cert.X509Certificate[] certs) {
96 return true;
97 }
98
99 public boolean isClientTrusted(
100 java.security.cert.X509Certificate[] certs) {
101 return true;
102 }
103
104 public void checkServerTrusted(
105 java.security.cert.X509Certificate[] certs, String authType)
106 throws java.security.cert.CertificateException {
107 return;
108 }
109
110 public void checkClientTrusted(
111 java.security.cert.X509Certificate[] certs, String authType)
112 throws java.security.cert.CertificateException {
113 return;
114 }
115 }
116
117

Maven integration


For Continuous Integration we can use the above client to deploy our project directly from Maven. We can do that by using the org.codehaus.mojo plugin.

 


……
<wls-targets>MyCluster</wls-targets>
<wls-user>weblogic</wls-user>
<wls-password>MyPassword</wls-password>
<restEndpoint>https://<WLS-AdminServerIP>:7002/management/wls/latest/deployments/application </restEndpoint>
<deployment>target/MyApp.war</deployment>
<deploymentName>MyApp.war</deploymentName>
……
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2</version>
<configuration>
<mainClass>com.oracle.cloud.demo.oe.rest.JerseyClientPost</mainClass>
<arguments>
<argument>${wls-user}</argument>
<argument>${wls-password}</argument>
<argument>${restEndpoint}</argument>
<argument>target/MyApp.war</argument>
<argument>{ name: '${deploymentName}', targets: ['${wls-targets}'] }</argument>
</arguments>
</configuration>
<executions>
<execution>
<id>WLS-REST-DEPLOY</id>
<phase>WLS-REST-DEPLOY</phase>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
</plugin>
……


Your maven invocation will look something like this: clean package exec:java

Wednesday, April 1, 2015

Oracle Java Cloud Services – On-Premise (Intranet) Integration

 

Introduction

Scenario: You need to migrate you Java EE application to the Oracle Java cloud Services – PaaS. Your Java EE application has some dependencies on services that run in a restricted network (company, hotel, hospital)  on a "server" which you want to access from outside that network. You cannot forward ports to that machine, but you can ssh outside (to your own server) from that “server” or from another machine under the same intranet/firewall with your “server”.

You need another server or even the same “server” to which you setup a persistent ssh connection with a reverse tunnel. You will ssh from one of the machine inside your intranet to the JCS virtual machine. On that ssh session you’ll configure a reverse tunnel that will tunnel your traffic on the port X from your sever to port Y on your JCS VM.

Make sure you have permission to do this from the administrators. They generally don't like holes in the firewall/security. They don't block it for no reason.

We need to make sure this kind of tunnel is highly available and this is why we will configure it as a service with the following properties:

  • Auto-recreation after failure
  • Auto-start during OS boot

As an example we will configure a VirtualBox VM running the VPN client, so being inside the organization intranet. This VBox VM will run 2 services: OpenLdap (port 389) and Oracle 11g XE Database(port 1521).

We’ll configure highly available reverse tunneling from the VBox VM to the Oracle Java Cloud Service Weblogic Admin Server vm for the upper mentioned traffic.

Then we will configure JCS Weblogic Admin Server to consume the upper mentioned LDAP and Database services that are running inside the Oracle Intranet.

 

VirtualBox VM

 

Step 1: Make sure your VirtualBox VM is inside the Intranet

  • Make sure your VPN connection is up:

image

  • Double check an Intranet IP has been allocated to your VirtualBox VM:

image

 

  • Set up your system proxy configurations:

image

Step 2: Check that OpenLDAP and Oracle Database are up and running on your VBox VM

  • Check that your local VBox VM database is up and running and listening on port 1521:

image

  • Check that your local VBox VM OpenLDAP is up and running and listening on port 389:

image

 

JCS WLS Admin Server VM – set up ssh reverse tunneling

 

Step 3: Get the IP of the JCS Admin Server VM

  • Go to the Java cloud Service console and copy the JCS Admin VM IP:

image

  • Add this ip to the VBox VM /etc/hosts file:

image

  • Using the provided key ssh from the VirtualBox VM to the JCS WLS Admin Server VM:

image

Step 4: Configure paswordless root SSH by using public key authentication on JCS WLS AdminServer VM

  • Generate a new ssh key for root user on the JCS Admin WLS VM:
    • Switch to root on the JCS Admin Server WLS VM

    • Generate a new ssh key:

    • Set the file for the key to /home/opc/rootsshkey
    • Set empty passphrase

image

    • Create a new file called authorized_keys under /root/.ssh/

    • Paste inside the /root/.ssh/authorized_keys the entire content of the public key created above: /home/opc/rootsshkey.pub

image

    • Set the proper permissions:

    • To enable the key copy process change the ownership of the previously generated public keys:

    • Copy the ssh key from the remote host(JCS VM) to the local host(VirtualBox VM)

    • Set the right permissions for the root ssh key on the VBox VM:

  • Enable root passwordless ssh key login on the JCS WLS Admin VM:
    • As root on the JCS WLS Admin VM edit /etc/ssh/sshd_config

    • Set the following configuration:

image

    • Restart sshd service on the JCS WLS Admin VM:

image

    • SSH without using the password and by using the generated key from the VBox VM to the JCS WLS Admin VM:

image

Step 5: Create the reverse ssh tunnel

Note: SSH –R: Specifies that the given port on the remote (server) host is to be forwarded to the given host and port on the local side. This works by allocating a socket to listen to port on the remote side, and whenever a connection is made to this port, the connection is forwarded over the secure channel, and a connection is made to host port hostport from the local machine.

By default, the listening socket on the server will be bound to the loopback interface only. This may be overridden by specifying a bind_address. An empty bind_address, or the address ‘*’, indicates that the remote socket should listen on all interfaces. Specifying a remote bind_address will only succeed if the server's GatewayPorts option is enabled.

  • Check that there is nothing listening on the JCS WLS Admin VM on ports 1522, respectively 389.

  • Let’s reverse tunnel (forward) traffic on ports 1521 and 389 from the intranet VBox VM to the JCS WLS Admin VM ports: 1522 and, respectively 389. We’ll do this in a SSH session started on the VBox VM against the JCS WLS Admin VM:

  • Check if the JCS WLS Admin VM is listening now on ports 1522 and 389:

image

Create a highly available ssh reverse tunnel

High availability is the most important concern of this approach.

In the next section will transform the ssh tunnel configured above in a linux service that will also auto restart the tunnel in case of failure. This linux service should run the ssh tunneling process as a background process.

We will also make this service start at OS boot.

 

STEP 6: Creating an SSH tunnel high available linux service

  • For creating a high available ssh tunnel linux service we can create our own /etc/ini.d/ script or we can use an already existing tool. In this document we will use an already existing tool called autossh.

Note: Autossh is a program to start an instance of ssh and monitor it, restarting it as necessary should it die or stop passing traffic. The idea is from rstunnel (Reliable SSH Tunnel), but implemented in C. Connection monitoring is done using a loop of port forwardings. It backs off on the rate of connection attempts when experiencing rapid failures such as connection refused.

Download the autossh rpm for Red Hat/Oracle Enterprise Linux from here.

  • Create the autossh ssh tunneling session from the VBox VM to the JCS WLS Admin VM. First make sure you have killed your previous tunneling sessions.

  • Now you have a highly available service like background process that creates to reverse tunnels.

STEP 7: Make this service start at OS boot

  • For making this service start at boot we can either create a /etc/rc.d/ script or use a simple Oracle Linux UI menu feature called: Startup Applications.
  • Navigate to System -> Preferences -> Startup Applications

image

  • Create a new entry there that will contain the autossh command above:

image

 

Connect from JCS WLS Server to the on premise (intranet) Database and OpenLDAP servers

 

  • Check that your JCS WLS Admin VM is listening on ports: localhost:1522 for database and locahost:389 for OpenLDAP

image

STEP 8: Create a JCS WLS datasource connection against the Oracle 11g XE Database that runs inside Oracle intranet

  • Access the JCS Weblogic admin console and configure a new datasource with the following details:

image

STEP 9: Create a JCS WLS OpenLDAP authentication provider against the OpenLDAP server that runs inside Oracle intranet

  • We have already created an OpenLDAP provider called OpenLDAPIntranet that can be found under: JCS WLS Admin Console -> security Realms -> myrealm -> Providers:

image

  • The OpenLDAP provider relevant configuration is:

image

  • In order to test the OpenLDAPIntranet provider navigate to JCS WLS Admin Console -> security Realms -> myrealm -> Users and Groups and identify the OpenLDAPIntranet users:

image