Docker 搭建 WordPress博客

作为一个docker初学者,几乎第一个案例都是docker轻松搭建一个WordPress,关于WordPress是什么,用来做什么,建议自行百度,下面直接讲解搭建步骤:

使用的ubuntu16.04,每次都要加sudo,我索性直接使用root用户操作。

1、下载MySQL镜像,使用docker命令:docker pull mysql ,我下载的有400多M还是有点慢,需要耐心等待

MySQL下载完毕

2、启动MySQL镜像

docker run --name mysql_db -e MYSQL_ROOT_PASSWORD=123456 -d mysql

取个名字叫mysql_db 密码123456 最后的mysql是告诉docker容器启动一个mysql的数据库。

3、可以通过 docker ps来查看运行的容器

还可以通过 docker inspect id来查看具体信息,ID可以不用输全,只要唯一即可。

4、下载WordPress镜像

docker pull wordpress

5、启动Wordpress

docker run --name wordpressTest --link mysql_db:mysql -p 8086:80 wordpress

ocker run --name wordpressTest --link mysql_db:mysql -p 8086:80 wordpress

给运行的WordPress取个名字叫wordpressTest 数据库link到mysql_db的mysql数据库,端口由默认的80转换到8086,wordpress

6、移除运行的容器,按名字移除

docker rm 名字

7、访问

我是通过虚拟机来进行访问,需要把虚拟机的防火墙进行设置,否则访问不到,我这里直接停止了防火墙服务
Redhat停止防火墙:service iptables stop
Ubuntu停止防火墙:ufw disable
如果启动成功,直接访问http://ip:端口/容器名字 即可访问,
第一次访问需要设置语言、设置登录用户名和密码,由于忘记了截图,请见谅
后面访问

好了,docker运行WordPress到此就搭建完毕。只需要运行docker命令既可,如果按照传统的搭建方式,需要按照各种软件和环境,还有可能每个环境不一样,出现各种奇葩问题,有了docker就不会出现这样的问题。
至于在Linux怎么按照docker,在这里提供ubuntu的按照命令,由于docker是诞生在ubuntu的环境下,建议学习就用ubuntu环境,我在这里就介绍一种自带的按照方式
sudo apt-get update
sudo apt-get install docker.io
这种安装方式需要时ubuntu14.04及以上版本,之前没有自带docker。
docker在Windows和Mac环境没有支持,但是提供了一个docker tool的安装方式,其实也是在Windows和Mac下虚拟了一个Linux环境,网上的建议是安装双系统,没有那个条件,建议还是用虚拟机吧。

无法回应的ARP请求包导致的网站缓慢问题排错

问题

访问一个网站,从本地访问很快,但是从客户端访问大概要等待3秒的样子。在服务器放上静态网页,在客户端访问则返回时间很快。

排错步骤

  • 在客户端访问问题网站,在客户端用wireshark抓包

用tcp 三次握手及客户端请求与服务器返回的ACK来判断是否存在线路或者服务器忙问题,发现不是。348-349 显示出服务器响应很快。 349-498 之间用了3.28秒,说明这是服务器应用的问题。

未分类

  • 让开发人员调查服务器端的应用,开发人员说之前有个小功能可以抓取客户端MAC地址,但是看到抓的包,应该不是用的客户端的代码,因为第一个web页响应就3秒多,要是客户端代码那也是后续的JS或者资源加载较慢。
  • 不管三七二十一,在服务器端也抓了下包。过滤下arp 和http的包看看,过滤后发现有三个ARP请求,但是没有对应回应。另外仔细看ARP请求的具体内容也不对,服务器用ARP请求去解析客户端的MAC地址应该是不对的,原因是服务器和客户端不在一个网段,正常的跨网段的ARP请求是同一个网段才会用的,如果跨网段那应该去解析路由器的MAC地址。所以这些ARP请求有问题。

未分类

  • 开发人员注释掉了客户端ARP地址查询的代码。访问速度瞬间提升了。
  • 开发人员同时注意到客户端ARP地址查询的结果为00-00-00-00-00-00,和我们的服务器上的抓包结果一致,因为去请求一个跨网段IP地址的MAC,所以目标地址不会收到,因为ARP广播会在路由器端终止。

揭开真相

开发人员给了我服务器端的代码C#

c#
[DllImport(“Iphlpapi.dll”)]

  private static extern int SendARP(Int32 dest, Int32 host, ref Int64 mac, ref Int32 length);
  [DllImport("Ws2_32.dll")]
  private static extern Int32 inet_addr(string ip);

  public string getClientMac(string userip)
  {
      if (string.IsNullOrEmpty(userip)) return null;
      //string userip = Request.UserHostAddress;
      string strClientIP = userip.ToString().Trim();
      Int32 ldest = inet_addr(strClientIP);
      Int32 lhost = inet_addr("");
      Int64 macinfo = new Int64();
      Int32 len = 6;
      int res = SendARP(ldest, 0, ref macinfo, ref len);
      string mac_src = macinfo.ToString("X");
      //if (mac_src == "0")
      //{
      //    ip = userip;
      //}

      while (mac_src.Length < 12)
      {
          mac_src = mac_src.Insert(0, "0");
      }

      string mac_dest = "";

      for (int i = 0; i < 11; i++)
      {
          if (0 == (i % 2))
          {
              if (i == 10)
              {
                  mac_dest = mac_dest.Insert(0, mac_src.Substring(i, 2));
              }
              else
              {
                  mac_dest = "-" + mac_dest.Insert(0, mac_src.Substring(i, 2));
              }
          }
      }

      return mac_dest;
  }
