apache多SSL證書虛擬主機使用mod_jk方式代理tomcat

1.問題背景

   公司舊版的客戶端產品之前使用了早期的證書(其簽名算法是SHA1),只能與帶有SHA1證書的tomcat服務器通信。
SHA1的證書今年年底都要進行吊銷,也就是說年底需要重新簽發成sha256證書。

Google關於淘汰SHA1的資料

https://security.googleblog.com/2015/12/an-update-on-sha-1-certificates-in.html

CA/Browser Forum關於通過淘汰SHA1的資料

https://cabforum.org/2014/10/16/ballot-118-sha-1-sunset/

同時,新版客戶端產品能夠支持sha256證書,也能支持sha1證書。因此,爲了兼容性不同版本的客戶端產品對證書籤名算法的支持,需要在tomcat服務器端配置多SSL證書機制。


2.技術知識背景

“早期的SSLv2根據經典的公鑰基礎設施PKI(Public Key Infrastructure)設計,它默認認爲:一臺服務器(或者說一個IP)只會提供一個服務,所以在SSL握手時,服務器端可以確信客戶端申請的是哪張證書。
但是讓人萬萬沒有想到的是,虛擬主機大力發展起來了,這就造成了一個IP會對應多個域名的情況。解決辦法有一些,例如申請泛域名證書,對所有*.yourdomain.com的域名都可以認證,但如果你還有一個yourdomain.net的域名,那就不行了。
在HTTP協議中,請求的域名作爲主機頭(Host)放在HTTP Header中,所以服務器端知道應該把請求引向哪個域名,但是早期的SSL做不到這一點,因爲在SSL握手的過程中,根本不會有Host的信息,所以服務器端通常返回的是配置中的第一個可用證書。因而一些較老的環境,可能會產生多域名分別配好了證書,但返回的始終是同一個。
既然問題的原因是在SSL握手時缺少主機頭信息,那麼補上就是了。
SNI(Server Name Indication)定義在RFC 4366,是一項用於改善SSL/TLS的技術,在SSLv3/TLSv1中被啓用。它允許客戶端在發起SSL握手請求時(具體說來,是客戶端發出SSL請求中的ClientHello階段),就提交請求的Host信息,使得服務器能夠切換到正確的域並返回相應的證書。
[warning]要使用SNI,需要客戶端和服務器端同時滿足條件,幸好對於現代瀏覽器來說,大部分都支持SSLv3/TLSv1,所以都可以享受SNI帶來的便利。”--參考自《SNI: 實現多域名虛擬主機的SSL/TLS認證
更多的參考資料:

找了很多資料,並沒有很好的對tomca的多證書支持的配置資料,而apahce對多證書的支持的資料較多,從後續的維護權衡,採用apahce的代理tomcat方式來配置web服務器的多證書。apache對SNI的支持能夠採用mod_ssl和mod_gnutls來支持,mod_gnutls需要安裝gnutls模塊,安裝步驟十分繁瑣又容易出錯,故不採用(本人安裝過,繞了一大圈一大堆的依賴,最後不得不放棄。)。網上拷貝的文章有些是過時的,採用的是gnutls,其實mod_ssl早已支持SNI機制,因爲它依賴於openssl,而openssl在0.9.8f 版就已經支持SNI了,同時注意Apache的版本 2.2.12 and later才能實現SNI。

3.問題處理

3.1  軟件環境

apache2.2.31   tomcat-7.0.64   mod_ssl(apache2.2.31編譯自帶的版本)   mod_jk  linux centos6操作系統

3.2  軟件安裝

先來安裝apache2.2.31
下載Apache安裝包apache2.2.31,下載地址:http://httpd.apache.org/
編譯命令:
./configure --prefix=/usr/local/apache2 --with-apr=/usr/local/apr --with-apr-util=/usr/local/apr-util/ 
(除了指定Apache的安裝目錄外,還要安裝apr、apr-util並指定參數)
make
make install
在編譯Apache時分別出現了apr not found、APR-util not found  not found的問題,下面就這些問題解決來實際操作一把。
http://apr.apache.org/download.cgi  下載apr-1.4.5.tar.gz、apr-util-1.3.12.tar.gz
1.解決apr not found問題
  [root@localhost bin]# tar -zxf apr-1.4.5.tar.gz
  [root@localhost apr-1.4.5]# ./configure --prefix=/usr/local/apr
  [root@localhost apr-1.4.5]# make
  [root@localhost apr-1.4.5]# make install
