一、Tomcat 安全优化
1、Telnet 管理端口保护
使用telnet连接进来可以输入SHUTDOWN可以直接关闭tomcat,极不安全,必须关闭。可以修改默认的管理端口8005改为其他端口,修改SHUTDOWN指令为其他字符串。

 

#  vim /data/project/apache-tomcat1/conf/server.xml
<Server port="8011" shutdown="IN0IT">

 

2. AJP 连接端口保护
Tomcat 服务器通过Connector连接器组件与客户程序建立连接,Connector组件负责接收客户的请求,以及把Tomcat服务器的响应结果发送给客 户。默认情况下,Tomcat在server.xml中配置了两种连接器,一种使用ajp,要和apache结合使用,一种使用http。当使用http 时,可以限制ajp端口访问,在于防止线下测试流量被mod_jk转发至线上tomcat服务器。可以通过iptables规则限制ajp端口的访问,或 者直接将改行注释。

#  vim /data/project/apache-tomcat1/conf/server.xml
<Connector port="8081" protocol="AJP/1.3"
connectionTimeout="20000" redirectPort="8443" />



3.禁用管理端

对于tomcat的web管理端属于高危安全隐患,一旦被攻破,黑客通过上传web shell方式取得服务器的控制权,那是非常可怕的。我们需要删除tomcat安装目录下conf/tomcat-user.xml或者删除webapps下默认的目录和文件。

