Building an Infor Grid Lab – Part 2

I am building an Infor ION Grid laboratory manually without LifeCycle Manager (LCM) for my learning purposes. In the previous post I had installed a minimalist Grid using an old version. Today I will install the latest version.

1. Preparation

Choose values for the following properties (here are some sample values):

Grid name e.g. Grid
Grid folder e.g. C:\Infor\Grid\
Database name e.g. InforIONGrid
Host name e.g. localhost
Host address e.g. 127.0.0.1
Grid agent port e.g. 50003
Registry port e.g. 50004

2. Download latest version

Download the latest version of the Grid; as of today (5/12/2017) it is 11.1.13.0.77:

3. Create folder structure

Choose a home directory for your Grid, e.g. C:\Infor\Grid\ where the folder must match the Grid name (e.g. Grid), and create these sub-folders:

Grid
+---config
+---drivers
+---resources
\---secure

4. Copy JAR files

Let’s find the main grid-core.jar and supporting JAR files:

  1. Unzip the LCM file.
  2. Go to folder: Grid_Installer_11.1.13.0.77.lcm\products\Infor_ION_Grid_11.1.13.0\tasks\
  3. Select these JAR files:
    bcmail-jdk16.jar
    bcprov-jdk16.jar
    grid.httpclient.jar
    grid.liquibase.jar
    grid-core.jar
    javax.servlet-api.jar

  4. Copy them to your Grid\resources\ folder:

5. Create database

Let’s create the Grid database:

  1. Install SQL Server and SQL Management Studio (I installed SQL Server 2014 Express Edition at no cost), and ensure it works correctly:
  2. Download and install the Microsoft SQL Server JDBC Driver, and ensure you can connect to the database via JDBC (e.g. with SQuirreL):
  3. Create a new database (e.g. InforIONGrid):
  4. Run the following SQL to create the configuration table:
    CREATE TABLE GRIDCONF (
        GRID varchar(64) NOT NULL,
        TYPE varchar(32) NOT NULL,
        NAME varchar(128) NOT NULL,
        TS numeric(20, 0) NOT NULL,
        DATA varbinary(max) NULL,
        SEQID numeric(5, 0) NOT NULL
    )

  5. Run the following SQL to create a Grid configuration with name (e.g. Grid), runtime XML and topology XML (replace the Grid name and XML contents as needed):
    DECLARE @runtime VARCHAR(300)
    DECLARE @topology VARCHAR(300)
    SET @runtime =
    '<?xml version="1.0" ?>
    <runtime xmlns="http://schemas.lawson.com/grid/configuration_v3">
        <bindings />
        <sessionProviders />
        <routers />
        <contextRoots />
        <propertySettings />
    </runtime>'
    SET @topology =
    '<?xml version="1.0" ?>
    <topology xmlns="http://schemas.lawson.com/grid/configuration_v3">
        <hosts>
            <host name="localhost" address="127.0.0.1" gridAgentPort="50003" />
        </hosts>
        <registry host="localhost" port="50004" />
    </topology>'
    INSERT INTO GRIDCONF (GRID, TYPE, NAME, TS, DATA, SEQID) VALUES ('Grid', 'runtime' , 'null', 0, CONVERT(varbinary(max), @runtime), 0)
    INSERT INTO GRIDCONF (GRID, TYPE, NAME, TS, DATA, SEQID) VALUES ('Grid', 'topology' , 'null', 0, CONVERT(varbinary(max), @topology), 0)

  6. Verify the result:
    SELECT GRID, TYPE, NAME, TS, DATA, LEFT(DATA, LEN(DATA)), SEQID
    FROM GRIDCONF

  7. Copy the JDBC driver to your Grid\drivers\ folder:
  8. Create the JDBC configuration file at Grid\config\jdbc.properties with the values you chose above and with your database password Base64-encoded (in a production environment, keep this file secure):
    driverDir=C:/Infor/Grid/drivers
    url=jdbc:sqlserver://localhost:1433;databaseName=InforIONGrid
    dbType=sqlserver
    user=sa
    encryptedPwd=cGFzc3dvcmQxMjM=
    schema=dbo