* 按照代码逻辑的话,服务器应该是用了一次SendARP 调用,但是为什么会有三个ARP请求产生,而且不同的ARP请求包之间的等待时间不一。所以为了验证这个SendARP的调用的实际操作,我用powershell 写了上面一个sendARP 调用,然后用wireshark抓包。
``` powershell
Function Send-Arp { 
param( 
    [string]$DstIpAddress, 
    [string]$SrcIpAddress = 0 
) 


$signature = @" 
[DllImport("iphlpapi.dll", ExactSpelling=true)] 
   public static extern int SendARP(  
       uint DestIP, uint SrcIP, byte[] pMacAddr, ref int PhyAddrLen); 
"@ 

    Add-Type -MemberDefinition $signature -Name Utils -Namespace Network 

    try { 
        $DstIp = [System.Net.IPAddress]::Parse($DstIpAddress) 
        $DstIp = [System.BitConverter]::ToInt32($DstIp.GetAddressBytes(), 0) 
    } catch { 
        Write-Error "Could not convert $($DstIpAddress) to an IpAddress type.  Please verify your value is in the proper format and try again." 
        break 
    } 


    if ($SrcIpAddress -ne 0) { 
        try { 
            $SrcIp = [System.Net.IPAddress]::Parse($SrcIpAddress) 
            $SrcIp = [System.BitConverter]::ToInt32($SrcIp.GetAddressBytes(), 0) 
        } catch { 
            Write-Error "Could not convert $($SrcIpAddress) to an IpAddress type.  Please verify your value is in the proper format and try again." 
            break 
        } 
    } else { 
        $SrcIp = $SrcIpAddress 
    } 


    $New = New-Object PSObject -Property @{ 
        IpAddress = $DstIpAddress 
        PhysicalAddress = '' 
        Description = '' 
        ArpSuccess = $true 
    } | Select-Object IpAddress,PhysicalAddress,ArpSuccess,Description 

    $MacAddress = New-Object Byte[] 6 
    $MacAddressLength = [uint32]$MacAddress.Length 

    $Ret = [Network.Utils]::SendARP($DstIp, $SrcIp, $MacAddress, [ref]$MacAddressLength) 

    if ($Ret -ne 0) { 
        $New.Description =  "An error was returned from SendArp() with error code:  $($Ret)" 
        $New.ArpSuccess = $false 
    } else { 
        $MacFinal = @() 
        foreach ($b in $MacAddress) { 
            $MacFinal += $b.ToString('X2') 
        } 

        $New.PhysicalAddress = ($MacFinal -join ':') 
    } 

    Write-Output $New 
}
  • 使用powershell 来解析一个跨网段的目标IP地址,然后紧接着ping目标主机,这样可以根据ping包的开始时间得出sendARP 的结束时间。

powershell 命令如下:

send-arp serverIP ;ping serverIP
  • 抓包过滤ARP以及ICMP来验证,SendARP函数会发送三个ARP包,可能也会等待超时,因为没有ARP包回应,这个测试的时间大概也在3.1秒左右,符合问题现象。

未分类

最后总结

  1. 在服务器上本机访问非常快,是因为服务器使用ARP请求查本机,应该会很快有回应。如果其他客户端和服务器在同一个网段,估计也不会慢。

  2. 客户端慢是因为服务器在返回给客户端http信息时,先用ARP请求跨网段的客户端IP,但是不会有ARP回应,因为路由的原因,客户端看不到服务器的ARP请求,而SendARP函数的超时时间大概为3.1秒,所以跨网段的客户端收到服务器的一个HTTP响应在3.28秒左右。同样单纯在客户端抓包只能分析出服务器应用有问题,但是说不出具体的问题。

  3. 静态网页快是因为,静态网页不执行服务器端代码,所以不会执行ARP查询。

  4. 另外开发人员也应该熟悉常见的网络协议,像这次的代码就会仅仅在特定场景下工作,如果网站是面向互联网的话,那这个代码将不会起到作用,反而影响性能。

TCP三次握手wireshark抓包分析

本文内容有以下三个部分:

  • wireshark过滤规则
  • osi模型简述
  • tcp三次握手

一、wireshark过滤规则

wireshark只是一个抓包工具,用其他抓包工具同样能够分析tcp三次握手协议。以下这张图片完整地展现了wireshark的面板。

未分类

使用好wireshark一个关键是如何从抓到的众多的包中找到我们想要的那一个。这里就要说filter过滤规则了。如上图,在过滤器方框,我们加上了ip.src==192.168.1.102 or ip.dst==192.168.1.102的过滤规则,意思是在封包列表中,只显示源ip地址为192.168.1.102或者目的ip地址为192.168.1.102的包。
下面列举一些常用的过滤规则:

  1. 过滤IP,如来源IP或者目标IP等于某个IP
    如前面说的例子: ip.src==192.168.1.102 or ip.dst==192.168.1.102
    比如TCP,只显示TCP协议。

  2. 过滤端口
    tcp.dstport == 80 // 只显tcp协议的目标端口80
    tcp.srcport == 80 // 只显tcp协议的来源端口80
    也可以写成tcp.port eq 80 or udp.port eq 80 这样的模式

  3. 过滤协议
    单独写上tcp、udp、xml、http就可以过滤出具体协议的报文。你也可以用tcp or xml这样格式来过滤。
    我们还可以更加具体过滤协议的内容,如tcp.flags.syn == 0x02 表示显示包含TCP SYN标志的封包。

  4. 过滤mac地址
    eth.src eq A0:00:00:04:C5:84 // 过滤来源mac地址
    eth.dst==A0:00:00:04:C5:84 // 过滤目的mac地址

  5. http模式过滤
    http.request.method == “GET”
    http.request.method == “POST”
    http.request.uri == “/img/logo-edu.gif”
    http contains “GET”
    http contains “HTTP/1.”
    // GET包
    http.request.method == “GET” && http contains “Host: ”
    http.request.method == “GET” && http contains “User-Agent: ”
    // POST包
    http.request.method == “POST” && http contains “Host: ”
    http.request.method == “POST” && http contains “User-Agent: ”
    // 响应包
    http contains “HTTP/1.1 200 OK” && http contains “Content-Type: ”
    http contains “HTTP/1.0 200 OK” && http contains “Content-Type: “

  6. 过滤内容
    contains:包含某字符串
    ip.src==192.168.1.107 and udp contains 02:12:21:00:22
    ip.src==192.168.1.107 and tcp contains “GET”
    前面也有例子,http contains “HTTP/1.0 200 OK” && http contains “Content-Type: “

更加高级的用法就不讨论了,读者有兴趣可以查阅wireshark官网用户手册。

二、osi模型简述

OSI定义了网络互连的七层框架(物理层、数据链路层、网络层、传输层、会话层、表示层、应用层),即ISO开放互连系统参考模型。如下图左边部分。在每个分层中,都会对所发送的数据附加一个首部,在这个首部中包含了该层必要的信息,如源ip地址和目的ip地址等。

未分类

osi模型中,在下一层的角度看,当收到上一层的包时,全部会被认为是本层的数据,然后在本层中加上自己本层的首部,继续往下传递。一个数据包格式如下:

未分类

举一个例子,比如客户端应用程序A向远端服务器应用程序B发送“早上好”的数据,那么大致流程是这样的:

  1. 应用程序A把数据发送给下一层的TCP模块。

  2. TCP模块属于传输层。TCP在应用层数据的前端加上一个TCP首部。TCP首部中包含源端口号和目标端口号等信息。然后将包发送给IP模块。

  3. IP模块属于网络层。IP将TCP传过来的TCP首部和TCP数据合起来当做自己的数据,并且在TCP首部的前面加上自己的IP首部。IP首部包含了源ip地址和目的ip地址。然后,IP包将被发送给数据链路层,也就是以太网驱动程序。

  4. 从IP传过来的包,对于以太网驱动程序来说不过就是数据。给这些数据加上以太网首部,里面包含了源MAC地址和目的MAC地址。然后再通过物理层把数据发送给目的MAC地址。

  5. 服务器物理层接收到来自客户端的数据包时,首先从以太网的包首部找到MAC地址,判断是否为发给自己的包,如果不是就丢弃,如果是就向上转移给IP模块解析。

  6. IP模块收到IP包首部和后面的数据以后,判断包首部的目的ip地址与自己的ip地址是否匹配,如果匹配,就接收数据并传给TCP模块处理。

  7. TCP模块会检查端口号确定接收数据的应用程序是哪一个。

  8. 应用程序接收到数据包之后也会根据自己的规则判断做出一系列的处理。

通过上面例子大致的过程,可以体会到从上而下发包再到从下而上收包的过程。

三、tcp三次握手

未分类

上图为tcp三次握手,很多书籍都能看到。TCP提供面向有连接的通信传输,在数据通信开始之前先做好通信两端之间的准备工作。也就是说必须握手成功之后,才能进行通信。 接下来,用wireshark来抓取tcp三次握手报文。打开浏览器输入 http://blog.csdn.net/u014530704/article/。运用我们再前面介绍的过滤规则,在过滤框中输入http contains u014530704,找到如下包:

未分类

可以发现我们向服务器请求的包,其中目的ip地址是47.95.165.112。这个地址是blog.csdn.net的ip地址。
然后我们找 ip.src==47.95.165.112 or ip.dst==47.95.165.112 的包,找到了如下:

未分类

红色方框为本机192.168.1.101和服务器47.95.165.112之间的三次握手协议。三次握手协议的过程为:

客户端通过TCP首部发送一个SYN包作为建立连接的请求等待确认应答。
服务器发送ACK包确认应答,发送SYN包请求连接。
客户端针对SYN包发送ACK包确认应答。
最后,讲述一下看懂报文的方法。想要读懂tcp报文,头部至关重要,对着下图去看wireshark tcp报文,并且找到tcp首部各个字段代表的意思,你就能读懂tcp报文了。其他像以太网报文、ip报文、http报文等同样如此。

未分类

tomcat https配置方法(免费证书)

相较于http,https提供了身份验证与加密通信方法.于是,出于安全性考虑,越来越多的网站开始使用https协议.

一、环境准备

为了成功配置https,你需要具备以下环境:

  • java jdk
  • tomcat

二、SSL证书简介

要想使用https,首先,我们需要有SSL证书,证书可以通过两个渠道获得:

1、公开可信认证机构
例如CA,但是申请一般是收费的,一般几百到几千一年.

2、自己生成
虽然安全性不是那么高,但胜在成本低.

目前证书有以下常用文件格式:JKS(.keystore),微软(.pfx),PEM(.key + .crt)。其中,tomcat使用JKS格式,nginx使用PEM格式.
虽然两种方式博主都已经实现过,但是这里只讲JKS格式.

三、JKS格式证书生成

好了,我们的jdk要派上用场了

1、打开你的终端或者命令行或者其他黑窗口,输入:

keytool -genkey -v -alias testKey -keyalg RSA -validity 3650 -keystore ~/Lee/test.keystore
  • alias: 别名 这里起名testKey
  • keyalg: 证书算法,RSA
  • validity:证书有效时间,10年
  • keystore:证书生成的目标路径和文件名,替换成你自己的路径即可,我定义的是~/Lee/test.keystore

2、回车,然后会让你输入一些信息,其中秘钥库口令和秘要口令最好输入同一个,并且记下这个口令,其他的随便填即可

未分类

3、在你刚才的目标路径里拿到生成好的test.keystore.

四、tomcat配置https

1、把keystore证书上传到你的tomcat服务器上(如果你的tomcat在本地,那么不移动也可以),并记下证书所在路径.

2、cd到tomcat的conf目录下,打开server.xml文件,搜索https找到下面这项:

未分类

3、先去掉注释,然后将keystoreFile和keystorePass处替换成你自己的证书路径和生成证书时的口令即可.

<Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol"
               maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS" keystoreFile="你的keystore路径" keystorePass="生成证书时的口令"  />
  • port: https的端口,默认8443
  • clientAuth:设置是否双向验证,默认为false,设置为true代表双向验证keystoreFile
  • keystoreFile: keystore证书的路径
  • keystorePass: 生成keystore时的口令

4、启动tomcat,然后就可以使用https和8443端口访问你的服务了!

Tomcat 安全部署实战指南

0x01 关于Tomcat,更多详情大家可直接参考百科说明

https://zh.wikipedia.org/wiki/Apache_Tomcat

此次演示环境:

CentOS7 x86_64  ip: 192.168.3.64
Apache Tomcat/8.5.24    建议大家用稳定的相对比较新的版本

0x02 首先,在正式部署Tomcat之前,需要先来准备好jdk环境,因为毕竟底层还是在靠java来处理,所以必须要得先有java的运行环境才行,其实,在实际生产环境中,也可以单独使用jre,不过个人觉得这和安全的关系并不大,试想,如果你手里都已经拿到了一个可以运行java的环境了,我在本地用对应版本的jdk编译好了再丢上运行也是一样,防不住啥,太泛泛

# tar xf jdk-8u151-linux-x64.tar.gz
# mv jdk1.8.0_151/ /usr/local/
# ln -s /usr/local/jdk1.8.0_151/ /usr/local/jdk
# tar xf apache-tomcat-8.5.24.tar.gz
# mv apache-tomcat-8.5.24 /usr/local/
# ln -s /usr/local/apache-tomcat-8.5.24/ /usr/local/tomcat
# ll /usr/local/
# vi /etc/profile
  export JAVA_HOME=/usr/local/jdk/ 
  export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH 
  export CLASSPATH=.$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$JAVA_HOME/lib/tools.jar
  export TOMCAT_HOME=/usr/local/tomcat
# source /etc/profile
# java -version
# javac

0x03 务必专门创建一个普通用户来运行tomcat服务,防止入侵者直接拿到root权限的webshell,极度建议大家在实际应用中在满足当前及拓展业务需求的前提下,尽量把tomcat压缩在一个极小的系统权限下

# useradd -u 301 tomcat
# passwd tomcat
# chown -R tomcat:tomcat /usr/local/tomcat/
# chown -R tomcat:tomcat /usr/local/jdk/
# ll /usr/local/jdk/ /usr/local/tomcat/
# su - tomcat
$ /usr/local/tomcat/bin/catalina.sh start
$ netstat -tulnp
$ cat /usr/local/tomcat/logs/catalina.out  启动过程中如果有什么问题可以去该日志

0x04 理解Tomcat的基本目录结构

# tree -L 1 /usr/local/tomcat/
/usr/local/tomcat/
├── bin        主要用来放置各种tomcat服务管理脚本
├── conf       用于存放Tomcat服务的各类配置文件
├── lib        此目录用于存放Tomcat的核心类库文件
├── LICENSE
├── logs       主要用于放置Tomcat的启动和访问日志文件
├── NOTICE
├── RELEASE-NOTES
├── RUNNING.txt
├── temp       存放一些缓存临时数据的目录
├── webapps    默认的站点根目录
└── work       把jsp代码转换为java代码[class]文件时的存放目录,这就是为什么访问速度极快的原因之一

0x05 理解Tomcat各个配置文件的主要功用

# tree -L 1 /usr/local/tomcat/conf/
/usr/local/tomcat/conf/
├── Catalina                
├── catalina.policy   当带上-security选项启动tomcat时,会自动读取应用该文件中的策略配置
├── catalina.properties   有关Tomcat内部各种类加载器的一些配置
├── context.xml       此处的context.xml是作用域全局的,一般情况下,每个站点目录下都应有一个context.xml文件,用于定义会话管理器,JDBC等
├── jaspic-providers.xml
├── jaspic-providers.xsd
├── logging.properties    指定不同类日志的格式
├── server.xml        tomcat主配置文件
├── tomcat-users.xml      用于tomcat web管理端认证的配置文件
├── tomcat-users.xsd
└── web.xml       站点被部署时使用的默认部署配置项

0x06 深入理解tomcat的运作细节及常用组件

简单回顾Tomcat的基本运作细节

-> 此处,以请求 http://www.rootkit.org:8080/demo/index.jsp 为例
  -> 首先,客户端来请求www.rootkit.org的8080端口,正好被监听于此的Http Connector所捕获
    -> 随后,Connector会把该请求交给它所在的Service中的Engine去处理并等待Engine回应
      -> 当Engine拿到这个请求后,会自动根据请求中的www.rootkit.org这个域名来匹配到它内部属于哪个Host,然后直接丢给那个Host
    -> 当请求到达指定的Host时,Host会自动匹配出Path为/demo所在的Context
      -> 之后,对应Context会拿到/index.jsp请求,再到映射表中找出对应的Servlet类去处理,如,构造HttpServletRequest和HttpServletResponse对象
        -> Context会把处理好之后的HttpServletResponse对象返回给Host
          -> Host再把HttpServletResponse对象返回给Engine
        -> 最后,Engine把HttpServletResponse对象返回对应的Connector,至此,一次相对完整的Tomcat请求响应过程就基本完成了

关于上述提到的各个组件功用简介

Server     一个Server即一个Tomcat实例,实际中机器性能好的情况,建议多实例,不建议用自身的虚拟主机功能,虚拟主机之间影响较大   
Service    通常由一个Engine以及一个或多个Connector组成,一个
Connector  主要就是用来监听指定端口,然后把指定端口的请求都交给Engine
Engine     其实就是实际的处理引擎,下面可以同时配置多个虚拟主机,它自身会根据域名来匹配到对应的虚拟主机
Host       即虚拟主机,一个即一个虚拟主机
Context    Context在创建的时候会根据配置文件$CATALINA_HOME/conf/web.xml
       和$WEBAPP_HOME/WEB-INF/web.xml载入Servlet类,当Context收到请求时
       将到自己的映射表 [mapping table]中寻找相匹配的Servlet类,如果找到,则执行该类,并回应该请求

tomcat 主配置文件结构大致如下

<Server>
  <Service>
    <Connector/>
    <Engine>
      <Host>
        <Context></Context>
      </Host>
     </Engine>
  </Service>
  <Service>
   ...
  </Service>
</Server>

0x07 一个标准的 Java web 目录结构,如下

未分类

0x08 如何快速纯手工在Tomcat中自定义一个基于域名的虚拟主机,还是那句话,实际中机器性能够的情况下,建议用tomcat多实例,不建议用虚拟主机

先准备好站点所需的各种目录,务必要严格按照上面的java web标准来

# mkdir /data/{javaweb,logs} -p
# chown -R tomcat:tomcat /data/
$ su - tomcat
$ cd /data/javaweb/
$ mkdir secapp/{lib,classes,WEB-INF,META-INF} -p
$ vi secapp/index.jsp
  <html>
  <body>
  <br>
  <center><h1>This is a Tomcat Server 8.x </h1> 
  <br><h2>Now time is: <%=new java.util.Date()%></h2></center>
  </body>
  </html>

再到tomcat主配置文件中添加Host标签段,记得禁用自动部署和自动解war包

$ vi /usr/local/tomcat/conf/server.xml
  <Host name="www.sec.org" appBase="/data/javaweb/secapp" autoDeploy="false" unpackWARs="true">
    <Context path="" docBase="." debug="0" />
    <Valve className="org.apache.catalina.valves.AccessLogValve" directory="/data/logs"
        prefix="www.sec.org_access_log" suffix=".txt"
        pattern="%h %l %u %t "%r" %s %b" />
  </Host>
$ /usr/local/tomcat/bin/catalina.sh stop
$ cd /usr/local/tomcat/   重启前最好把下面两个目录中的内容清空,防止缓存作祟
$ rm -fr ./temp/*
$ rm -fr ./work/*
$ /usr/local/tomcat/bin/catalina.sh start 
$ cat /usr/local/tomcat/logs/catalina.out   查看tomcat启动日志,一般来这儿都是为了看报错
$ tail -f /data/logs/www.sec.org_access_log.2017-12-17.txt
$ tree /usr/local/tomcat/work/

未分类

0x09 配置Tomcat的web端管理功能,此功能比较危险,容易被人跑到管理账号密码,直接进来即可部署webshell,如非必须,建议删除

$ vi /usr/local/tomcat/conf/tomcat-users.xml
  <role rolename="manager-gui"/>
  <role rolename="admin-gui"/>
  <user username="tomcat" password="tomcat" roles="manager-gui,admin-gui"/> 让tomcat同时应用于两个角色
$ /usr/local/tomcat/bin/catalina.sh start    修改完配置后重启tomcat
$ /usr/local/tomcat/bin/catalina.sh stop

0x10 修改管理端口及用于关闭服务的字符串,默认为8005,SHUTDOWN,实际中,一般也不会用这种方式来关闭tomcat,所以可以把关闭字符串设的更长更随机一些,不过,好在最新版本tomcat默认只监听在127.0.0.1,所以对此项不必太过紧张

$ vi /usr/local/tomcat/conf/server.xml
  <Server port="9301" shutdown="a8HelEd45fm43LseDF">

0x11 修改AJP协议通信端口,其实你可以直接把该项注释掉,因为绝大多数情况下我们都只会使用http进行反向代理,而不会用AJP

$ vi /usr/local/tomcat/conf/server.xml
  <!-- <Connector port="8010" protocol="AJP/1.3" redirectPort="8443" />-->

0x12 禁止tomcat目录遍历,将param-value标签中的值改为false,其实默认就是禁止的,只不过为了保险,还是需要再确认一下

$ vi /usr/local/tomcat/conf/web.xml
  <init-param>
    <param-name>listings</param-name>
    <param-value>false</param-value>   <!-- false即表示禁止目录遍历 --> 
  </init-param>

  <welcome-file-list> <!-- 设置主页索引文件 -->
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>demo.jsp</welcome-file>
  </welcome-file-list>

未分类

0x13 隐藏服务器版本信息,为了能一定程度上防止被人用0day批量打,我需要将tomcat的详细版本稍微隐藏下,比如,在出现403,404,500这样的状态码时,就很容易会暴露我们web服务器的详细版本,当然,你也可以直接把指定的状态码重定向到指定的页面中

$ cd /usr/local/tomcat/lib/
$ unzip catalina.jar
$ cd org/apache/catalina/util/
$ vi ServerInfo.properties
  server.info=Microsoft-IIS/7.5
  server.number=7.5
  server.built=Nov 27 2017 13:05:30 UTC
$ cd /usr/local/tomcat/lib/
$ jar uvf catalina.jar org/apache/catalina/util/ServerInfo.properties
$ /usr/local/tomcat/bin/catalina.sh stop
$ /usr/local/tomcat/bin/catalina.sh start

未分类

0x14 修改http响应头中的server字段名称,只需要在http连接器中添加指定的server属性即可

$ vi /usr/local/tomcat/conf/server.xml
  <Connector port="8080" protocol="HTTP/1.1"
    connectionTimeout="20000"
    redirectPort="8443" server="Microsoft-IIS/7.5" />

未分类

0x15 最好把所有host组件中的自动部署选项全部关掉

$ vi /usr/local/tomcat/conf/server.xml
  <Host name="www.sec.org" appBase="/data/javaweb/secapp" autoDeploy="false" unpackWARs="true">

0x16 如果我们是在前面使用apache或者nginx做的反向代理,也可通过限制特定ip的方式来访问,如下,表示仅允许192.168.3.0/24这个段来访问

$ vi /usr/local/tomcat/conf/server.xml
  <Host>
    ...
    <Context path="" docBase="." debug="0" />
      <Valve className="org.apache.catalina.valves.RemoteAddrValve" allow="192.168.4.*" deny="*.*.*.*" />
    ...  
  </Host>

未分类

0x17 限制其他用户对tomcat各类服务管理工具的使用

$ chmod -R 744 /usr/local/tomcat/bin/*

0x18 配置更加详细的访问日志格式,方便后续审查,如添加记录 user-agent,referer 字段数据

$ vi /usr/local/tomcat/conf/server.xml
  <Valve className="org.apache.catalina.valves.AccessLogValve" directory="/data/logs"
    prefix="www.sec.org_access_log" suffix=".txt"
    pattern="%h %l %u %t "%r" %s %b %{Referer}i %{User-Agent}i %D" resolveHosts="false" />

未分类

0x19 关于Tomcat 7.x 版本,你可能还需要手工去禁用一些危险请求方法,将readonly的值设为true,即禁止DELETE及PUT方法,如下,不过在新版中,默认就处于禁用状态,无需关心

$ vi /usr/local/tomcat/conf/web.xml
  <init-param>
    <param-name>readonly</param-name> 
    <param-value>true</param-value> 
  </init-param>

0x20 必要情况下,可直接禁用tomcat提供的默认web管理端,防止入侵者用此方式部署webshell,此处的采用的禁用方式只是把原来web管理端的文件都重新放到别的目录中,而后再到主配置文件中去注释掉默认的locahost所对应的host组件,而后重启tomcat即可,话说回来,status可以留着,因为可能后续还要靠此来监控jvm的一些性能参数,大家还是酌情而定吧

$ cd /usr/local/tomcat/webapps/
$ mkdir tmp
$ mv docs/ examples/ host-manager/ manager/ ROOT/ ./tmp/
<!-- 
<Host name="localhost"  appBase="webapps"
   unpackWARs="true" autoDeploy="true">
-->
<!--
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
   prefix="localhost_access_log" suffix=".txt"
   pattern="%h %l %u %t "%r" %s %b" />
</Host>
-->

0x21 最后,时常去关注tomcat官方发布的一些高危漏洞补丁,tomcat 8.x 已经相对比较稳定了,所以也建议大家,在稳定的前提下使用更新一点的版本

后话:
因为关注的点不同,所以肯定有所偏颇,此处还是旨在防入侵,至于针对jvm的各种性能优化,后续有机会我们再单独说明,因为tomcat几乎都不会直接面向用户,大多都是通过反向代理的方式来提供服务的,另外,java自身的安全性和性能采用编译的方式来运行都要优于php,所以,针对tomcat的安全并没有像nginx或者apache压力那么大,而且由于tomcat是在后端,也相对比较好控制,虽然它到现在对java的一些类库支持的还不是很完整,但那些对我们而言暂时无需关心,以上仅供部署参考,并不完整,后续还会不断更新,大家可根据自己的实际业务需求再做进更进一步调整,关于java对于的功用,比如,免杀…想必大家也都非常熟悉了,这里就不多说了,更多其它内容,也非常欢迎大家来一起私信交流 ^_^

tomcat优化——并发和Tomcat线程数

最近一直在解决线上一个问题,表现是:
Tomcat每到凌晨会有一个高峰,峰值的并发达到了3000以上,最后的结果是Tomcat线程池满了,日志看很多请求超过了1s。
服务器性能很好,Tomcat版本是7.0.54,配置如下:

<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
       maxThreads="3000" minSpareThreads="800"/>

   <Connector executor="tomcatThreadPool" port="8084" protocol="org.apache.coyote.http11.Http11AprProtocol"
              connectionTimeout="60000"
              keepAliveTimeout="30000"
              maxKeepAliveRequests="8000"
              maxHttpHeaderSize="8192"
              URIEncoding="UTF-8"
              enableLookups="false"
              acceptCount="1000"
              disableUploadTimeout="true"
              redirectPort="8443" />

事后thread dump看其实真正处于RUNNABLE状态的线程很少,绝大部分线程都处于TIMED_WAITING状态:

于是大伙都开始纠结为什么线程会涨到3000,而且发现即使峰值过了线程数并不会降下来。
我们首先想到的是:后端应用的处理瞬间比较慢,“堵住了”导致前端线程数涨了起来。但是优化一个版本上线后发现虽然涨的情况有所好转,但是最终线程池还是会达到3000这个最大值。

==================================分割线=========================================

以上是大背景,中间的过程省略,直接跟各位说下目前我得到的结论:

1、首先是为什么线程不释放的问题?

简单说下我验证的Tomcat(7.0.54)线程池大概的工作机制
Tomcat启动时如果没有请求过来,那么线程数(都是指线程池的)为0;
一旦有请求,Tomcat会初始化minSapreThreads设置的线程数;
Tomcat会停止长时间闲置的线程。Tomcat还有一个参数叫maxIdleTime:

其实从这个参数解释也能看出来Tomcat会停止闲置了超过一定时间的线程的,这个时间就是maxIdleTime。但我之前的测试中确实没有发现线程释放的现象,这是为什么呢?我发现除了这个参数线程池线程是否释放?释放多少?还跟当前Tomcat每秒处理的请求数(从Jmeter或LoadRunner来看可以理解为TPS)有关系。通过下表可以清晰的看出来线程数,TPS和maxIdleTime之间的关系:

TPS     maxIdleTime(ms) Thread Count
10  60,000  600
5   60,000  300
1   60,000  60

依次类推,当然Thread Count这一列是一个大约数,上下相差几个,但基本符合这样一个规则:

Thread Count = min(max((TPS * maxIdleTime)/1000,minSpareThreads),maxThreads)

当然这个Thread Count不会小于minSpareThreads,这个跟之前的结论还是一样的。我现在大胆猜测下(回头看源码验证下,或者哪位同学知道告诉我下,谢谢):

Tomcat线程池每次从队列头部取线程去处理请求,请求完结束后再放到队列尾部,也就是说前后两次请求处理不会用同一个线程。某个线程闲置超过maxIdleTime就释放掉。

未分类

假设首先线程池在高峰时期暴涨到1000,高峰过后Tomcat处理一次请求需要1s(从Jmeter看TPS大约就为1),那么在maxIdleTime默认的60s内会用到线程池中60个线程,那么最后理论上线程池会收缩到60(假设minSpareThreads大于60)。另外:这个跟用不用Keep-Alive没关系(之前测试结论是因为用了Keep-Alive导致程序性能下降,TPS降低了很多导致的)

这就是为什么我之前的测试中、还有我们生产环境中线程数只增不减的原因,因为就算峰值过后我们的业务每秒请求次数仍然有100多,100*60=6000,也就是3000个线程每个线程在被回收之前肯定会被重用。

那么现在有另外一个问题,那么正常情况下为什么每秒100次的请求不会导致线程数暴增呢?也就是说线程暴增到3000的瓶颈到底在哪?这个我上面的结论其实也不是很准确。
真正决定Tomcat最大可能达到的线程数是maxConnections这个参数和并发数,当并发数超过这个参数则请求会排队,这时响应的快慢就看你的程序性能了。
这里没说清楚的是并发的概念,不管什么并发肯定是有一个时间单位的(一般是1s),准确的来讲应该是当时Tomcat处理一个请求的时间内并发数,比如当时Tomcat处理某一个请求花费了1s,那么如果这1s过来的请求数达到了3000,那么Tomcat的线程数就会为3000,maxConnections只是Tomcat做的一个限制。

2、为什么线程池会满?

这是我现在纠结的核心。到底是不是应用的性能慢导致的,我现在的结论是有关系,但关键是并发。

Tomcat的线程池的线程数跟你的瞬间并发有关系,比如maxThreads设置为1000,当瞬间并发达到1000那么Tomcat就会起1000个线程来处理,这时候跟你应用的快慢关系不大。
那么是不是并发多少Tomcat就会起多少个线程呢?这里还跟Tomcat的这几个参数设置有关系,看官方的解释是最靠谱的:

maxThreads:The maximum number of request processing threads to be created by this Connector, which therefore determines the maximum number of simultaneous requests that can be handled. If not specified, this attribute is set to 200. If an executor is associated with this connector, this attribute is ignored as the connector will execute tasks using the executor rather than an internal thread pool.
maxConnections:The maximum number of connections that the server will accept and process at any given time. When this number has been reached, the server will accept, but not process, one further connection. This additional connection be blocked until the number of connections being processed falls below maxConnections at which point the server will start accepting and processing new connections again. Note that once the limit has been reached, the operating system may still accept connections based on the acceptCount setting. The default value varies by connector type. For BIO the default is the value of maxThreads unless an Executor is used in which case the default will be the value of maxThreads from the executor. For NIO the default is 10000. For APR/native, the default is 8192.……
acceptCount:The maximum queue length for incoming connection requests when all possible request processing threads are in use. Any requests received when the queue is full will be refused. The default value is 100.
minSpareThreads:The minimum number of threads always kept running. If not specified, the default of 10 is used.

我简单理解就是:maxThreads:Tomcat线程池最多能起的线程数;maxConnections:Tomcat最多能并发处理的请求(连接);acceptCount:Tomcat维护最大的对列数;minSpareThreads:Tomcat初始化的线程池大小或者说Tomcat线程池最少会有这么多线程。

比较容易弄混的是maxThreads和maxConnections这两个参数:maxThreads是指Tomcat线程池做多能起的线程数,而maxConnections则是Tomcat一瞬间做多能够处理的并发连接数。比如maxThreads=1000,maxConnections=800,假设某一瞬间的并发时1000,那么最终Tomcat的线程数将会是800,即同时处理800个请求,剩余200进入队列“排队”,如果acceptCount=100,那么有100个请求会被拒掉。

注意:根据前面所说,只是并发那一瞬间Tomcat会起800个线程处理请求,但是稳定后,某一瞬间可能只有很少的线程处于RUNNABLE状态,大部分线程是TIMED_WAITING,如果你的应用处理时间够快的话。所以真正决定Tomcat最大可能达到的线程数是maxConnections这个参数和并发数,当并发数超过这个参数则请求会排队,这时响应的快慢就看你的程序性能了。

tcpdump抓包和scp导出以及wireshark查看

【命令和工具】

tcpdump

scp

wireshark

(1)tcpdump

sudo tcpdump -i eth0 -w /home/tcpdump/1.pcap host 10.214.117.103

-i    查看网卡eth0,抓该网卡报文。

-w    保存到/home/tcpdump目录,保存为1.pcap文件。

host    抓取源主机或目的主机为10.214.117.103的报文。

port    抓取8080端口的报文。

Ctrl+C键停止抓取报文。

(2)scp

scp  [email protected]:/home/tcpdump/1.pcap /home/aiox/1.pcap

从pop这个用户进入主机172.17.118.70的/home/tcpdump目录,下载1.pcap文件到本机的/home/aiox/1.pcap位置。

改命令会提示输入172.17.118.70主机的pop用户名密码。

(3)wireshark查看

用wireshark打开1.pacp报文,在随便哪个报文的地方右键-【追踪流】-【tcp流】查看http报文。

未分类

未分类

linux 下抓包命令 tcpdump tcpflow

tcpflow实际上也是一个抓包工具,这个抓包工具与tcpdump不同的是它是以流为单位显示数据内容,而cpdump以包为单位显示数据。我们平常会经常分析HTTP数据,用tcpflow会更便捷,且tcpflow看起来会更加直观些。

#截取本机(192.168.31.147)和主机114.114.114.114之间的数据
tcpdump -n -i eth0 host 192.168.31.147 and 114.114.114.114
#截取全部进入服务器的数据可以使用以下的格式
tcpdump -n -i eth0 dst 192.168.31.147
#服务器有多个IP 可以使用参数
tcpdump -n -i eth0 dst 192.168.31.147  or  192.168.31.157
#从本机出去的数据包
tcpdump -n -i eth0 src 192.168.31.147 or 192.168.31.157

-C
在将一个原始数据包写入一个保存文件之前,请检查该文件是否大于 file_size ,如果是,关闭当前的保存文件并打开一个新文件。第一个保存文件后的保存文件将具有用-w 标志指定的名称 ,后面跟着一个数字,从1开始并继续向上。file_size的单位 是数百万字节(1,000,000字节,而不是1,048,576字节)。

-w
将原始数据包写入 文件, 而不是解析并打印出来。他们以后可以用-r选项打印。如果文件 是“ – ”,则使用标准输出 。有关文件格式

-W
与-C 选项一起使用时 ,这会将创建的文件数量限制为指定的数字,并从头开始覆盖文件,从而创建“旋转”缓冲区。另外,它将命名带有足够前导0的文件来支持最大数量的文件,使它们能够正确排序。
与-G 选项一起使用时 ,这将限制创建的旋转转储文件的数量,在达到限制时以状态0退出。如果与-C一起使用 ,则行为将导致每个时间片的循环文件。

Ubuntu 开启远程登录 SSH 的安装和配置

SSH 为 SecureShell 的缩写,由 IETF 的网络工作小组(NetworkWorkingGroup)所制定;SSH 是一种安全协议,主要用于给远程登录会话数据进行加密,保证数据传输的安全。利用 SSH 协议可以有效防止远程管理过程中的信息泄露问题。

更新源列表

打开”终端窗口”,输入 “sudo apt-get update” –> 回车 –> “输入当前登录用户的管理员密码” –> 回车,就可以了。

安装 SSH

在 “终端窗口” 输入 “sudo apt-get install openssh-server” –> 回车 –> 输入 “y” –> 回车 –> 安装完成。

sudo apt-get install openssh-server

查看 SSH 服务是否启动

打开”终端窗口”,输入 “sudo ps -e | grep ssh” –> 回车 –> 有 sshd,说明 ssh 服务已经启动,如果没有启动,输入 “sudo service ssh start” –> 回车 –> ssh 服务就会启动。

未分类

查看 Ubuntu 的 IP 地址

打开”终端窗口”,输入 “sudo ifconfig” –> 回车 –> 就可以查看到 IP 地址。

未分类

使用 Putty 远程登录

运行 putty –> 输入主机的 IP 地址、会话名称 –> 保存 。

未分类

双击 “会话名称” 打开连接 –> 输入用户名和密码 –> 登录成功。

未分类

登录之后可能会出现中文字符无法正确显示,只要在标题栏上右键 –> Chenge Settings… -> Translation -> Remote character set –> 选择 “UTF-8” –> 点击 “Apply” 即可。

未分类

未分类

编译安装squid

概述

什么是squid,简单的说squid是一款代理缓存软件就是加速网站访问的,国内部分cdn服务用的就是squid,cdn大家应该清楚吧,内容分发网络,cdn的作用也就是为了加速网站在全国各地的访问

工作流程

squid的工作流程是怎么样的呢,我举一个例子,我要请求一个网页,我不是直接向网站的服务器请求的,而是向网站的代理服务器,简单来说就是安装了squid的这台服务器请求数据,这台装着squid的服务器如果有你需要的网站页面的话它会直接发送给你,如果没有,它就会代你去向网站的服务器去请求,当squid服务器请求到数据了之后,它会复制一份道自己这里,然后发一份给你,当你第二次要这些网页的时候,squid会直接把它存的页面发送给你。因为网站的服务器我不可能每一个省都部署一个,但是squid服务器却可以,所以部署squid缓存服务器有降低网站服务器压力和带宽的作用,同时也可以让用户更快得得到自己所需要的网页。

问题

每个东西有利也有弊,当你的网页更新了,但是缓存服务器里的数据还没有更新的时候,问题就出现了,用户请求的网页总是旧的网页。导致不能即使更新网站,所以什么东西适合缓存呢,当然是更新少的东西适合缓存了,比如js,css,图片等,而那些更新快的数据就尽量减少缓存了

编译squid

官网:http://www.squid-cache.org/
首先下载源码包,我下载的是3.5.27版本的

wget http://www.squid-cache.org/Versions/v3/3.5/squid-3.5.27.tar.gz

解压

tar -zxvf squid-3.5.27.tar.gz

接着我的编译参数是

./configure 
--prefix=/usr/local/squid 
--localstatedir=/usr/local/squid/var 
--sysconfdir=/usr/local/squid/etc 
--with-openssl=/root/software/squid/openssl-1.0.2n 
--enable-dlmalloc 
--enable-gnuregex 
--enable-carp 
--enable-async-io 
--enable-storeio=aufs,diskd,ufs 
--enable-icmp 
--enable-delay-pools 
--enable-removal-policies=heap,lru 
--enable-useragent-log 
--enable-referer-log 
--enable-snmp 
--enable-arp-acl 
--enable-htcp 
--enable-ssl 
--enable-cache-digests 
--with-coss-membuf-size=2097152 
--enable-poll 
--enable-linux-netfilter 
--enable-x-accelerator-vary 
--enable-stacktrace 
--enable-truncate 
--enable-underscores 
--enable-auth

它们的意思是

安装目录/usr/local/squid
缓存日志目录/usr/local/squid/var
配置文件目录/usr/local/squid/etc
指定openssl路径
使用dlmalloc内存分配器
使用gnu正则表达式
使用carp
使用异步io
使用aufs,diskd,ufs存储模块
启用icmp
启用延时池
启用排除策略
激活来自客户的useragent日志
激活来自客户的referer日志
启用snmp
启动arp-acl
启用htcp
启用ssl
启用cache-digests
指定coss内存缓冲大小为2M
启用poll
使用netfilter
启用x-accelerator-vary
启用stacktrace
启用truncate
使主机名可以带下划线
使squid支持所有的验证机制

所以首先我要安装openssl库

下载

wget https://www.openssl.org/source/openssl-1.0.2n.tar.gz

记住解压之后不需要安装,之后在指定openssl路径的编译参数那里加上它的路径就好,还有就是openssl1.1版本是不能使用的,要使用1.0的

接着编译

make -j4

安装

make install

安装完成