2.解決APR-util not found問題
  [root@localhost bin]# tar -zxf apr-util-1.3.12.tar.gz
  [root@localhost apr-util-1.3.12]# ./configure --prefix=/usr/local/apr-util -with-apr=/usr/local/apr/bin/apr-1-config
  [root@localhost apr-util-1.3.12]# make
  [root@localhost apr-util-1.3.12]# make install

安裝好了apache後再來看下在apache上安裝mod_ssl模塊(可以在安裝apahce時就順帶安裝mod_ssl,在configure 命令追加 --enable-mods-shared=all --enable-ssl,在此主要說明如何靈活地安裝apahce的模塊)
1. 首先定位到Apache源碼的 proxy目錄
# cd /root/Desktop/httpd-2.2.31
# cd modules/proxy/
2. 編譯相應模塊:
其中 "/usr/local/apache2" 爲之前Apache的安裝目錄
# /usr/local/apache2/bin/apxs -c -i mod_proxy.c proxy_util.c
加載模塊:
# /usr/local/apache2/bin/apxs -i -a -n proxy mod_proxy.la
這樣,就將proxy安裝成功了,你可以到httpd.conf中看到自動添加了如下語句:
LoadModule ssl_module        modules/mod_ssl.so
modules 文件夾中也生成了相應的 mod_ssl.so 模塊,要安裝其他模塊也是同樣的流程。
至於mod_jk如何安裝和配置知識可參考《Tomcat系列之Apache使用mod_proxy和mod_jk反向代理Tomcat

3.3  配置

apache httpd.conf文件配置:
ServerRoot "/usr/local/apache2"

LoadModule ssl_module modules/mod_ssl.so

<IfModule !mpm_netware_module>
<IfModule !mpm_winnt_module>
#
# If you wish httpd to run as a different user or group, you must run
# httpd as root initially and it will switch.  
#
# User/Group: The name (or #number) of the user/group to run httpd as.
# It is usually good practice to create a dedicated user and group for
# running httpd, as with most system services.
#
User daemon
Group daemon

</IfModule>
</IfModule>


ServerAdmin [email protected]

<Directory />
    Options FollowSymLinks
    AllowOverride None
    Order deny,allow
    Deny from all
</Directory>

<Directory "/usr/local/apache2/htdocs">
    Options Indexes FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all
</Directory>

#
# DirectoryIndex: sets the file that Apache will serve if a directory
# is requested.
#
<IfModule dir_module>
    DirectoryIndex index.html
</IfModule>

#
# The following lines prevent .htaccess and .htpasswd files from being 
# viewed by Web clients. 
#
<FilesMatch "^\.ht">
    Order allow,deny
    Deny from all
    Satisfy All
</FilesMatch>

ErrorLog "logs/error_log"

LogLevel warn

<IfModule log_config_module>
    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
    LogFormat "%h %l %u %t \"%r\" %>s %b" common

    <IfModule logio_module>
      # You need to enable mod_logio.c to use %I and %O
      LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
    </IfModule>

    CustomLog "logs/access_log" common
</IfModule>

<IfModule alias_module>
    ScriptAlias /cgi-bin/ "/usr/local/apache2/cgi-bin/"
</IfModule>

<IfModule cgid_module>

</IfModule>

<Directory "/usr/local/apache2/cgi-bin">
    AllowOverride None
    Options None
    Order allow,deny
    Allow from all
</Directory>


DefaultType text/plain

<IfModule mime_module>
    #
    # TypesConfig points to the file containing the list of mappings from
    # filename extension to MIME-type.
    #
    TypesConfig conf/mime.types

    #
    # AddType allows you to add to or override the MIME configuration
    # file specified in TypesConfig for specific file types.
    #
    #AddType application/x-gzip .tgz
    #
    # AddEncoding allows you to have certain browsers uncompress
    # information on the fly. Note: Not all browsers support this.
    #
    #AddEncoding x-compress .Z
    #AddEncoding x-gzip .gz .tgz
    #
    # If the AddEncoding directives above are commented-out, then you
    # probably should define those extensions to indicate media types:
    #
    AddType application/x-compress .Z
    AddType application/x-gzip .gz .tgz

    #
    # AddHandler allows you to map certain file extensions to "handlers":
    # actions unrelated to filetype. These can be either built into the server
    # or added with the Action directive (see below)
    #
    # To use CGI scripts outside of ScriptAliased directories:
    # (You will also need to add "ExecCGI" to the "Options" directive.)
    #
    #AddHandler cgi-script .cgi

    # For type maps (negotiated resources):
    #AddHandler type-map var

    #
    # Filters allow you to process content before it is sent to the client.
    #
    # To parse .shtml files for server-side includes (SSI):
    # (You will also need to add "Includes" to the "Options" directive.)
    #
    #AddType text/html .shtml
    #AddOutputFilter INCLUDES .shtml