Configuration Import & Edit

Alternatively, instead of using SQL to insert the runtime and topology XML into the GRIDCONF table, we can run the following command to import the XML files from the Grid\config\ folder into the GRIDCONF table (it requires the EXISTING_GRIDS table):

CREATE TABLE EXISTING_GRIDS (
    GRID_NAME varchar(64) NOT NULL,
    GRID_VERSION varchar(32) NOT NULL,
    MODIFIED_BY varchar(128) NULL,
    TIMESTAMP numeric(20, 0) NOT NULL,
)
INSERT INTO EXISTING_GRIDS (GRID_NAME, GRID_VERSION, MODIFIED_BY, TIMESTAMP) VALUES ('Grid', 1, 'Thibaud', 0)
java -cp resources/grid-core.jar;resources/grid.liquibase.jar;drivers\sqljdbc42.jar com.lawson.grid.config.JDBCConfigAreaRuntime C:\Infor\Grid

Then, we can use this other command to launch the XML Editor and edit, format and validate the XML:

java -cp resources/grid-core.jar;resources/grid.liquibase.jar;drivers\sqljdbc42.jar;resources/bcprov-jdk16.jar;resources/bcmail-jdk16.jar com.lawson.grid.config.client.ui.Launch

6. Security

The Grid uses cryptography to protect its network traffic. We need the following four files in the folder Grid\secure\ . For now, I will simply get these files from an existing Grid, and I will create new ones later.

Grid.ks
server.key
server.ks
server.pw

7. Start the Grid

Start the Grid:

java -cp resources/grid-core.jar;resources/bcprov-jdk16.jar;resources/bcmail-jdk16.jar;resources/grid.liquibase.jar;drivers\sqljdbc42.jar;resources/javax.servlet-api.jar;resources/grid.httpclient.jar com.lawson.grid.Startup -registry -configDir . -host localhost -logLevel ALL

8. Grid Management Pages

Start the Grid Management Pages and connect to the registry at localhost:50004:

java -jar resources/grid-core.jar

9. Topology View

For the Topology View, we need another table:

CREATE TABLE APPMAPPINGS (
    GRID varchar(256) NOT NULL,
    NAME varchar(256) NOT NULL,
    HOST varchar(256) NOT NULL,
    ID varchar(64) NULL,
    PENDINGID varchar(64) NULL,
    STATE varchar(32) NOT NULL,
    LOGNAME varchar(256) NULL,
    PROFILENAME varchar(64) NULL,
    PROFILEDATA varbinary(max) NULL,
    JVMID varchar(64) NULL
)

Result

We now have a minimalist Grid installed manually without LCM.

Future work

In the next post, I will show how to create the security files.

Conclusion

That was how to install a minimalist latest version of the Infor ION Grid manually without LifeCycle Manager. We have the minimalist folder structure, database, configuration, commands, Grid Management Pages, and Topology View. I will continue in the next post.

That’s it!

M3 Ideas, now 282 subscribers.

Related posts

Building an Infor Grid Lab – Part 1

These days I am doing a lot of work with the Infor ION Grid – to learn, troubleshoot, and do penetration testing – and I need to setup my own laboratory. I will follow the footsteps of PotatoIT’s Lab.

Grid concepts

The Infor ION Grid is a proprietary application framework to run Java applications in a distributed, redundant, fail-over, load balanced, scalable, performant, and secure environment, sort of a crossing between IBM WebSphere Application Server (WAS) and Platform as a Service (PaaS), for the purposes of Infor products, and that over the years has become a rich framework that helps power the Infor CloudSuite. Grid concepts are explained in the Infor documentation and in my previous work. Basically, there are: hosts (physical/virtual machines), a registry (to keep track of the nodes), nodes (JVM), applications (e.g. M3), routers (to direct network traffic), and more.

