JIRA with Apache2 and SSL

Responding to a question from AAC. This worked well for me using Ubuntu 10.04 LTS.

sudo aptitude -y install apache2 apache2-mpm-worker libapache2-mod-proxy-html libapache2-mod-jk
sudo a2enmod ssl proxy_ajp rewrite
sudo vi /etc/apache2/mods-enabled/jk.conf

JkWorkersFile /etc/apache2/workers.properties

sudo vi /etc/apache2/sites-available/jira

NameVirtualHost <ipaddress>:443
<VirtualHost <ipaddress>:443>
        ServerAdmin webmaster@localhost
        ServerName <FQDN>
        SSLEngine On
        SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
        SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
        JkMount / jira
        JkMount /* jira
        JkLogLevel info
        JkLogFile /var/log/apache2/jira_jk.log

sudo vi /etc/apache2/workers.properties


sudo vi /etc/apache2/sites-available/default and add to the bottom:

RewriteEngine   on
RewriteCond     %{SERVER_PORT} ^80$
RewriteRule     ^(.*)$ https://%{SERVER_NAME}$1 [L,R]

sudo vi /opt/jira/conf/server.xml

<Connector port="8006"
    enableLookups="false" protocol="AJP/1.3" minSpareThreads="5" maxThreads="256" URIEncoding="UTF-8" />

sudo a2ensite jira
sudo /etc/init.d/apache2 restart


Get public IP from Linux CLI

Quick way to find your public IP address from a Linux command prompt:

wget -O - -q icanhazip.com

1 Comment

Setup Fail2Ban for JIRA and Confluence

While this article is a good starting point, I thought it was worth documenting some more details on configure Fail2Ban for these applications.

To begin, install Fail2Ban:

sudo aptitude install fail2ban

Ensure that your application is logging access attempts. I have Apache in front of both standalone applications:

LogLevel warn
ErrorLog /var/log/apache2/jira-error.log
CustomLog /var/log/apache2/jira-access.log combined

Next, update the /etc/fail2ban/jail.local file:

enabled  = true
filter   = confluence
action   = iptables-allports[name=Confluence, protocol=all]
           sendmail-whois[name=Confluence, dest=root, sender=fail2ban]
logpath = /var/log/apache2/confluence-access.*
maxretry = 5
bantime = 300

enabled  = true
filter   = jira
action   = iptables-allports[name=JIRA, protocol=all]
           sendmail-whois[name=JIRA, dest=root, sender=fail2ban]
logpath = /var/log/apache2/jira-access.*
maxretry = 5
bantime = 300

You’ll see I decided to ban the offending IP from all ports, not just port accessed. After 5 failed attempts at logging in, the IP is banned for 5 minutes.

Now, setup a filter file for each application:


failregex = <HOST>.*"GET /login.jsp
            <HOST>.*"POST /rest/gadget/1.0/login

ignoreregex =


failregex = <HOST>.*"GET /login.action
            <HOST>.*"POST /dologin.action

ignoreregex =

Finally, restart Apache and Fail2Ban:

sudo /etc/init.d/apache restart && sudo /etc/init.d/fail2ban restart


Fix Fail2Ban on Ubuntu 10.04 LTS

I wrongly assume Fail2Ban was working just fine. I recently audited it’s logs and found very little in the way of banning. I tried to get banned from another host via ssh, but that failed:

2011-04-11 12:32:02,098 fail2ban.actions.action: ERROR iptables -n -L INPUT | grep -q fail2ban-ssh-iptables returned 100
2011-04-11 12:32:02,099 fail2ban.actions.action: ERROR Invariant check failed. Trying to restore a sane environment

Luckily, someone else posted a solution to this on serverfault.

def __processCmd(self, cmd, showRet = True):
beautifier = Beautifier()
for c in cmd:
time.sleep(0.1) ## Added per http://serverfault.com/questions/84569/fail2ban-error-gentoo

1 Comment

JIRA: Change Custom Field Type

I created a custom field for a project with a custom field type of Number. It quickly became evident that this should have been a Text Field instead. Unfortunately, you can’t change this in JIRA (at least not without some convoluted custom field configuration scheming).

Direct SQL access to the rescue!

First, stop JIRA before access the database directly:

--This will change the field type to text
UPDATE customfield set customfieldtypekey = 'com.atlassian.jira.plugin.system.customfieldtypes:textfield',
customfieldsearcherkey = 'com.atlassian.jira.plugin.system.customfieldtypes:textsearcher'
where cfname = '<customfield_name>';
--Find all the possible values that need to be changed
select distinct numbervalue from customfieldvalue where customfield = (select id from customfield where cfname = '<customfield_name>');
--each of these values will be included in the case statement
update customfieldvalue SET stringvalue =
  when numbervalue = 1 then '1'
  when numbervalue = 2 then '2'
  when numbervalue = 3 then '3'
  else stringvalue
where customfield = (select id from customfield where cfname = '<customfield_name>');
--Now, remove the number values that were copied to string values
UPDATE customfieldvalue set numbervalue = null where customfield = (select id from customfield where cfname = '<customfield_name>');

Now, you can start JIRA and check that everything went as planned. Once you’ve confirmed the changes, you should reindex JIRA.

Leave a comment

JIRA: Auto ReIndex

It can be helpful to schedule a reindex of issues after hours (likely when you are asleep!). Here is the shell script that can make that happen:

### SETTINGS ###
### COMMANDS ###
curl -u $USERNAME:$PASSWORD --cookie-jar $COOKIE_FILE_LOCATION --output /dev/null $DASHBOARD_PAGE_URL
curl --cookie $COOKIE_FILE_LOCATION --header "X-Atlassian-Token: no-check" -d "indexPathOption=DEFAULT" -d "Re-Index=Re-Index" -d "indexPath=" --output /dev/null $INDEX_PAGE_URL


JIRA: Add watchers at issue creation

After struggling with this feature request from users for quite some time, I have a way now to add a list of watchers to an issue automatically at creation.

The secret here is (1) you need to add a scripted post function to the creation step in your workflow (so this may mean seperate workflows for individual projects) and (2) you need the Script Runner plugin.

Add the following groovy script somewhere in your CATALINA_BASE and call it in your workflow.

import com.atlassian.jira.ComponentManager

def componentManager = ComponentManager.getInstance()
def watcherManager = componentManager.getWatcherManager()
def userManager = componentManager.getUserUtil()

def watchUsers = {usernames ->
   usernames.each {
         def user = userManager.getUser(it)
         watcherManager.startWatching(user, issue.getGenericValue())

   def users = ["comma", "separated", "usernames"]

Special thanks to Brian LeGros for help on this too!