</IfModule>
# Server-pool management (MPM specific)
#Include conf/extra/httpd-mpm.conf

# Multi-language error messages
#Include conf/extra/httpd-multilang-errordoc.conf

# Fancy directory listings
#Include conf/extra/httpd-autoindex.conf

# Language settings
#Include conf/extra/httpd-languages.conf

# User home directories
#Include conf/extra/httpd-userdir.conf

# Real-time info on requests and configuration
#Include conf/extra/httpd-info.conf

# Virtual hosts
#Include conf/extra/httpd-vhosts.conf

# Local access to the Apache HTTP Server Manual
#Include conf/extra/httpd-manual.conf

# Distributed authoring and versioning (WebDAV)
#Include conf/extra/httpd-dav.conf

# Various default settings
#Include conf/extra/httpd-default.conf

# Secure (SSL/TLS) connections
Include conf/extra/httpd-ssl.conf

<IfModule ssl_module>
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
</IfModule>

# Load the mod_jk
LoadModule jk_module modules/mod_jk.so
JkWorkersFile /usr/local/apache2/conf/extra/workers.properties
JkLogFile logs/mod_jk.log
JkLogLevel debug


<Directory "/usr/local/apache-tomcat-7.0.64/webapps/rs">  
    Options Indexes FollowSymLinks  
    AllowOverride None  
    Order allow,deny  
    Allow from all  
</Directory> 



mod_jk的workers.properties文件配置:
#讓mod_jk模塊知道tomcat的位置 
workers.tomcat_home=/usr/local/apache-tomcat-7.0.64

worker.list=TomcatA
worker.TomcatA.port=8009
worker.TomcatA.host=localhost
worker.TomcatA.type=ajp13
worker.TomcatA.lbfactor=1

httpd-ssl.conf文件配置:
NameVirtualHost *:443
Listen 443

AddType application/x-x509-ca-cert .crt
AddType application/x-pkcs7-crl    .crl

SSLCipherSuite HIGH:MEDIUM:!MD5:!RC4
SSLProxyCipherSuite HIGH:MEDIUM:!MD5:!RC4


SSLHonorCipherOrder on 


SSLProtocol all -SSLv2 -SSLv3
SSLProxyProtocol all -SSLv2 -SSLv3


SSLPassPhraseDialog  builtin
SSLSessionCache        "shmcb:/usr/local/apache2/logs/ssl_scache(512000)"
SSLSessionCacheTimeout  300

#   Semaphore:
#   Configure the path to the mutual exclusion semaphore the
#   SSL engine uses internally for inter-process synchronization. 
SSLMutex  "file:/usr/local/apache2/logs/ssl_mutex"

<Directory "/usr/local/apache-tomcat-7.0.64/webapps/ROOT">  
    Options Indexes FollowSymLinks  
    AllowOverride None  
    Order allow,deny  
    Allow from all  
</Directory> 

<VirtualHost _default_:443>
DocumentRoot "/usr/local/apache-tomcat-7.0.64/webapps/rs"
ServerName ******************:443
ServerAdmin [email protected]
ErrorLog "/usr/local/apache2/logs/old_error_log"
TransferLog "/usr/local/apache2/logs/old_access_log"