Download

The Grid is available for download from the Infor Xtreme Product Download Center:

Documentation

The Installation Guide has a chapter Installing Infor ION Grid:

LCM? No.

The documentation says Infor LifeCycle Manager (LCM) is a prerequisite to install the Grid. But in my previous encounter with LCM I had concluded I can reproduce installation steps manually without LCM, albeit with a lot of work. Anyway, for my purposes I just need a minimal Grid without Infor M3 which makes the installation easier. To that end, I set out to learn how to install a minimal Grid manually without LCM. I will split my learning into several blog posts.

Version 0.x

In my archives of 10 years ago I found an early internal development unreleased version of the Grid with some documentation. It was a pure Java application that started Grid hosts, nodes, routers, registry, and user interface. It did not have database, certificates, configuration, or web server. It was not available publicly. Thanks to its simplicity, I will use it as a starting point of my learning.

1) Start the registry

java -cp grid.jar com.lawson.grid.Startup -registry -groupName THIBAUD

2) Start a node

java -cp grid.jar com.lawson.grid.Startup

3) Start a router

java -cp grid.jar com.lawson.grid.Startup -router

4) Start the user interface

java -jar grid.jar localhost 44444

Result

We have a minimal Grid with a host, a registry, a node, a router, and a user interface.

Future work

In my next blog posts, I will:

  • Install a later version of the Grid
  • Use the new Grid installer
  • Install the Grid on Linux and PostgreSQL

Conclusion

That was a starting point for me to learn how to install a minimal Infor ION Grid manually without LifeCycle Manager. I will continue in the next post.

That’s it!

Related posts

Site map

I just re-organized this blog: I grouped all posts by category, I added a menu, I made this sitemap, and I re-wrote the pages About and Authors. The idea is to better navigate this blog so you can find more content.

Tips on how to use this blog:

  • Use Google Search to search this blog. Use the keyword m3ideas to filter results, e.g. the search  m3ideas m3 web services  will lead you to this blog. You can refine even further with the operator site, e.g.  site:m3ideas.org m3 web services . This blog has a search bar, but unfortunately the WordPress theme doesn’t let me put it above the menu so it’s buried below.
  • Click on the title of a blog post to open it in full and reveal the additional sections.
  • Read the Related and Pingback sections at the bottom of the blog posts, I use them to cross-reference posts with each other so you can find other content related to what you are reading; they are created automatically by WordPress. Sometimes I create my own Related section.
  • Read the comments at the bottom of a blog post, that’s where I put latest updates and notes about a post that I didn’t incorporate in the original post.
  • Please, if you like what you read, click Like, leave a comment, let me know what you think, subscribe to stay informed, and share around you. If there is something you want to write about, let me know, and I will send you an author invite. Even better, start your own blog and create your own community. The more information the better our work.
  • This blog is volunteer and community based and wouldn’t exist without your contribution. Thank you.

3D

Augmented Reality

Customer Lifecycle Management (CLM)

Event Hub, Event Analytics

External Program Connector (EPC)

Field Audit Trail (FAT)

H5 Client

Hacking

IBrix

Ideas

M3 MI Data Import for Web Services (MDIWS)

I just learned the existence of the M3 MI Data Import for Web Services (MDIWS), which is the equivalent of the traditional M3 Data Import (MDI) but using the M3 API REST/JSON endpoint instead of the traditional proprietary binary endpoint.

The tool and documentation are straightforward, so I will just promote it here with some screenshots:

Here are the executable and documentation:

Here is a sample semicolon separated CSV file with data, no header:

Here is the tool in action:

As a reference, here is the traditional M3 Data Import tool:

Thanks Björn P. for the tool.

That’s it!

Poll: Infor e-Commerce Application Installer (IAI)

I have a question regarding Infor e-Commerce (f.k.a. e-Sales): have you ever used the Infor Application Installer (IAI) to deploy e-Commerce applications via ZIP files instead of via the Development Studio?

