По работе приходится использовать и обслуживать стек приложений от Atlassian - JIRA, Confluence, Stash, Bamboo, FishEye. Как правило, все они стоят за reverse proxy серверами - apache/nginx и никаких проблем с устранением уязвимостей вроде POODLE там нет. Но недавно пришлось столкнуться с нетипичной настройкой - терминировать SSL надо было средствами самого Tomcat, даже не смотря на то, что сам Atlassian не оказывает поддержку таких конфигураций
Но мы ведь не ищем легких путей. Итак, приступаем.
Согласно официальной документации настраивается все просто. Нам нужно создать свой т.н. keystore и поместить в него наши сертификаты и закрытый ключ. Казалось бы, что может быть проще, но тут я столкнулся с первой проблемой. В сети есть масса примеров как это делается, но все они, как правило приведены с учетом создания само подписанного сертификата, как корневого, так и сертификата для сервиса. Но у нас ситуация немного другая, в нашем распоряжении есть:
А так как по дефолту JIRA предлагает использовать т.н. блокирующий коннектор (blocking Java connector), который не понимает x509 сертификаты, а использует свой формат - JKS, то нам придется преобразовать наши сертификаты и сохранить их в JKS хранилище. Собственно NIO (non blocking Java connector) тоже требует JKS.
Итак, для начала объединяем все наши сертификаты. Хочу обратить ваше внимание, что порядок имеет значение! Если у вас есть корневой и промежуточные сертификаты вашего центра сертификации, то последовательность должна быть следующей:
root -> intermediate(s) -> domain
# cat rootca.crt > chain.pem # cat sub.class1.server.ca.crt >> chain.pem # cat jira.example.net.crt >> chain.pem
Проверяем нашу цепочку
# openssl verify chain.pem chain.pem: OK
Если все нормально, то преобразовываем наши сертификаты и закрытый ключ в формат pkcs#12
# openssl pkcs12 -export -in chain.pem -inkey jira.example.net.key > jira.example.net.p12 Enter Export Password: ********** Verifying - Enter Export Password: **********
А теперь импортируем нашу цепочку и закрытый ключ и на их основе создаем свой keystore
# keytool -importkeystore -srckeystore jira.example.net.p12 -destkeystore jira.example.net.jks -srcstoretype pkcs12 Enter destination keystore password: ********** Re-enter new password: ********** Enter source keystore password: ********** Entry for alias 1 successfully imported. ********** Import command completed: 1 entries successfully imported, 0 entries failed or cancelled
Проверяем полученное хранилище
$ keytool -list -keystore jira.example.net.jks Enter keystore password: Keystore type: JKS Keystore provider: SUN Your keystore contains 1 entry 1, Jun 24, 2015, PrivateKeyEntry, Certificate fingerprint (SHA1): F2:CE:0D:6F:D7:71:DF:57:41:96:A9:48:EB:CA:6C:94:C9:C5:4E:F0
Теперь у нас все готово к настройке SSL. Для этого редактируем JIRA-INSTALL-DIR/conf/server.xml, в нем есть секция отвечающая за SSL, по умолчанию она закоментирована.
Все действия в данной статье производятся с учетом следующего
JIRA-HOME=/opt/projects/alpha/application-data JIRA-INSTALL-DIR=/opt/projects/alpha/jira
<Connector protocol="org.apache.coyote.http11.Http11Protocol" SSLEnabled="true" port="443" scheme="https" secure="true" useBodyEncodingForURI="true" maxHttpHeaderSize="8192" maxThreads="150" minSpareThreads="25" enableLookups="false" disableUploadTimeout="true" acceptCount="100" clientAuth="false" sslProtocol="TLS" keystoreFile="/etc/pki/jira/jira.example.net.jks" keystorePass="7654321" keystoreType="JKS" />
Запускаем JIRA и смотрим логи: JIRA-INSTALL-DIR/logs/catalina.out
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=384m; support was removed in 8.0 Jul 02, 2015 10:20:02 AM org.apache.catalina.core.AprLifecycleListener init INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: /usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib Jul 02, 2015 10:20:03 AM org.apache.coyote.AbstractProtocol init INFO: Initializing ProtocolHandler ["http-bio-443"] Jul 02, 2015 10:20:03 AM org.apache.catalina.startup.Catalina load INFO: Initialization processed in 1619 ms Jul 02, 2015 10:20:03 AM org.apache.catalina.core.StandardService startInternal INFO: Starting service Catalina Jul 02, 2015 10:20:03 AM org.apache.catalina.core.StandardEngine startInternal INFO: Starting Servlet Engine: Apache Tomcat/7.0.55 2015-07-02 10:20:17,902 localhost-startStop-1 INFO [atlassian.jira.startup.JiraStartupLogger] **************** JIRA starting... **************** ... ... ... Jul 02, 2015 10:21:42 AM org.apache.coyote.AbstractProtocol start INFO: Starting ProtocolHandler ["http-bio-443"] Jul 02, 2015 10:21:42 AM org.apache.catalina.startup.Catalina start INFO: Server startup in 98546 ms 2015-07-02 10:21:45,345 Modification Check:thread-1 INFO [atlassian.jira.startup.JiraStartupLogger] ___ Modifications ___________________________ Modified Files : jira-application.properties, WEB-INF/web.xml Removed Files : None log4j:WARN No appenders could be found for logger (com.amazonaws.jmx.spi.SdkMBeanRegistry). log4j:WARN Please initialize the log4j system properly. log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
Как видим Tomcat у нас запустился, так что производим проверку на SSLLABS
Не очень утешительная оценка. Отправляемся к чтению документации и после осмысления добавляем следующие строки
<Connector protocol="org.apache.coyote.http11.Http11Protocol" SSLEnabled="true" port="443" scheme="https" secure="true" useBodyEncodingForURI="true" maxHttpHeaderSize="8192" maxThreads="150" minSpareThreads="25" enableLookups="false" disableUploadTimeout="true" acceptCount="100" clientAuth="false" sslProtocol="TLS" keystoreFile="/etc/pki/jira/jira.example.net.jks" keystorePass="7654321" keystoreType="JKS" sslEnabledProtocols="TLSv1,TLSv1.1,TLSv1.2" сiphers="TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA" />
Перезапускаем JIRA и снова производим тестирование
После этого можно идти и пить кофе. Единственный нюанс - BIO коннектор поддерживает Secure Client-Initiated Renegotiation, что может быть не безопасным, поэтому лучше использовать неблокирующий коннектор NIO. Для этого достаточно заменить значение в параметре protocol на новое
<Connector protocol="org.apache.coyote.http11.Http11NioProtocol"
Так же хочу заметить, что значение 90 в Cipher Strength удалось получить благодаря использованию Java Cryptography Extension (JCE), данное расширение можно бесплатно скачать с сайта oracle. Все что нужно сделать, это скопировать два jar файла (local_policy.jar/US_export_policy.jar) из архива в папку JIRA-INSTALL-DIR/jre/lib/security. Без JCE Cipher Strength получается 80.
Если же хочется достичь полной нирваны и просветления, т.е. получить Cipher Strength 100, то вы можете поиграться со значением параметра сiphers. Но это я оставляю в виде домашнего задания.
Если внимательно посмотреть на логи запуска JIRA, то в самом начале можно заметить следующие строки
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=384m; support was removed in 8.0 Jul 02, 2015 10:20:02 AM org.apache.catalina.core.AprLifecycleListener init INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: /usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
Собственно обращаем внимание на строку - «The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found». Здесь речь идет о данной библиотеке. APR коннектор так же как и NIO является не блокирующим, а для SSL/TLS функционала используется openssl. Так же есть поддержка FIPS 140-2.
Ниже привожу содержимое server.xml для запуска APR
<Connector port="443" protocol="org.apache.coyote.http11.Http11AprProtocol" scheme="https" secure="true" clientAuth="false" useBodyEncodingForURI="true" SSLEnabled="true" SSLHonorCipherOrder="true" SSLDisableCompression="true" SSLCertificateFile="/etc/pki/jira/jira.example.net.crt" SSLCertificateKeyFile="/etc/pki/jira/jira.example.net.key" SSLCACertificateFile="/etc/pki/jira/ca.pem" SSLCertificateChainFile="/etc/pki/jira/sub.class1.server.ca.pem" SSLCipherSuite="kEECDH+AES128:kEECDH:kEDH:-3DES:kRSA+AES128:kEDH+3DES:DES-CBC3-SHA:!RC4:!aNULL:!eNULL:!MD5:!EXPORT:!LOW:!SEED:!CAMELLIA:!IDEA:!PSK:!SRP:!SSLv2" SSLProtocol="TLSv1+TLSv1.1+TLSv1.2" />
Те, кто настраивал SSL в apache сразу узнает знакомые названия параметров.
В составе JIRA уже идут исходники tomcat native, их можно найти в JIRA-INSTALL-DIR/bin/tomcat-native.tar.gz. Но к сожалению она не подойдет нам, так как согласно changelog поддержка TLSv1.2 и TLSv1.1 появилась только в 1.1.33. Иначе при попытке запуска вы будете получать подобные ошибки
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=384m; support was removed in 8.0 Jul 03, 2015 5:28:47 AM org.apache.catalina.core.AprLifecycleListener init INFO: Loaded APR based Apache Tomcat Native library 1.1.31 using APR version 1.3.9. Jul 03, 2015 5:28:47 AM org.apache.catalina.core.AprLifecycleListener init INFO: APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true]. Jul 03, 2015 5:28:48 AM org.apache.catalina.core.AprLifecycleListener initializeSSL INFO: OpenSSL successfully initialized (OpenSSL 1.0.1e 11 Feb 2013) ... ... ... SEVERE: Failed to initialize end point associated with ProtocolHandler ["http-apr-443"] java.lang.Exception: An invalid value [TLSv1+TLSv1.1+TLSv1.2] was provided for the SSLProtocol attribute at org.apache.tomcat.util.net.AprEndpoint.bind(AprEndpoint.java:490) ... ... ... Jul 03, 2015 5:28:48 AM org.apache.catalina.core.StandardService initInternal SEVERE: Failed to initialize connector [Connector[HTTP/1.1-443]] org.apache.catalina.LifecycleException: Failed to initialize component [Connector[HTTP/1.1-443]]
А забегая немного наперед сразу скажу, что и версия 1.1.33 нас не спасет, точнее она не позволит избежать Logjam уязвимости. Поэтому нам надо использовать 1.1.34, но на момент написания статьи, а это начало июля, эта версия доступна только в транке.
Скачиваем исходники
# cd /root/rpmbuild/BUILD/ # svn co https://svn.apache.org/repos/asf/tomcat/native/branches/1.1.x/ tcnative-1.1.34 A tcnative-1.1.34/jnirelease.sh A tcnative-1.1.34/KEYS A tcnative-1.1.34/README.txt A tcnative-1.1.34/build.properties.default A tcnative-1.1.34/build.xml ... ... ... A tcnative-1.1.34/java/org/apache/tomcat/jni/Stdlib.java A tcnative-1.1.34/java/org/apache/tomcat/jni/SSLExt.java A tcnative-1.1.34/java/org/apache/tomcat/jni/Status.java Checked out external at revision 1659384. Checked out revision 1688981.
Но так как это trunk, то там отсутствует файл configure и нам придется его сгенерировать, но для этого понадобятся исходники самой apr
# rpm -ivh http://vault.centos.org/6.6/os/Source/SPackages/apr-1.3.9-5.el6_2.src.rpm # cd /root/rpmbuild/SPECS/ # rpmbuild -bp --target=x86_64 apr.spec Building target platforms: x86_64 Building for target x86_64 Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.tThAuX + umask 022 + cd /root/rpmbuild/BUILD + LANG=C + export LANG ... ... ... + echo 'Patch #11 (apr-1.2.7-fnmatch.patch):' Patch #11 (apr-1.2.7-fnmatch.patch): + /usr/bin/patch -p1 -b --suffix .fnmatch --fuzz=0 + /bin/cat /root/rpmbuild/SOURCES/apr-1.2.7-fnmatch.patch patching file strings/apr_fnmatch.c + exit 0
Теперь у нас все готово для сборки самой библиотеки
# cd /root/rpmbuild/BUILD/tcnative-1.1.34/native/ # sh buildconf --with-apr=/root/rpmbuild/BUILD/apr-1.3.9 Looking for apr source in /root/rpmbuild/BUILD/apr-1.3.9 Creating configure ... Generating 'make' outputs ... rebuilding rpm spec file # ./configure --with-ssl --with-apr=/usr/bin/apr-1-config --with-java-home=/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.79.x86_64 checking build system type... x86_64-unknown-linux-gnu checking host system type... x86_64-unknown-linux-gnu checking target system type... x86_64-unknown-linux-gnu checking for a BSD-compatible install... /usr/bin/install -c checking for working mkdir -p... yes Tomcat Native Version: 1.1.34 checking for chosen layout... tcnative checking for APR... yes configure: APR 1.3.9 detected. ... ... ... checking OpenSSL library version >= 0.9.8m... ok checking for OpenSSL DSA support... yes setting TCNATIVE_LDFLAGS to "-lssl -lcrypto" adding "-DHAVE_OPENSSL" to CFLAGS setting TCNATIVE_LIBS to "" setting TCNATIVE_LIBS to " /usr/lib64/libapr-1.la -lpthread" checking for apr_pool_pre_cleanup_register in -lapr-1... yes adding "-DHAVE_POOL_PRE_CLEANUP" to CFLAGS configure: creating ./config.status config.status: creating tcnative.pc config.status: creating Makefile config.status: executing default commands # make
В результате в папке .libs должны появится следующие файлы
# ls -la total 2512 drwxr-xr-x. 2 root root 4096 Jul 3 06:12 . drwxr-xr-x. 9 root root 4096 Jul 3 06:12 .. -rw-r--r--. 1 root root 1631390 Jul 3 06:12 libtcnative-1.a lrwxrwxrwx. 1 root root 19 Jul 3 06:12 libtcnative-1.la -> ../libtcnative-1.la -rw-r--r--. 1 root root 1025 Jul 3 06:12 libtcnative-1.lai lrwxrwxrwx. 1 root root 23 Jul 3 06:12 libtcnative-1.so -> libtcnative-1.so.0.1.34 lrwxrwxrwx. 1 root root 23 Jul 3 06:12 libtcnative-1.so.0 -> libtcnative-1.so.0.1.34 -rwxr-xr-x. 1 root root 921921 Jul 3 06:12 libtcnative-1.so.0.1.34
Нас интересуют so модули. Копируем их в папку /usr/lib64 и перезапускаем JIRA. И снова получаем туже ошибку! А всему виной вот это баг, который был исправлен в tomcat-7.0.57, а в последней Jira, а именно 6.4.7 идет 7.0.55.
Скачиваем apache-tomcat-7.0.57 и копируем содержимое папки lib в JIRA-INSTALL-DIR/lib с заменой. Запускаем JIRA и смотрим логи
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=384m; support was removed in 8.0 Jul 03, 2015 6:43:47 AM org.apache.catalina.core.AprLifecycleListener lifecycleEvent INFO: Loaded APR based Apache Tomcat Native library 1.1.34 using APR version 1.3.9. Jul 03, 2015 6:43:47 AM org.apache.catalina.core.AprLifecycleListener lifecycleEvent INFO: APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true]. Jul 03, 2015 6:43:47 AM org.apache.catalina.core.AprLifecycleListener initializeSSL INFO: OpenSSL successfully initialized (OpenSSL 1.0.1e 11 Feb 2013) Jul 03, 2015 6:43:47 AM org.apache.coyote.AbstractProtocol init INFO: Initializing ProtocolHandler ["http-apr-443"] Jul 03, 2015 6:43:47 AM org.apache.catalina.startup.Catalina load INFO: Initialization processed in 1051 ms Jul 03, 2015 6:43:47 AM org.apache.catalina.core.StandardService startInternal INFO: Starting service Catalina Jul 03, 2015 6:43:47 AM org.apache.catalina.core.StandardEngine startInternal INFO: Starting Servlet Engine: Apache Tomcat/7.0.57 2015-07-03 06:43:59,014 localhost-startStop-1 INFO [atlassian.jira.startup.JiraStartupLogger] **************** JIRA starting... ****************
Обращаем внимание на версию APR и Tomcat. Запускаем тест на SSLLABS
Теперь уж точно все и можно смело идти отдыхать.
P.S. Я создал соответствующий запрос на предмет обновления версии tomcat в составе JIRA хотя бы до 7.0.57.
Полезные ссылки