JkMount /* TomcatA

SSLEngine on
SSLCertificateFile "/usr/local/apache-ssl-files/old/rps.crt"
SSLCertificateKeyFile "/usr/local/apache-ssl-files/old/rps.key"
SSLCertificateChainFile "/usr/local/apache-ssl-files/old/rps_ca2.cer"

<FilesMatch "\.(cgi|shtml|phtml|php)$">
    SSLOptions +StdEnvVars
</FilesMatch>
<Directory "/usr/local/apache2/cgi-bin">
    SSLOptions +StdEnvVars
</Directory>


BrowserMatch "MSIE [2-5]" \
         nokeepalive ssl-unclean-shutdown \
         downgrade-1.0 force-response-1.0

CustomLog "/usr/local/apache2/logs/ssl_request_log" \
          "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
</VirtualHost>


<VirtualHost *:443>
DocumentRoot "/usr/local/apache-tomcat-7.0.64/webapps/rs"
#DocumentRoot "/usr/local/apache2/htdocs"
DirectoryIndex index.html index.jsp
ServerName rps.test.com:443
ServerAdmin [email protected]
ErrorLog "/usr/local/apache2/logs/new_error_log"
TransferLog "/usr/local/apache2/logs/new_access_log"

JkMount /* TomcatA

SSLEngine on

SSLCertificateFile "/usr/local/apache-ssl-files/new/rps.cer"
SSLCertificateKeyFile "/usr/local/apache-ssl-files/new/rps.key"
SSLCertificateChainFile "/usr/local/apache-ssl-files/new/root.cer"

<FilesMatch "\.(cgi|shtml|phtml|php)$">
    SSLOptions +StdEnvVars
</FilesMatch>
<Directory "/usr/local/apache2/cgi-bin">
    SSLOptions +StdEnvVars
</Directory>

BrowserMatch "MSIE [2-5]" \
         nokeepalive ssl-unclean-shutdown \
         downgrade-1.0 force-response-1.0

CustomLog "/usr/local/apache2/logs/ssl_request_log" \
          "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
</VirtualHost> 
注意:默認的虛擬主機的配置  ServerName ******************:443 是採用通配符方式,有請求過來時先去匹配顯示指定域名的主機,直到找不到,最後會匹配通配符的主機,apache通配符的主機會對應tomcat的localhost的主機配置,所以DocumentRoot的路徑配置要和tomcat的localhost的路徑配置一致。

tomcat的server.xml配置:
<?xml version='1.0' encoding='utf-8'?>


<Server port="8005" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  <!-- Security listener. Documentation at /docs/config/listeners.html
  <Listener className="org.apache.catalina.security.SecurityListener" />
  -->
  <!--APR library loader. Documentation at /docs/apr.html -->
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html -->
  <Listener className="org.apache.catalina.core.JasperListener" />
  <!-- Prevent memory leaks due to use of particular java/javax APIs-->
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

  <!-- Global JNDI resources
       Documentation at /docs/jndi-resources-howto.html
  -->
  <GlobalNamingResources>
    <!-- Editable user database that can also be used by
         UserDatabaseRealm to authenticate users
    -->
    <Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="User database that can be updated and saved"
              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
              pathname="conf/tomcat-users.xml" />
  </GlobalNamingResources>

  <!-- A "Service" is a collection of one or more "Connectors" that share
       a single "Container" Note:  A "Service" is not itself a "Container",
       so you may not define subcomponents such as "Valves" at this level.
       Documentation at /docs/config/service.html
   -->
  <Service name="Catalina">

    <!--The connectors can use a shared executor, you can define one or more named thread pools-->
    <Connector port="8081" protocol="HTTP/1.1"
                URIEncodeing="UTF-8"
                maxThreads="200"
                minSpareThreads="16"
                maxSpareThreads="50"
                acceptCount="1000"
                maxKeepAliveRequests="100"
                bufferSize="8192"
                connectionTimeout="60000"
                useBodyEncodingForURI="true"
                keepAliveTimeout="10000"
                enableLookups="false"
                compression="on"
                compressionMinSize="2048"
                noCompressionUserAgents="gozilla,traviata"
                compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain"
               redirectPort="443" />
    <!-- Define an AJP 1.3 Connector on port 8009 -->
    <Connector port="8009" protocol="AJP/1.3" redirectPort="443" />

    <!-- You should set jvmRoute to support load-balancing via AJP ie :
    <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
    -->
    <Engine name="Catalina" defaultHost="localhost">

      <!--For clustering, please take a look at documentation at:
          /docs/cluster-howto.html  (simple how to)
          /docs/config/cluster.html (reference documentation) -->
      <!--
      <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
      -->

      <!-- Use the LockOutRealm to prevent attempts to guess user passwords
           via a brute-force attack -->
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <!-- This Realm uses the UserDatabase configured in the global JNDI
             resources under the key "UserDatabase".  Any edits
             that are performed against this UserDatabase are immediately
             available for use by the Realm.  -->
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>

      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">

        <!-- SingleSignOn valve, share authentication between web applications
             Documentation at: /docs/config/valve.html -->
        <!--
        <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
        -->

        <!-- Access log processes all example.
             Documentation at: /docs/config/valve.html
             Note: The pattern used is equivalent to using pattern="common" -->
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log." suffix=".txt"
               pattern="%h %l %u %t "%r" %s %b" />
	<Context path="" docBase="/usr/local/apache-tomcat-7.0.64/webapps/rs"/>
      </Host>


      <Host name="rps.est.com" appBase="webapps"
      unpackWARs="true" autoDeploy="true"
      xmlValidation="false" xmlNamespaceAware="false">
        <Context path="" docBase="/usr/local/apache-tomcat-7.0.64/webapps/rs" 
             reloadable="true" crossContext="true"/>
     </Host>

    </Engine>
  </Service>
</Server>

apache採用mod_jk代理tomcat的原理配置具體可參考:apache+tomcat+mod_jk整合配置虛擬主機


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章