Scenario

We have two different environments, development and production, that are physically isolated.

Our development environment is integrated: it has the e-Commerce Development Studio, the Subversion repository, and the e-Commerce Server, all on the same host. Thus, we can simply deploy the e-Commerce application from the Studio directly to the server.

Problem

Our production environment, on the other hand, is isolated from that network: it has the e-Commerce server, but it does not have the e-Commerce Development Studio, and it does not have access to the Subversion repository either. Then, how do we deploy the e-Commerce application?

Unskillful solution

We cannot deploy from the Studio because the development environment does not have access to the production network, the two are isolated from each other.

One solution is to install the Studio on the production environment and give it access to the Subversion repository, to mimic the development environment. But because the production environment is isolated, it does not have access to the Subversion repository, so we would have to make a copy of the source code.

I challenge this solution. Indeed, we would end up with double maintenance of the Studio and of the source code. We would risk generating a non-identical version of the application. And we would risk creating an accidental branch of the source code then have to resolve and merge. There ought to be a simpler and more elegant solution.

Documentation

There are two e-Commerce documents that explain the application deployment process and how it uses the Infor Application Installer (IAI):

Development Studio

According to the documentation, the Studio generates this temporary ZIP file:
C:\Windows\Temp\laitmp\upload\infor-ecom-b2b.zip

The ZIP file has a datasources folder with connection information in XML files (e.g. movex.dsc and sqlserver.dsc):

Infor Application Installer (IAI)

The Infor Application Installer (IAI) has the following Servlets and JSP to upload the ZIP file and deploy the application:

Proposed solution

The solution I propose is to use the Infor Application Installer (IAI) to deploy a modified version of the temporary ZIP file.

We would take the temporary ZIP file from the development environment, make a copy of it, unzip it, change the datasources connection information, re-zip it all, copy the resulting ZIP file to the production environment, and use the publish JSP to deploy it. We can even write a script to automatically duplicate the file, unzip it, change the connection information, and re-zip it, to reduce the number of manual steps and to avoid possible human errors.

I postulate this new solution is much simpler than the other one as we would just manipulate ZIP files, and we would not need to double maintain another Studio or another source code. And it is elegant because it is part of e-Commerce.

What others think

I asked others for opinion.

The experienced e-Commerce consultant disagrees. He says that all e-Commerce applications MUST be deployed from the Studio in order to make sure they are working properly, that it is the right way, that everyone uses this method, that there is no other method, and that Infor would not support another method.

Nonsense.

Similarly, Infor Support reached out to the development team who reached out to the e-Commerce product owner who said the ZIP file deployment can be done for DEVELOPMENT purposes only but it is NOT RECOMMENDED, that it can be explored at your own risk, and that support would NOT be provided if further issues or concerns occur.

Rubbish.

I do not believe either of these responses. e-Commerce is about 15 years old, and most of the original developers are no longer part of the company. I believe the responses above are from new developers that lack knowledge, and are not willing to try another way. Or perhaps there is a valid reason that they have not yet articulated.

Poll

What about YOU? Do YOU know the answer?

Let me know in the comments below, please. Thank you.

Access Control on Grid Management Pages

It is my great pleasure to be an author of M3 Ideas. Thanks very much for Thibaud’s invitation.

I put my foot into M3 water five years ago and M3 Ideas has been giving me great help in developing my technical skills since then. It is my great honor to share my work and make contributions to it.

The issue of access control on grid management pages has been a trouble to me for nearly two years. The installation web page of ISO, in our case, http://BE Server:19005/LSO/index.html is very close to the grid information page, http://BE Server:19005/grid/info.html and the grid management pages, http://BE Server:19005/grid/ui/#. It is fairly easy for a user with web skills to figure out the latter two web pages and he/she can explore and check basically all the grid information.

