Featured image of post 应用配置https的实践

应用配置https的实践

前言

这个方案的很多核心问题都是在公司同事的帮助下解决的,本文特别予以感谢。本文是通过自签名证书对GeoSmarter5.2系统进行https改造,为了使得改造成本变低本文采用把tomcat使用https + nginx使用https的方式(如果仅修改nginx的话,会需要修改程序jsp获取scheme的逻辑,否则将获取不到css和js)。

nginx:

使用我的脚本自签名证书给nginx签发一对tls证书,脚本如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/bin/sh

HOST=$1
PASSWORD="123@Abc.com"
SUBJECT="/C=CN/ST=Hubei/L=Wuhan/O=GeoStar/CN=$HOST"

# create self-signed server certificate:

echo "Create server key..."

openssl genrsa -passout pass:$PASSWORD -des3 -out $HOST.key 4096

echo "Create server certificate signing request..."
openssl req -passin pass:$PASSWORD -new -subj $SUBJECT -key $HOST.key -out $HOST.csr

echo "Remove password..."

mv $HOST.key $HOST.origin.key
openssl rsa -passin pass:$PASSWORD -in $HOST.origin.key -out $HOST.key

echo "Sign SSL certificate..."
openssl x509 -req -passin pass:$PASSWORD -days 3650 -in $HOST.csr -signkey $HOST.key -out $HOST.crt

echo "Example TODO:"
echo "Copy $HOST.crt to /etc/nginx/ssl/$HOST.crt"
echo "Copy $HOST.key to /etc/nginx/ssl/$HOST.key"
echo "Add configuration in nginx:"
echo "server {"
echo "    ..."
echo "    listen 443 ssl;"
echo "    ssl_certificate     /etc/nginx/ssl/$HOST.crt;"
echo "    ssl_certificate_key /etc/nginx/ssl/$HOST.key;"
echo "}"

执行脚本的时候,脚本的第一个参数是nginx的ip,这个很关键,因为程序在调用https的接口的时候回校验证书,如果你的host跟证书的CommonName不匹配的话就会报错:

1
2
3
4
5
javax.net.ssl.SSLPeerUnverifiedException: Certificate for <xxx> doesn't match common name of the certificate subject: xx.xxx.xx.xx
	at org.apache.http.conn.ssl.DefaultHostnameVerifier.matchCN(DefaultHostnameVerifier.java:186)
	at org.apache.http.conn.ssl.DefaultHostnameVerifier.verify(DefaultHostnameVerifier.java:133)
	at org.apache.http.conn.ssl.DefaultHostnameVerifier.verify(DefaultHostnameVerifier.java:99)
	at org.apache.http.conn.ssl.SSLConnectionSocketFactory.verifyHostname(SSLConnectionSocketFactory.java:463)

生成证书以后,拷贝证书到应用代理的nginx容器,把证书配置到应用代理的nginx配置中:

1
2
3
4
5
6
server{
    listen 9010 ssl;
    ssl_certificate     <crt公钥证书>;
    ssl_certificate_key <key私钥证书>;
    error_page 497 307 https://$host:$server_port$request_uri;
}

然后修改location下的proxy_pass地址为https协议,一般cas我们是不需要修改的,修改其他应用即可。

其他应用(tomcat下):

①屏蔽系统配置初始化的选项,这个配置在/srv/tomcat8/bin/catalina.sh,打开脚本后一般在第一或者第二页能够找到形如:

1
java -jar /opt/jar/update-bigdatacenter-config.jar ***

在这一句前面添加一个#号,注释掉。

②给tomcat生成一个keystore证书:

1
2
$ mkdir /ssl
$ keytool -genkey  -alias tomcat  -keyalg RSA  -storetype pkcs12  -validity 7300  -keystore /ssl/server.keystore  -keysize 2048  -keypass DwpPAd23xzvZTn09RmMuZKe3T4FeXfhM  -storepass DwpPAd23xzvZTn09RmMuZKe3T4FeXfhM  -dname "CN=GeoStar, OU=GeoStar, O=GeoStar, L=Wuhan, S=Hubei, C=China"

③配置证书到8080端口,有人会疑问为啥不直接用8443端口,很简单,为了不用改动容器的端口映射:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<!-- # 屏蔽掉原来8080的connector
    <Connector port="8080" URIEncoding="UTF-8" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" /> -->
<!-- 新增如下connector -->
    <Connector protocol="org.apache.coyote.http11.Http11NioProtocol" 
               port="8080" maxThreads="200" URIEncoding="UTF-8" 
               scheme="https" secure="true" SSLEnabled="true" 
               keystoreFile="/ssl/server.keystore" 
               keystorePass="DwpPAd23xzvZTn09RmMuZKe3T4FeXfhM" 
               clientAuth="false" sslProtocol="TLS"/>

④拷贝nginx的证书到每个应用的容器中,使用keytools工具把nginx证书添加为jdk的白名单:

1
2
3
4
# 假设你nginx的ip为10.0.0.1,并且证书被拷贝到了容器的/root目录,$JAVA_HOME已经到了jre的目录(如果没到的话需要增加一级jre路径)
$ keytool -keystore $JAVA_HOME/lib/security/cacerts -import -alias 10.0.0.1 -file /root/10.0.0.1.crt
# 输入默认密码,这里只需要输入一次密码,如果你需要输入两次说明你的路径不对
$ changeit

⑤修改应用的配置文件:

一般应用的配置文件都在/srv/tomcat8/webapps/<应用名>/WEB-INF/classes或者/srv/tomcat8/webapps/<应用名>/WEB-INF/classes/config,将应用间调用的地址改成https。

⑥cas_client会校验证书的合法性,由于是我们自己签发的证书,所以铁定会报错

1
ERROR [https-jsse-nio-8080-exec-8] org.jasig.cas.client.util.CommonUtils.getResponseFromServer - SSL error getting response from host xx.xx.xx.xx: Error Message: No subject alternative names present javax.net.ssl.SSLHandshakeException:No subject alternative names present

拷贝cas_client的补丁(点我下载),到容器里面,解压到/srv/tomcat8/webapps/<应用名>/WEB-INF/classes/com/geostar/gfstack/util目录

1
2
3
$ mkdir -p /srv/tomcat8/webapps/<应用名>/WEB-INF/classes/WEB-INF/classes/com/geostar/gfstack/util
$ mv /root/com/geostar/gfstack/util/TrustCASServerSSLListener.class /srv/tomcat8/webapps/<应用名>/WEB-INF/classes/WEB-INF/classes/com/geostar/gfstack/util/
$ mv /root/com/geostar/gfstack/util/TrustCASServerSSLListener\$TrustAllManager.class /srv/tomcat8/webapps/<应用名>/WEB-INF/classes/WEB-INF/classes/com/geostar/gfstack/util/

然后配置/srv/tomcat8/webapps/<应用名>/WEB-INF/web.xml,增加监听这个类:

1
2
3
	<listener>
		<listener-class>com.geostar.gfstack.util.TrustCASServerSSLListener</listener-class>
	</listener>

⑦重启应用