mv webapps/* /tmp  #在此我们选择将默认目录拷贝到/tmp下

4.降权启动tomcat

tomcat 启动用户权限必须为非root,避免一旦tomcat服务被入侵,获取root权限,普通用户只能使用大于1024端口,如果要想使用80端口,可以使用 iptables规则进行转发,或者使用代理。一般情况下,tomcat前方有一个反向代理服务器nginx或者apache等。

参考:https://tomcat.apache.org/tomcat-8.0-doc/setup.html

[root@Tomcat bin]# cd /data/project/apache-tomcat1/bin/
[root@Tomcat bin]# tar xf commons-daemon-native.tar.gz 
[root@Tomcat bin]# cd commons-daemon-1.0.15-native-src/unix/
[root@Tomcat unix]# ./configure --with-java=/usr/local/jdk1.8.0_60/
[root@Tomcat unix]# make
[root@Tomcat unix]# cp jsvc /data/project/apache-tomcat1/bin/
[root@Tomcat unix]# cd  /data/project/apache-tomcat1/bin/

编辑daemon.sh,首行加入以下内容,表示启动tomcat用户为nginx

 TOMCAT_USER=nginx



启动关闭tomcat

[root@Tomcat bin]# ./daemon.sh start
[root@Tomcat bin]# ./daemon.sh stop
[root@Tomcat bin]# ./daemon.sh version
jsvc (Apache Commons Daemon) 1.0.15-dev
Copyright (c) 1999-2011 Apache Software Foundation.
java version "1.8.0_60"
Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)
commons daemon version "1.0.15-dev"
commons daemon process (id: 7561, parent: 7560)
Server version: Apache Tomcat/8.0.27
Server built:   Sep 28 2015 08:17:25 UTC
Server number:  8.0.27.0
OS Name:        Linux
OS Version:     3.10.0-229.el7.x86_64
Architecture:   amd64
JVM Version:    1.8.0_60-b27
JVM Vendor:     Oracle Corporation
[root@Tomcat bin]# ./daemon.sh run


查看启动状态:
 

5. 文件列表访问控制

web.xml文件中的default部分listings的配置必须为false,false为不列出目录文件,true为允许列出,默认为false。

# vim /data/project/apache-tomcat1/conf/web.xml
<init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>



6.隐藏版本信息

对一些常见错误重定向,避免出错暴露服务器和版本信息。在conf/web.xml重定向403,404及500等错误到指定页面。

<error-page>
    <error-code>404</error-code>
    <location>/404.html</location>
</error-page>
<error-page>
    <error-code>403</error-code>
    <location>/403.html</location>
</error-page>
<error-page>
    <error-code>500</error-code>
    <location>/500.html</location>
</error-page>

7. 脚本权限回收

去除其他用户对bin目录下可执行权限,防止其他用户起停tomcat

chmod 744 /data/project/apache-tomcat1/bin/*

8. 访问日志格式规范

开启Referer和User-Agetn是为了一旦出现安全问题能够更好的根据日志进行排查

# vim /data/project/apache-tomcat1/conf/server.xml
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t &quot;%r&quot; %s %b %{Referer}i %{User-Agent}i %D" />

9. 设置信任IP白名单

# vim /data/project/apache-tomcat1/conf/server.xml
<Valve className="org.apache.catalina.valves.RemoteAddrValve" allow="192.168.1.*"/>



10.屏蔽DNS查询
# vim /data/project/apache-tomcat1/conf/server.xml
    <Connector port="8081" protocol="AJP/1.3"
               connectionTimeout="20000"
               enableLookups="false"        #  enableLookups="false" 代表屏蔽
               acceptCount="800"
               redirectPort="8443" />


 

二、Tomcat 性能优化

1、jvm调优

Tomcat最吃内存,只要内存足够,这只猫就跑的很快。如果系统资源有限,那就需要进行调优,提高资源使用率。

a.优化catalina.sh

在catalina.sh配置文件中添加以下代码:

JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms1024m -Xmx1024m -XX:NewSize=512m -XX:MaxNewSize=512m -XX:PermSize=512m -XX:MaxPermSize=512m"

-server :一定要作为第一个参数,在多个CPU时性能最佳

-Xms:初始堆内存Heap大小,使用的最小内存,cpu性能高时此值应设的大一些

-Xmx:初始堆内存heap最大值,使用的最大内存

上面两个值是分配JVM的最小和最大内存,取决于硬件物理内存的大小,建议均设为物理内存的一半。

-XX:PermSize:设定内存的永久保存区域

-XX:MaxPermSize:设定最大内存的永久保存区域

-XX:MaxNewSize:

-Xss 15120 这使得JBoss每增加一个线程(thread)就会立即消耗15M内存,而最佳值应该是128K,默认值好像是512k.

+XX:AggressiveHeap 会使得 Xms没有意义。这个参数让jvm忽略Xmx参数,疯狂地吃完一个G物理内存,再吃尽一个G的swap。

-Xss:每个线程的Stack大小

-verbose:gc 现实垃圾收集信息

-Xloggc:gc.log 指定垃圾收集日志文件

-Xmn:young generation的heap大小,一般设置为Xmx的3、4分之一

-XX:+UseParNewGC :缩短minor收集的时间

-XX:+UseConcMarkSweepGC :缩短major收集的时间

配置完成后重启tomcat,通过以下命令查看是否配置生效:

查看tomcat进程号

[root@Tomcat conf]#  ps -ef|grep java


查看是否生效:


我们可以看到MaxHeapSize 等参数已经生效。

2. 优化server.xml

Tomcat的主配置文件,该文件中包含很多主要元素,比如Service、Connector、Host等,这些元素都会创建软件”对象”、排序及进程管道中设置的这些元素嵌套方,使我们可以执行过滤、分组等工作。

如果要对该文件做优化,我们需要先了解该文件的结构!

server.xml的结构图:

 

该文件描述了如何启动Tomcat Server
<Server>
  <Listener />
  <GlobaNamingResources>
  </GlobaNamingResources
  <Service>
    <Connector />
    <Engine>
      <Logger />
      <Realm />
         <host>
           <Logger />
           <Context />
         </host>
    </Engine>
  </Service>
</Server>

针对该文件,我们需要优化的点有如下:

1、 maxThreads 连接数限制

maxThreads 是 Tomcat 所能接受最大连接数。一般设置不要超过8000以上,如果你的网站访问量非常大可能使用运行多个Tomcat实例的方法, 即,在一个服务器上启动多个tomcat然后做负载均衡处理。

这里还需要注意的一点是,tomcat 和 php 不同。php可以按照cpu和内存的情况去配置连接数,上万很正常。而 java 还需要注意 jvm 的参数配置。如果不注意就会因为jvm参数过小而崩溃。

2、多虚拟主机

强烈建议不要使用 Tomcat 的虚拟主机,推荐每个站点使用一个实例。即,可以启动多个 Tomcat,而不是启动一个 Tomcat 里面包含多个虚拟主机。因为 Tomcat是多线程,共享内存,任何一个虚拟主机中的应用崩溃,都会影响到所有应用程序。虽然采用多实例的方式会产生过多的开销,但至少保障了应用程序的隔离和安全。

3、压缩传输

tomcat作为一个应用服务器,也是支持 gzip 压缩功能的。我们可以在 server.xml 配置文件中的 Connector 节点中配置如下参数,来实现对指定资源类型进行压缩。

compression="on"             # 打开压缩功能 
compressionMinSize="50"      # 启用压缩的输出内容大小,默认为2KB 
noCompressionUserAgents="gozilla, traviata"      # 对于以下的浏览器,不启用压缩 
compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain" # 哪些资源类型需要压缩

提示:

Tomcat 的压缩是在客户端请求服务器对应资源后,从服务器端将资源文件压缩,再输出到客户端,由客户端的浏览器负责解压缩并浏览。相对于普通的浏览过程 HTML、CSS、Javascript和Text,它可以节省40% 左右的流量。更为重要的是,它可以对动态生成的,包括CGI、PHP、JSP、ASP、Servlet,SHTML等输出的网页也能进行压缩,压缩效率也很高。但是, 压缩会增加 Tomcat 的负担,因此最好采用Nginx + Tomcat 或者 Apache + Tomcat 方式,将压缩的任务交由 Nginx/Apache 去做。

一旦启用了这个压缩功能后,我们怎么来测试压缩是否有效呢?首先Tomcat是根据浏览器请求头中的accept-encoding来判断浏览器是否支持 压缩功能,如果这个值包含有gzip,就表明浏览器支持gzip压缩内容的浏览,所以我们可以用httpclient来写一个这样的简单测试程序

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
 
public class HttpTester {
 
public static void main(String[] args) throws Exception{
HttpClient http = new HttpClient();
GetMethod get = new GetMethod("http://www.dlog.cn/js/prototype.js");
try{
get.addRequestHeader("accept-encoding", "gzip,deflate");
get.addRequestHeader("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; Alexa Toolbar; Maxthon 2.0)");
int er = http.executeMethod(get);
if(er==200){
System.out.println(get.getResponseContentLength());
String html = get.getResponseBodyAsString();
System.out.println(html);
System.out.println(html.getBytes().length);
}
}finally{
get.releaseConnection();
}
}
 
}

执行这个测试程序,看看它所输出的是什么内容,如果输出的是一些乱码,以及打印内容的长度远小于实际的长度,那么恭喜你,你的配置生效了,你会发现你网站的浏览速度比以前快多了。

3.关闭war自动部署

默认 Tomcat 是开启了对war包的热部署的。为了防止被植入木马等恶意程序,因此我们要关闭自动部署。

      <Host name="localhost"   appBase="/data/www/www"
            unpackWARs="false" autoDeploy="true">

 

4. 并发优化

调整连接器connectior的并发处理能力

# vim /data/project/apache-tomcat1/conf/server.xml

参数说明:

maxThreads         #客户请求最大线程数

minSpareThreads    #Tomcat初始化时创建的 socket 线程数

maxSpareThreads    #Tomcat连接器的最大空闲 socket 线程数

minProcessors:    #最小空闲连接线程数,用于提高系统处理性能,默认值为 10

maxProcessors:   #最大连接线程数,即:并发处理的最大请求数,默认值为 75

acceptCount:   #允许的最大连接数,应大于等于 maxProcessors ,默认值为 100

enableLookups:   #是否反查域名,取值为: true 或 false 。为了提高处理能力,应设置为 false

redirectPort       #在需要基于安全通道的场合,把客户请求转发到基于SSL 的 redirectPort 端口

acceptAccount      #监听端口队列最大数,满了之后客户请求会被拒绝(不能小于maxSpareThreads  )

connectionTimeout  #网络连接超时,单位:毫秒。设置为 0 表示永不超时,这样设置有隐患的。通常可设置为30000 毫秒。

URIEncoding       ##URL统一编码

其中和最大连接数相关的参数为maxProcessors 和 acceptCount 。如果要加大并发连接数,应同时加大这两个参数。

web server允许的最大连接数还受制于操作系统的内核参数设置,通常 Windows 是 2000 个左右, Linux 是1000 个左右。

示例:

 <Connector port="8081" protocol="AJP/1.3"
            connectionTimeout="20000"
            maxHttpHeaderSize="8192"  
            maxThreads="1000"  
            minSpareThreads="100"  
            maxSpareThreads="1000"  
            minProcessors="100"  
            maxProcessors="1000"  
            enableLookups="false"  
            URIEncoding="utf-8"  
            acceptCount="1000"  
            redirectPort="8443"  
            disableUploadTimeout="true"/>



4.tomcat缓存优化

# vim /data/project/apache-tomcat1/conf/server.xml

参数说明:

maxThreads:tomcat起动的最大线程数,即同时处理的任务个数,默认值为200

acceptCount:当tomcat起动的线程数达到最大时,接受排队的请求个数,默认值为100

示例:

<Connector port="8081" protocol="AJP/1.3"
           connectionTimeout="20000"
           maxThreads="800"
           acceptCount="1000"/>




这两个值如何起作用,请看下面三种情况

情况1:接受一个请求,此时tomcat起动的线程数没有到达maxThreads,tomcat会起动一个线程来处理此请求。

情况2:接受一个请求,此时tomcat起动的线程数已经到达maxThreads,tomcat会把此请求放入等待队列,等待空闲线程。

情况3:接受一个请求,此时tomcat起动的线程数已经到达maxThreads,等待队列中的请求个数也达到了acceptCount,此时tomcat会直接拒绝此次请求,返回connection refused

maxThreads如何配置

一般的服务器操作都包括量方面:1计算(主要消耗cpu),2等待(io、数据库等)

第一种极端情况,如果我们的操作是纯粹的计算,那么系统响应时间的主要限制就是cpu的运算能力,此时maxThreads应该尽量设的小,降低同一时间内争抢cpu的线程个数,可以提高计算效率,提高系统的整体处理能力。

第二种极端情况,如果我们的操作纯粹是IO或者数据库,那么响应时间的主要限制就变为等待外部资源,此时maxThreads应该尽量设的大,这样才能提高同时处理请求的个数,从而提高系统整体的处理能力。此情况下因为tomcat同时处理的请求量会比较大,所以需要关注一下tomcat的虚拟机内存设置和linux的open file限制。

我在测试时遇到一个问题,maxThreads我设置的比较大比如3000,当服务的线程数大到一定程度时,一般是2000出头,单次请求的响应时间就会急剧的增加,百思不得其解这是为什么,四处寻求答案无果,最后我总结的原因可能是cpu在线程切换时消耗的时间随着线程数量的增加越来越大,

cpu把大多数时间都用来在这2000多个线程直接切换上了,当然cpu就没有时间来处理我们的程序了。

以前一直简单的认为多线程=高效率。。其实多线程本身并不能提高cpu效率,线程过多反而会降低cpu效率。

当cpu核心数<线程数时,cpu就需要在多个线程直接来回切换,以保证每个线程都会获得cpu时间,即通常我们说的并发执行。

所以maxThreads的配置绝对不是越大越好。

现实应用中,我们的操作都会包含以上两种类型(计算、等待),所以maxThreads的配置并没有一个最优值,一定要根据具体情况来配置。

最好的做法是:在不断测试的基础上,不断调整、优化,才能得到最合理的配置。

acceptCount的配置,我一般是设置的跟maxThreads一样大,这个值应该是主要根据应用的访问峰值与平均值来权衡配置的。如果设的较小,可以保证接受的请求较快相应,但是超出的请求可能就直接被拒绝如果设的较大,可能就会出现大量的请求超时的情况,因为我们系统的处理能力是一定的。

提示:

很多做过php运维的朋友在这里会犯一个大错误,php优化服务器通常怎做法是安装cpu以及内存的情况配置连接数,连接数过万都很正常,但java不同jvm配置要非常小心,稍有差错就会崩溃。

maxThreads 配置要结合 JVM -Xmx 参数调整,也就是要考虑内存开销。

5.优化网络

# sysctl settings are defined through files in
# /usr/lib/sysctl.d/, /run/sysctl.d/, and /etc/sysctl.d/.
#
# Vendors settings live in /usr/lib/sysctl.d/.
# To override a whole file, create a new file with the same in
# /etc/sysctl.d/ and put new settings there. To override
# only specific settings, add a file with a lexically later
# name in /etc/sysctl.d/ and put new settings there.
#
# For more information, see sysctl.conf(5) and sysctl.d(5).
###############################################################
net.core.netdev_max_backlog = 32768 
net.core.somaxconn = 32768 
net.core.wmem_default = 8388608 
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216 
net.ipv4.ip_local_port_range = 1024 65000 
net.ipv4.route.gc_timeout = 100 
net.ipv4.tcp_fin_timeout = 30 
net.ipv4.tcp_keepalive_time = 1200 
net.ipv4.tcp_timestamps = 0 
net.ipv4.tcp_synack_retries = 2 
net.ipv4.tcp_syn_retries = 2 
net.ipv4.tcp_tw_recycle = 1 
net.ipv4.tcp_tw_reuse = 1 
net.ipv4.tcp_mem = 94500000 915000000 927000000 
net.ipv4.tcp_max_orphans = 3276800 
net.ipv4.tcp_max_syn_backlog = 65536
######################################################################
net.core.netdev_max_backlog = 32768   #每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目
net.core.somaxconn = 32768                  #定义了系统中每一个端口最大的监听队列的长度,这是个全局的参数,默认值为1024
net.core.wmem_default = 8388608        #该文件指定了发送套接字缓冲区大小的缺省值(以字节为单位)。
net.core.rmem_default = 8388608         #该文件指定了接收套接字缓冲区大小的默认值(以字节为单位)。
net.core.rmem_max = 16777216    #指定了接收套接字缓冲区(接收窗口)大小的最大值(以字节为单位)最大的TCP数据接收缓冲
net.core.wmem_max = 16777216 #指定了发送套接字缓冲区(接收窗口)大小的最大值(以字节为单位)最大的TCP数据发送缓冲
net.ipv4.ip_local_port_range = 1024 65000 #指定端口范围的一个配置,默认是32768 61000
net.ipv4.route.gc_timeout = 100      #路由缓存刷新频率,当一个路由失败后多长时间跳到另一个路由,默认是300。
net.ipv4.tcp_fin_timeout = 30          #表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间。
net.ipv4.tcp_keepalive_time = 1200 #表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为20分钟。
net.ipv4.tcp_timestamps = 0           #以一种比重发超时更精确的方法(请参阅 RFC 1323)来启用对 RTT 的计算;为了实现更好的性能应该启用这个选项,时间戳在(请参考RFC 1323)TCP的包头增加12个字节
net.ipv4.tcp_synack_retries = 2       # syn-ack握手状态重试次数,默认5,遭受syn-flood攻击时改为1或2
net.ipv4.tcp_syn_retries = 2            #外向syn握手重试次数,默认4
net.ipv4.tcp_tw_recycle = 1            # 默认0,tw快速回收
net.ipv4.tcp_tw_reuse = 1               #表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_mem = 94500000 915000000 927000000
#确定 TCP 栈应该如何反映内存使用;每个值的单位都是内存页(通常是 4KB)。
第一个值是内存使用的下限。
第二个值是内存压力模式开始对缓冲区使用应用压力的上限。
第三个值是内存上限。在这个层次上可以将报文丢弃,从而减少对内存的使用。
对于较大的 BDP 可以增大这些值(但是要记住,其单位是内存页,而不是字节)
net.ipv4.tcp_max_orphans = 3276800
#系统中最多有多少个TCP套接字不被关联到任何一个用户文件句柄上。
如果超过这个数字,孤儿连接将即刻被复位并打印出警告信息。
这个限制仅仅是为了防止简单的DoS攻击,你绝对不能过分依靠它或者人为地减小这个值,更应该增加这个值(如果增加了内存之后)

net.ipv4.tcp_max_syn_backlog = 65536#表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。

打赏作者

Leave a Reply

Your email address will not be published.