Although the grid management pages requires credentials to log on to be able to start or stop a process, a user actually can try the default account,  and the famous password, which are mentioned by the Companion, to get full control. This is not a joke, but a real case. It makes M3 system really vulnerable. Even if users do not make any actions to it, there is no sense and it is ridiculous to disclose all the system information to the public.

We did raise InforXtreme case for Infor to fix it. However, they could not give a robust solution after a long time discussion. The reason is the grid management pages and LSO is sharing the same Java process in the BE server. If you check the TCP connections and processes using netstat in the BE server, you would find there is no way to tell which connection is from the grid management pages visit or from LSO request, which is the biggest technical challenge.

After many tests it seems the most feasible solution is to analyze each incoming and outgoing TCP packet in the BE sever to see whether there are any patterns. If we can be 100% sure that a remote IP address is visiting the grid management pages according to the patterns, then we can apply IPSec to block it automatically. Although it means that IP address cannot use LSO as well, it can be sorted out by manually removing it from the black list given a promise is made not to visit again. If the promise is broken, there is no second chance for them. It is something like Damocles’ Sword.

So I made a Console Application using C# as follows.


using System;
using System.Collections.Generic;
using SharpPcap;
using SharpPcap.LibPcap;
using System.Text;
using System.Net.Mail;
using System.Diagnostics;

namespace CA_Grid
{
class CA_Grid
{
static List<string> jsIPList;

static void Main(string[] args)
{
jsIPList = new List<string>();

var varDevices = CaptureDeviceList.Instance;

if(varDevices.Count<1) { Console.WriteLine("No devices were found on this machine"); return; } Console.WriteLine("The following devices are available on this machine:"); Console.WriteLine("----------------------------------------------------"); Console.WriteLine(); int i = 0; foreach(var varDec in varDevices) { Console.WriteLine("{0}) {1}", i, varDec.Description); i++; } Console.WriteLine(); Console.Write("-- Please choose a device to capture: "); int varChoice = 0; if (!int.TryParse(Console.ReadLine(), out varChoice) || varChoice>=i)
{
Console.WriteLine("The device is not valid!");
return;
}

ICaptureDevice varDevice =varDevices[varChoice];

varDevice.OnPacketArrival += Device_OnPacketArrival;

varDevice.Open();
varDevice.Filter = "ip and tcp";
Console.WriteLine();
Console.WriteLine("-- Listening on {0}, hit 'Ctrl-C' to exit...",varDevice.Description);

varDevice.Capture();

varDevice.OnPacketArrival -=Device_OnPacketArrival;
varDevice.Close();
}

private static void Device_OnPacketArrival(object sender, CaptureEventArgs e)
{
try
{
string varMIP = "BE Server IP Address";
if (e == null || e.Packet == null)
return;

if (e.Packet.Data == null)
return;

var varPacket = PacketDotNet.Packet.ParsePacket(e.Packet.LinkLayerType, e.Packet.Data);
if (varPacket == null)
return;

if (varPacket.GetType()!=typeof(PacketDotNet.EthernetPacket))
return;

var varIP = (PacketDotNet.IpPacket)varPacket.Extract(typeof(PacketDotNet.IpPacket));
if (varIP == null)
return;

string varDIP = varIP.DestinationAddress.ToString();
string varSIP = varIP.SourceAddress.ToString();

if (varSIP != varMIP)
return;

var varTCP = (PacketDotNet.TcpPacket)varPacket.Extract(typeof(PacketDotNet.TcpPacket));
if (varTCP == null)
return;

if (varTCP.PayloadData == null)
return;

var varData = Encoding.UTF8.GetString(varTCP.PayloadData);

if (varData == null)
return;

string varDPort = varTCP.DestinationPort.ToString();
string varSPort = varTCP.SourcePort.ToString();

bool varHTTPS = varSPort == "443";
bool varHTTP = varSPort.Contains("19005") && varData.Contains("text/html") && varData.Contains("gzip");
if ((varHTTPS || varHTTP) && !jsIPList.Contains(varDIP))
{
jsIPList.Add(varDIP);

string varWay = varHTTP ? "HTTP" : "HTTPS";
string varM1 = "----------------------------------------------------";
string varM2 = string.Format("{0}, {1}, {2}", DateTime.Now, varDIP, varWay);
string varM3 = "Original IP packet: " + varIP.ToString();
string varM4 = "Original TCP packet: " + varTCP.ToString();
string varM5 = "Original TCP Header: " + varData;

if (varDIP != "IP to Exclude")
{
using (var varProcess = new Process())
{
varProcess.StartInfo.FileName = "cmd.exe";
varProcess.StartInfo.UseShellExecute = false;
varProcess.StartInfo.RedirectStandardInput = true;
varProcess.StartInfo.RedirectStandardOutput = true;
varProcess.StartInfo.RedirectStandardError = true;
varProcess.StartInfo.CreateNoWindow = true;
varProcess.Start();

string varCommand = "netsh ipsec static add filter filterlist=\"IP_Filter_Grid\" srcaddr=" + varDIP + " dstaddr=me protocol=TCP mirrored=No";
varProcess.StandardInput.WriteLine(varCommand);
varProcess.StandardInput.WriteLine("exit");
varProcess.StandardInput.AutoFlush = true;
varProcess.WaitForExit();
}
}

Console.WriteLine(varM1);
Console.WriteLine(varM2);
Console.WriteLine();
Console.WriteLine(varM3);
Console.WriteLine();
Console.WriteLine(varM4);
Console.WriteLine();
Console.WriteLine(varM5);
Console.WriteLine();

using (var varSMTP = new SmtpClient("Mail Forwarder IP Address"))
{
using (var varMail = new MailMessage())
{
varMail.From = new MailAddress("from address");
varMail.To.Add("my e-mail address");
varMail.Subject = "Unauthorised Access to Infor Grid Management Pages";
varMail.Body = varM1 + "\r\n" + varM2 + "\r\n" + "\r\n" + varM3 + "\r\n" + "\r\n" + varM4 + "\r\n" + "\r\n" + varM5;
varSMTP.Send(varMail);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}

SharpPcap can be downloaded from GitHub. It has two Dlls, SharpPcap for capturing packets and Packet.Net for packet analysis. WinPcap needs to be downloaded and installed into the BE server. There is no need to restart the server after installation.

The console application is running in an asynchronous mode, so there is trivial impact on the M3 performance.

The script starts with detecting all the devices first. Then it asks for a device number to listen to. Each device could be a network adapter. A filter is applied to the device for IP and TCP packets. An event to capture the packets is attached to the device. Finally the listening starts.

The event handler has the key logic.

1. Two patterns to find the visit to the grid management pages.

(1) HTTPS: The source port would be 443.

(2) HTTP: The packet contains text/html and gzip.

HTTPS communication is encrypted, so we cannot analyze the packet content. Fortunately LSO never uses the port of 443.

2. If the destination IP address is not on the white list, then it would be added to the IPSec filter list. We need to manually create a IPSec policy and a IPSec filter & Action beforehand.

3. Console would output the captured information.

4. An e-mail alert would be sent with the captured information.

5. A global variable of jsIPList is used to keep the IP addresses already captured to avoid repeated alerts.

Then it works. There were three IT users from local division, who still tried to access to the grid management pages on Monday as usual. Then they found they could not visit it any more, as well as LSO. So far there are no normal users to be blocked, no matter whether they run M3 programs, or download LSO from the installation web page. All of sudden the two-year headache is gone.

If you have the similar concerns to me, I hope the above solution would give you a ride. If you have got any issues using the solution, please reply to this post or send me an e-mail at warren.dahai.hou@gmail.com.

Finally my heartfelt appreciations to all the authors on this website, as well as the author of SharpPcap and Packet.Net.

Let’s Encrypt Infor e-Commerce

Today I setup SSL/TLS for Infor e-Commerce using Let’s Encrypt, the new free, automated, and open Certificate Authority (CA).

Topology

Infor e-Commerce (f.k.a. Movex e-Sales) is a J2EE application running on IBM HTTP Server (IHS) and IBM WebSphere Application Server (WAS), where IHS is on the DMZ and has a certificate on port 443, and where WAS is on the local network and has a certificate on port 9043. That’s two certificates.

Step 1. Backup

Backup the following IHS and WAS files in case you need to restore them:

IBM
├───HTTPServer
│   ├───conf
│   │       httpd.conf
│   │
│   └───Plugins
│       └───config
│           └───webserver1
│                   plugin-cfg.xml
│                   plugin-key.crl
│                   plugin-key.kdb
│                   plugin-key.rdb
│                   plugin-key.sth
│
└───WebSphere
    └───AppServer
        └───profiles
            └───AppSrv01
                ├───config
                │   └───cells
                │       └───Node01Cell
                │           │   security.xml
                │           │
                │           └───nodes
                │               ├───AppNode01
                │               │       key.p12
                │               │       trust.p12
                │               │
                │               └───WebNode01
                │                   └───servers
                │                       └───webserver1
                │                               httpd.conf
                │                               plugin-cfg.xml
                │                               plugin-key.crl
                │                               plugin-key.kdb
                │                               plugin-key.rdb
                │                               plugin-key.sth
                │                               server.xml
                │
                └───etc
                        clientCert.arm
                        key.p12
                        serverCert.arm
                        trust.p12

Step 2. Setup IHS on DMZ

Setup IHS on the DMZ (DNS, firewall, etc.) to serve requests on the Internet:

Step 3. Key database

Let’s verify the key database.

The public/private keys, certificate signing requests (CSR), intermediate certificates, and signed certificates are managed in the IBM key database file format (KDB). Apparently, that format does not allow importing private keys that are created externally (e.g. with OpenSSL or EFF’s Certbot), so we must create them internally using either the IBM Key Management tool (iKeyman), the WAS admin console, or the gsk7cmd command. I will use iKeyman.
  1. Find the latest version of iKeyman (there are several versions of iKeyman throughout IHS and WAS); use version 8.0.399 or later for the most recent cryptographic properties (e.g. SHA256):
    C:\IBM\HTTPServer\Plugins\bin\ikeyman.bat
  2. Open the default plugin key database:
    C:\IBM\HTTPServer\Plugins\config\webserver1\plugin-key.kdb

    The default password is WebAS. You can recover a lost password by calculating the stash (plugin-key.sth) XOR 245, or you can create a new key database from scratch.
  3. Ensure the Signer Certificates contains the same signer certificates as the WAS default trust store (e.g. datapower and root); compare by fingerprints. They should already be there; otherwise, extract them from the WAS admin console, and add them to the key database. That will allow IHS to trust WAS over SSL:

Step 4. Generate key pair + CSR

Let’s generate a public/private key pair and CSR.

  1. In iKeyman, delete the default personal certificate:
  2. Create a new key pair and CSR with the FQDN and cryptographic properties of your choice, leave the email address blank or certbot will throw an error, and save to some temporary file (e.g. certreq.arm):
  3. The result is a new public/private key pair in the key database (plugin-key.kdb) and a new CSR in PKCS#10 format (certreq.arm):

Step 5. Submit CSR to Let’s Encrypt

Let’s submit the CSR to Let’s Encrypt and get a signed certificate in return.

In the ACME protocol, the Let’s Encrypt servers will issue a set of challenges, and our web server must respond correctly to prove ownership of the domain. Normally, it is all automated, but there is no certbot plugin for IHS, so I will use the manual plugin. And because I have not yet tried an ACME client for Windows, I will use certbot on my Linux virtual machine.
  1. Execute certbot with the CSR as input:
    certbot certonly --manual --csr ~/certreq.arm
  2. Enter your email address, accept the Terms of Service, enter the domain name, and select Yes to log your IP address. It will present a challenge (a signed nonce):
  3. Create the specified file with the specified content at the specified path:
    C:\IBM\HTTPServer\htdocs\.well-known\acme-challenge\
  4. Test the URL over the Internet (the Let’s Encrypt servers will request it):
  5. Back in certbot, press ENTER to complete the domain validation:
  6. The result is a signed certificate, intermediate chain, and full chain:
    0000_cert.pem
    0000_chain.pem
    0001_chain.pem

Step 5bis. Submit CSR to another CA

If you prefer, you can skip Let’s Encrypt, and submit the CSR to another CA of your choice (Verisign, Thawte, GoDaddy, Comodo, etc.).

Step 6. Add the certificate

Let’s receive the signed certificate into the key database.

  1. In iKeyman > Personal Certificates, receive the certificate 0000_cert.pem:
  2. Extract the Let’s Encrypt root certificate from one of the chains (e.g. with OpenSSL), or download it directly from IdenTrust at DST Root CA X3, and save it to a temporary file somewhere (e.g. dst_root.pem).
  3. In Signer Certificates, add the intermediate certificate 0000_chain.pem (Let’s Encrypt Authority X3), and the root certificate dst_root.pem (DST Root CA X3):
  4. Copy the key database files to WAS:
    C:\IBM\WebSphere\AppServer\profiles\AppSrv01\config\cells\Node01Cell\nodes\Node01\servers\webserver1\

Step 7. SSL in IHS

Let’s enable SSL in IHS.

  1. Open the IHS configuration file in a text editor (e.g. Notepad):
    C:\IBM\HTTPServer\conf\httpd.conf
  2. Add the following directives:
    ServerName example.com
    LoadModule ibm_ssl_module modules/mod_ibm_ssl.so
    KeyFile C:\IBM\HTTPServer\Plugins\config\webserver1\plugin-key.kdb
    SSLStashFile C:\IBM\HTTPServer\Plugins\config\webserver1\plugin-key.sth
    Listen *:443
    <VirtualHost *:443>
    SSLEnable
    SSLProtocolDisable SSLv2 SSLv3
    SSLServerCert default
    </VirtualHost>
    SSLDisable
  3. Add the following to redirect all HTTP traffic to HTTPS; it is required for at least the login page, password change, credit card, XML Gateway, and a few other sensitive pages; it is optional for the rest:
    LoadModule rewrite_module modules/mod_rewrite.so
    RewriteEngine on
    RewriteCond %{SERVER_PORT} =80
    RewriteRule ^(.*) https://%{SERVER_NAME}%{REQUEST_URI} [R,L]
  4. Copy httpd.conf to WAS:
    C:\IBM\WebSphere\AppServer\profiles\AppSrv01\config\cells\Node01Cell\nodes\Node01\servers\webserver1\httpd.conf

Step 8. SSL in WAS

Let’s enable SSL in WAS.

  1. Ensure the default key store has a personal certificate signed by the signer certificates verified earlier:
  2. Set the default SSL configuration to use that server certificate (default):

WAS will not have the Let’s Encrypt certificate, but that’s OK for now.

Step 9. Backup + restart

Backup the files, and restart IHS and WAS.

Result

The result is that the browser now trusts the site:

Limitations

Let’s Encrypt does not provide Extended Validation (EV) certificates.

Future work

  • Use an ACME client for Windows
  • Automate the manual steps
  • Setup certificate renewal
  • Setup certificate revocation
  • Setup the new certificate in WAS too
  • Setup WAS for remote web server management
  • Replace IHS with nginx

Conclusion

That was my setup of SSL/TLS for Infor e-Commerce using Let’s Encrypt as the certificate authority. In a next post, I would like to setup the certificate in WAS too and setup automatic certificate renewal.

That’s it! Please like, comment, subscribe, share, contribute.