VirtualBox虚拟机上的Ubuntu Linux Java开发环境部署记录

创建虚拟机时需注意三点:

  • 在BIOS中开启Intel VT,以支持64位虚拟机系统;
  • 对于Ubuntu 16以上版本,至少分配10G以上初始控件。

未分类

  • 增加一块网卡,选择Host-Only网络,用于主机和虚拟机的网络连接。

未分类

Ubuntu安装好以后,创建用户和root密码,进入系统。

点击虚拟机面板“管理-全局设定”,将网络地址设置为与主机同一个网段:

未分类

进入Ubuntu网络设置(点击右上角图标),把Host-Only网卡设为手动获取IP地址,填入主机同网段IP:

未分类

重启一下网络,用主机ping一下虚拟机地址进行验证。

必须注意的是,如果主机使用Wifi网络连接,需要在主机的无线网络属性中勾选Internet连接共享,共享的网段为192.168.137.1,上述设置要随之变化。

未分类

接下来安装增强功能,点击“设备-安装增强功能”,将自动加载光盘:

未分类

打开终端运行autorun.sh:

sudo ./autorun.sh

共享粘贴板中勾选双向:

未分类

First of all,更新一下系统:

sudo apt upgrade

安装FTP:

sudo apt-get install vsftpd

设置FTP路径:

sudo mkdir /usr/ftp

新建一个用于FTP的用户,设置密码以及工作目录:

sudo useradd -d /home/ftp -s /bin/bash ftpuser
passwd ftpuser

接下来有两种方法让ftpuser拥有更改ftp目录的权限,选项一:授予ftpuser用户读写ftp目录权限:

setfacl -R -m u:ftpuser:rwx /usr/ftp

选项二:将ftpuser添加到用户组ftp中,赋予用户组权限:

usermod -a -G ftp ftpuser
chown -R :ftp /usr/ftp
chmod -R g+rwx /usr/ftp
chmod g+s /usr/ftp

检查vsftpd.conf配置文件:

cat /etc/vsftpd.conf | grep -v "#" | more

在配置文件结尾加上如下字段:

write_enable=YES
local_root=/usr/ftp
chroot_local_user=YES
chroot_list_enable=YES

重新启动vsftpd服务:

sudo service vsftpd restart

打开Ubuntu自带浏览器尝试访问即可。

接下来安装Oracle JDK,这里以8u131版本为例,在Oracle官网上下载 jdk-8u131-linux-x64.tar解压到/usr/java目录(可通过上一步安装的FTP来将JDK安装文件传输到虚拟机目录):

sudo tar -xzvf jdk-8u131-linux-x64.tar.gz
sudo mkdir /usr/java
sudo mv usr/ftp/jdk1.8.0_131 /usr/java/jdk1.8

重命名为jdk1.8是为了之后配置环境变量方便,编辑.bashrc文件:

sudo gedit ~/.bashrc

在文件末尾加上以下内容:

# Java
JAVA_HOME=/usr/java/jdk1.8
JRE_HOME=$JAVA_HOME/jre
JAVA_BIN=$JAVA_HOME/bin
CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib
PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
export JAVA_HOME JRE_HOME PATH CLASSPATH

让更改立即生效:

source ~/.bashrc

验证一下是否配置成功:

java -version

未分类

安装扩展包使Virtualbox能够识别USB设备

在默认情况下,Virtualbox是无法读取插到主机的U盘、u盾等USB设备的。但是,Virtualbox官方网站还是提供了解决办法。

那就是安装VirtualBox x.x.xxx Oracle VM VirtualBox Extension Pack 这个扩展包。该包的功能简单地说就是提供了USB2.0设备支持,Virtualbox远程桌面协议、Intel PXE启动支持。

下载地址:https://www.virtualbox.org/wiki/Downloads

使用步骤:

1、下载并且安装

2、当主机为WIN,那么在安装好之后就可以支持USB了,当主机为linux时候,还需要把用户加到vboxusers组里

A.在Ubuntu、Debian、Deepin、Linux Mint里
终端打开:sudo gedit /etc/group
找到这一行:vboxusers:x:126:
添加你的用户名,如你的用户名位kevin,则改成 vboxusers:x:126:kevin 然后保存退出

B.在Redhat、Fedora、Centos
su -
vi /etc/group
找到这一行:vboxusers:x:126:
添加你的用户名,如你的用户名位kevin,则改成 vboxusers:x:126:kevin 然后保存退出

保存修改后,重启计算机,打开终端,输入 id 看看用户次要组里是不是有了 vboxusers 呢。

这样打开Virtualbox,启动虚拟系统,分配USB设备就行了。

更改Tomcat字符编码设置及解决post请求中文字符乱码

当你在web.xml中配置了字符编码设置,可是请求url中的中文字符仍然乱码,很可能的原因就是Tomcat的字符编码配置问题,具体的解决办法如下。

默认编码

如果没有指定字符编码,Servlet规范规定使用”ISO-8859-1″作为默认的编码。

Get请求

如果是get请求,我们可以通过更改Tomcat目录下,conf/server.xml中的Connector中指定 URIEncoding=”UTF-8″ 参数,具体修改内容如下:

<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" URIEncoding="UTF-8" />

Post请求

Post请求需要指定它发送的参数和值的编码。因为大多数客户端并没有设置一个明确的编码,默认采用的是ISO-8859-1。大多数情况下,这并不是我们想要的编码,我们可以使用过滤器来进行控制,Tomcat已经提供了完成这个功能的过滤器的例子或者内置了。请参看:

<!--4.x-->

webapps/examples/WEB-INF/classes/filters/SetCharacterEncodingFilter.java

<!--5.x-->

webapps/servlets-examples/WEB-INF/classes/filters/SetCharacterEncodingFilter.java
webapps/jsp-examples/WEB-INF/classes/filters/SetCharacterEncodingFilter.java

<!--6.x-->

webapps/examples/WEB-INF/classes/filters/SetCharacterEncodingFilter.java

<!--7.x (已经将这个Filter加入Tomcat内置了,具体位置:tomcat目录下的conf/web.xml,直接复制一下代码到你的项目web.xml中)-->

<filter>
<filter-name>setCharacterEncodingFilter</filter-name>
<filter-class>org.apache.catalina.filters.SetCharacterEncodingFilter</filter-class>
<init-param>
   <param-name>encoding</param-name>
   <param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>setCharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

快速找到Tomcat中最耗CPU的线程

1、找出TOMCAT的JVM的进程ID

[work@112542000 ~]$ jps                                                                                                                                         
290 Bootstrap                                                                                                                                                   
61213 Jps                                                                                                                                                       

2、查看该进程中,最耗费CPU的线程

[work@112542000 ~]$ ps -mp 290 -o THREAD,tid,time | sort -k2 -r | head -n 20                                                                                    
USER     %CPU PRI SCNT WCHAN  USER SYSTEM   TID     TIME                                                                                                        
work     15.3   -    - -         -      -     - 03:11:58                                                                                                        
work      1.2  19    - -         -      -   326 00:15:45                                                                                                        
work      0.2  19    - -         -      -   872 00:03:44                                                                                                        
work      0.1  19    - -         -      -   992 00:01:44                                                                                                        
work      0.1  19    - -         -      -   972 00:01:16                                                                                                        
work      0.1  19    - -         -      -   870 00:01:19                                                                                                        
work      0.1  19    - -         -      -   869 00:01:34                                                                                                        
work      0.0  19    - -         -      -  9993 00:00:00                                                                                                        
work      0.0  19    - -         -      -   997 00:00:06                                                                                                        
work      0.0  19    - -         -      -  9969 00:00:00                                                                                                        
work      0.0  19    - -         -      -  9968 00:00:00                                                                                                        
work      0.0  19    - -         -      -   996 00:00:34                                                                                                        
work      0.0  19    - -         -      -  9960 00:00:00                                                                                                        
work      0.0  19    - -         -      -   995 00:00:00                                                                                                        
work      0.0  19    - -         -      -  9944 00:00:00                                                                                                        
work      0.0  19    - -         -      -   994 00:00:31                                                                                                        
work      0.0  19    - -         -      -  9936 00:00:00                                                                                                        
work      0.0  19    - -         -      -  9934 00:00:26                                                                                                        
work      0.0  19    - -         -      -  9933 00:00:21                                                                                                        

3、以TID==326为例,查看该线程的堆栈

首先,将十进制的326转换成十六进制,可以在线转换:

http://tool.oschina.net/hexconvert

结果等于146

使用jstack查询该线程堆栈:

[work@112542000 ~]$ jstack 290 | grep "0x160" -A 10                                                                                                             
"Timer-2955" daemon prio=10 tid=0x00007f39d3ecd000 nid=0x1602 in Object.wait() [0x00007f38fb273000]                                                             
   java.lang.Thread.State: TIMED_WAITING (on object monitor)                                                                                                    
        at java.lang.Object.wait(Native Method)                                                                                                                 
        at java.util.TimerThread.mainLoop(Timer.java:552)                                                                                                       
        - locked <0x00000007c2a0fc40> (a java.util.TaskQueue)                                                                                                   
        at java.util.TimerThread.run(Timer.java:505)                                                                                                            

"Timer-2954" daemon prio=10 tid=0x00007f392481e000 nid=0x15e4 in Object.wait() [0x00007f38fbe7f000]                                                             
   java.lang.Thread.State: TIMED_WAITING (on object monitor)                                                                                                    
        at java.lang.Object.wait(Native Method)                                                                                                                 
        at java.util.TimerThread.mainLoop(Timer.java:552)                                                                                                       
--                                                                                                                                                              
"Timer-2132" daemon prio=10 tid=0x00007f39d367d000 nid=0x1603 in Object.wait() [0x00007f3933f46000]                                                             
   java.lang.Thread.State: TIMED_WAITING (on object monitor)                                                                                                    
        at java.lang.Object.wait(Native Method)                                                                                                                 
        at java.util.TimerThread.mainLoop(Timer.java:552)                                                                                                       
        - locked <0x00000007b44ef3c8> (a java.util.TaskQueue)                                                                                                   
        at java.util.TimerThread.run(Timer.java:505)                                                                                                            

"Timer-2131" daemon prio=10 tid=0x00007f39d3b61800 nid=0x15e6 in Object.wait() [0x00007f3934d54000]                                                             
   java.lang.Thread.State: TIMED_WAITING (on object monitor)                                                                                                    
        at java.lang.Object.wait(Native Method)                                                                                                                 
        at java.util.TimerThread.mainLoop(Timer.java:552)                                                                                                       
--                                                                                                                                                              
"Timer-450" daemon prio=10 tid=0x00007f39d0728800 nid=0x160 in Object.wait() [0x00007f39b1312000]                                                               
   java.lang.Thread.State: TIMED_WAITING (on object monitor)                                                                                                    
        at java.lang.Object.wait(Native Method)                                                                                                                 
        at java.util.TimerThread.mainLoop(Timer.java:552)                                                                                                       
        - locked <0x00000007bddf8788> (a java.util.TaskQueue)                                                                                                   
        at java.util.TimerThread.run(Timer.java:505)                                                                                                            

"Timer-449" daemon prio=10 tid=0x00007f39ec01f000 nid=0xffc7 in Object.wait() [0x00007f39af5f5000]                                                              
   java.lang.Thread.State: TIMED_WAITING (on object monitor)                                                                                                    
        at java.lang.Object.wait(Native Method)                                                                                                                 
        at java.util.TimerThread.mainLoop(Timer.java:552)                                                                                                       

tcpdump在bond环境下抓到重复报文的问题说明

近期在排查问题时, 使用 tcpdump 抓包发现一个很奇怪的现象, 所有抓到的包都有重复的一份, 使用 wireshark 查看则表现为 tcp out of order, tcp dup ack 以及 tcp retransmission 这三种提示特别多, 如下图所示:

未分类

我们在 centos 6 和 centos 7 系统中验证都有这样的问题, 类似情况见 redhat-1260733

下面开始详细介绍该问题的描述及处理细节:

环境说明

10.0.21.5  -  web 主机 - centos6
10.0.21.7  -  redis 主机 - centos7

网络结构

bond 网络结构大致如下:

未分类

两台机器的 eth2, eth3 网口分别连接两个交换机, 选择模式 1 进行设置组成了 bond1 网卡. web 主机 eth2 为当前活跃的网卡, redis 主机 eth3 位当前活跃的网卡.

问题说明

在 bond 模式为 1(active-backup 模式) 的网络环境下, 使用 tcpdump 及选项 -i any 抓取 web 主机上所有网卡的报文, 使用 wireshark 发现所有的接收和发送都有重复的报文, 而对端的 redis 主机则一切正常.出现的现象如上图所示, 使用以下命令进行抓包测试:

  • web 主机上抓包:
tcpdump -S -s 0 -n -n -i any port 6380 -w 6380.pcap
  • 在 web 主机的另一个会话中, 执行 telnet
telnet 10.0.21.7 6380
Trying 10.0.21.7...
Connected to 10.0.21.7.
Escape character is '^]'.
^]
telnet> quit
Connection closed.

web 主机上抓到的包类似下面的内容:

11:18:02.056270 IP 10.0.21.5.24399 > 10.0.21.7.6380: Flags [S], seq 3834591311, win 14600, options [mss 1460,sackOK,TS val 4281235630 ecr 0,nop,wscale 8], length 0
11:18:02.056275 IP 10.0.21.5.24399 > 10.0.21.7.6380: Flags [S], seq 3834591311, win 14600, options [mss 1460,sackOK,TS val 4281235630 ecr 0,nop,wscale 8], length 0
11:18:02.056405 IP 10.0.21.7.3302 > 10.0.21.5.24399: Flags [S.], seq 522913168, ack 3834591312, win 14480, options [mss 1460,sackOK,TS val 3589524071 ecr 4281235630,nop,wscale 8], length 0
11:18:02.056405 IP 10.0.21.7.3302 > 10.0.21.5.24399: Flags [S.], seq 522913168, ack 3834591312, win 14480, options [mss 1460,sackOK,TS val 3589524071 ecr 4281235630,nop,wscale 8], length 0
11:18:02.056425 IP 10.0.21.5.24399 > 10.0.21.7.6380: Flags [.], ack 1, win 58, options [nop,nop,TS val 4281235631 ecr 3589524071], length 0
11:18:02.056426 IP 10.0.21.5.24399 > 10.0.21.7.6380: Flags [.], ack 1, win 58, options [nop,nop,TS val 4281235631 ecr 3589524071], length 0
11:18:02.056560 IP 10.0.21.7.3302 > 10.0.21.5.24399: Flags [P.], seq 1:91, ack 1, win 57, options [nop,nop,TS val 3589524071 ecr 4281235631], length 90
11:18:02.056560 IP 10.0.21.7.3302 > 10.0.21.5.24399: Flags [P.], seq 1:91, ack 1, win 57, options [nop,nop,TS val 3589524071 ecr 4281235631], length 90

可以看到几乎每个包都有重复的条目, 包括 tcp 的三次握手及数据传输等. 所以这里就出现了几个问题:

  • tcpdump 是从哪个网卡抓到的接收的报文(eth2 还是 bond1)?
  • 为什么 web 主机的出方向的包也有重复的条目?

分析处理

有趣的是在 redis 主机上单独监听 bond1 网卡, 数据报文都是正常的, web 主机上独立监听 bond1 网卡也是正常的, 这说明 redis 主机并没有向 web 主机重复发送报文, 也说明 web 主机没有重复向 redis 主机发送报文.

对于第一个问题, 很遗憾 tcpdump 并没有类似的功能能够显示来源数据对应的网卡, 所以我们难以区别到底是 eth2 还是 bond1 网卡引起了重复报文. 第二个问题则比较奇特, 从抓出的报文来看 wen 主机重复发送请求但是 redis 主机并没有收到, 这说明重复发送可能只是 web 主机内部的原因, 同样重复接收报文可能也是 web 主机的内部原因.

我们从内核文档bonding 找到以下重复报文相关的内容

10. 小节
 For the active-backup, balance-tlb and balance-alb modes, the
promiscuous mode setting is propagated only to the active slave.
......
For the active-backup, balance-tlb and balance-alb modes, when
the active slave changes (e.g., due to a link failure), the
promiscuous setting will be propagated to the new active slave.
......
......
13.2 小节:
This is not due to an error in the bonding driver, rather, it
is a side effect of how many switches update their MAC forwarding
tables.  Initially, the switch does not associate the MAC address in
the packet with a particular switch port, and so it may send the
traffic to all ports until its MAC forwarding table is updated.  Since
the interfaces attached to the bond may occupy multiple ports on a
single switch, when the switch (temporarily) floods the traffic to all
ports, the bond device receives multiple copies of the same packet
(one per slave device).

    The duplicated packet behavior is switch dependent, some
switches exhibit this, and some do not.  On switches that display this
behavior, it can be induced by clearing the MAC forwarding table (on
most Cisco switches, the privileged command "clear mac address-table
dynamic" will accomplish this).

从这点看, tcpdump 仅将混杂模式传播到当前活动的网卡中, 另外在交换机进行 MAC 地址学习的时候可能会出现这种重复报文的情况, 这种情况还依赖于交换机的功能. 从这点来看内核文档的解释和我们的问题并没有关系.

由此我们继续追查别的方面, 从底向上的分析, 在 web 主机收到报文的时候, 会对整个报文进行解封装, 如下图所示为数据报文的封装和解封装过程:

未分类

从右边的解封装过程来看, 主机的 MAC 地址经过交换机的学习后不会再出现上述内核文档的现象, 而且数据经过传输肯定会先传到 web 主机的 eth2 物理网卡上, 而不是我们认为的直接到 bond1 网卡上. 我们参考文章 tcpdump-work-with-bonding-interface 了解到在 centos 5 系统中, 内核接收到数据报文之后会直接将 skb-dev 从 eth2 更新为 bond1, 最后再将报文通过 bond1 网口发送给 ptye_all, 由于这个工作模式, 直接通过 tcpdump -i eth2 是抓取不到任何包的, 因为报文数据不会经过 eth2.

但是在 linux 内核 3.16 之后, 内核作者 torvalds 修改了工作模式, skb->dev 首先为 eth2 物理网卡, 数据先通过 eth2, 经过 rx_handler 进行处理, 最后找到 bond1 网卡, 再将数据发送到 bond1 进行处理. 如下:

bond related process is moved to dev->rx_handler, Just like the bridge or openvswitch.

Packet will first be processed by ptype_all with skb->dev is eth0 and then rx_handler(bond handler for eth0,eth1). if the rx handler return RX_HANDLER_ANOTHER, the packet arrive by ptye_all again with differentskb->dev (bond0).

从这点来看, web 主机接收的数据报文会先经过 eth2, 再经过 bond1, 而发送的报文则会先经过 bond1, 再经过 eth2 网卡. 而 tcpdump 使用 -i any 选项抓包, 则意味着不管接收还是发送都会出现重复的条目. 为了验证此猜测我们同时监听 eth2 和 bond1 网卡:

在两个终端界面输入下面命令:

tcpdump -S -s 0 -n -n -i eth2 port 6380 -w eth2_6380.pcap
tcpdump -S -s 0 -n -n -i eth2 port 6380 -w bond1_6380.pcap

可以看到如下输出:

eth2 抓包的数据

# tcpdump -n -n -r eth2_6380.pcap 
reading from file eth2_6380.pcap, link-type EN10MB (Ethernet)
11:40:59.309086 IP 10.0.21.5.42953 > 10.0.21.7.6380: Flags [S], seq 275391682, win 14600, options [mss 1460,sackOK,TS val 70624316 ecr 0,nop,wscale 8], length 0
11:40:59.309122 IP 10.0.21.7.6380 > 10.0.21.5.42953: Flags [S.], seq 969430611, ack 275391683, win 14480, options [mss 1460,sackOK,TS val 3674159462 ecr 70624316,nop,wscale 8], length 0
11:40:59.309137 IP 10.0.21.5.42953 > 10.0.21.7.6380: Flags [.], ack 1, win 58, options [nop,nop,TS val 70624316 ecr 3674159462], length 0
11:41:01.130610 IP 10.0.21.5.42953 > 10.0.21.7.6380: Flags [P.], seq 1:7, ack 1, win 58, options [nop,nop,TS val 70626138 ecr 3674159462], length 6
11:41:01.130643 IP 10.0.21.7.6380 > 10.0.21.5.42953: Flags [.], ack 7, win 57, options [nop,nop,TS val 3674161283 ecr 70626138], length 0
11:41:01.130728 IP 10.0.21.7.6380 > 10.0.21.5.42953: Flags [.], seq 1:1449, ack 7, win 57, options [nop,nop,TS val 3674161283 ecr 70626138], length 1448
11:41:01.130739 IP 10.0.21.5.42953 > 10.0.21.7.6380: Flags [.], ack 1449, win 69, options [nop,nop,TS val 70626138 ecr 3674161283], length 0
11:41:01.130776 IP 10.0.21.7.6380 > 10.0.21.5.42953: Flags [P.], seq 1449:2193, ack 7, win 57, options [nop,nop,TS val 3674161283 ecr 70626138], length 744
11:41:01.130784 IP 10.0.21.5.42953 > 10.0.21.7.6380: Flags [.], ack 2193, win 80, options [nop,nop,TS val 70626138 ecr 3674161283], length 0
11:41:04.453604 IP 10.0.21.5.42953 > 10.0.21.7.6380: Flags [F.], seq 7, ack 2193, win 80, options [nop,nop,TS val 70629461 ecr 3674161283], length 0
11:41:04.453694 IP 10.0.21.7.6380 > 10.0.21.5.42953: Flags [F.], seq 2193, ack 8, win 57, options [nop,nop,TS val 3674164606 ecr 70629461], length 0
11:41:04.453709 IP 10.0.21.5.42953 > 10.0.21.7.6380: Flags [.], ack 2194, win 80, options [nop,nop,TS val 70629461 ecr 3674164606], length 0

bond1 抓包的数据:

# tcpdump -n -n -r bond1_6380.pcap 
reading from file bond1_6380.pcap, link-type EN10MB (Ethernet)
11:40:59.309081 IP 10.0.21.5.42953 > 10.0.21.7.6380: Flags [S], seq 275391682, win 14600, options [mss 1460,sackOK,TS val 70624316 ecr 0,nop,wscale 8], length 0
11:40:59.309122 IP 10.0.21.7.6380 > 10.0.21.5.42953: Flags [S.], seq 969430611, ack 275391683, win 14480, options [mss 1460,sackOK,TS val 3674159462 ecr 70624316,nop,wscale 8], length 0
11:40:59.309136 IP 10.0.21.5.42953 > 10.0.21.7.6380: Flags [.], ack 1, win 58, options [nop,nop,TS val 70624316 ecr 3674159462], length 0
11:41:01.130605 IP 10.0.21.5.42953 > 10.0.21.7.6380: Flags [P.], seq 1:7, ack 1, win 58, options [nop,nop,TS val 70626138 ecr 3674159462], length 6
11:41:01.130643 IP 10.0.21.7.6380 > 10.0.21.5.42953: Flags [.], ack 7, win 57, options [nop,nop,TS val 3674161283 ecr 70626138], length 0
11:41:01.130728 IP 10.0.21.7.6380 > 10.0.21.5.42953: Flags [.], seq 1:1449, ack 7, win 57, options [nop,nop,TS val 3674161283 ecr 70626138], length 1448
11:41:01.130737 IP 10.0.21.5.42953 > 10.0.21.7.6380: Flags [.], ack 1449, win 69, options [nop,nop,TS val 70626138 ecr 3674161283], length 0
11:41:01.130776 IP 10.0.21.7.6380 > 10.0.21.5.42953: Flags [P.], seq 1449:2193, ack 7, win 57, options [nop,nop,TS val 3674161283 ecr 70626138], length 744
11:41:01.130782 IP 10.0.21.5.42953 > 10.0.21.7.6380: Flags [.], ack 2193, win 80, options [nop,nop,TS val 70626138 ecr 3674161283], length 0
11:41:04.453600 IP 10.0.21.5.42953 > 10.0.21.7.6380: Flags [F.], seq 7, ack 2193, win 80, options [nop,nop,TS val 70629461 ecr 3674161283], length 0
11:41:04.453694 IP 10.0.21.7.6380 > 10.0.21.5.42953: Flags [F.], seq 2193, ack 8, win 57, options [nop,nop,TS val 3674164606 ecr 70629461], length 0
11:41:04.453707 IP 10.0.21.5.42953 > 10.0.21.7.6380: Flags [.], ack 2194, win 80, options [nop,nop,TS val 70629461 ecr 3674164606], length 0

可以看到, eth2 和 bond1 抓到了同样的报文, 只是时间戳稍有不同, 这是正常的因为 redis 主机网 web 主机发的包的时间戳是固定的, 上面可以看到从 10.0.21.7 到 10.0.21.5 主机的时间戳都不变化, 而 web 主机到 redis 主机的时间戳则有一些不同, 基本都相差几微秒.

由此可以猜测出, tcpdump 使用 -i any 选项会把 eth2 和 bond1 的报文都抓取, 而两个网卡报文的时间戳有特别相近, 所以使用 wireshark 进行查看的时候会出现各种各样的提示. 以上述的截图为例进行说明:

1、tcp out of order

第一条为三次握手的第一个包, 因为是从 web 主机发出去的, 所以第一条应该是 bond1 网卡的 syn 报文, 紧跟的第二条则为 eth2 网卡的 syn 报文, 而且两个条目还相差 5 微秒, 对于 wireshark 而言第二条报文不是期待的 syn + ack, 时间戳也不一样, 所以就提示乱序. 事实上在 tcp 三次握手和四次回收的过程中是不会出现乱序的, 乱序只在传输数据报文的时候发生.

2、tcp Dup Ack

当乱序或丢包发生时,web 会收到一些 seq 号比期望大的包, web 每收到这种包就会 ack 一次期望的值提醒发送方, 于是就产生 Dup Ack, wireshark 则提示 tcp Dup Ack, 截图中则主要由于时间戳乱序引起了该提示.

3、tcp retransmission

正常情况下, 一个包丢失以及没有后续的 Dup Ack 包, 则会进行超时重传操作. 而在本文的示例中, 由于传输的数据包(P 标志)有重复的而且seq, ack 这些都一样所以 wireshark 会直接提示该包为重传的包.

总结

总体来看, 出现重复报文算是虚惊一场, 不是内核的 bug 也不是 tcpdump 的 bug, 而是 bond 的机制需要经过多次转发处理, 这也能解释为什么 redis 主机上的报文都是正常的. 如上所述, 如果在 centos 5 中抓包应该就不会出现本文的问题. 所以出现重复报文只是一种现象, 不会影响本地网卡的流量, 也不会影响业务的交互. 或许 tcpdump 会在将来修复这个不算错误的漏洞, linux 也可能换一种机制来完成数据的传输.

利用denyhosts防止服务器被ssh暴力破解

博主我新买的VPS建站没几天,心想应该没人动我的歪主意就一直没太注意服务器安全。今天用iftop查看到有几个IP一直连接中,而我的blog里的流量统计页面访问数量却没有新增,很明显这不是在访问blog。通过netstat -anp 可以查看到这几个IP,尤其有个IP还不断更换端口一直尝试访问我的22端口,能用22端口无非是ssh终端连接。心里一想百分百99%是尝试暴力破解,事不宜迟赶紧封锁。

未分类

一查乌克兰的,还不能ping它,也就是说单向访问

未分类

通过iftop能实时查看流量连接情况

未分类

tail -f /var/log/secure 可以查看ssh登陆失败的信息,证实了193.201.224.199这家伙在尝试暴力破解

利用denyhosts 防止暴力破解ssh

DenyHosts是Python语言写的一个程序,它会分析sshd的日志文件(/var/log/secure),当发现重 复的攻击时就会记录IP到/etc/hosts.deny文件,从而达到自动屏IP的功能。

思路:利用读取到/var/log/secure登陆失败的IP信息,我们可以设定一个规则,如果登陆失败超过多少次,我们把这个IP写到 /etc/hosts.deny中,拒绝访问!

1、先把始终允许的IP填入 /etc/hosts.allow ,因为自己访问超过了次数那就尴尬了。不过一般购买的VPS都有后台可以更改ROOT登陆密码,所以这个步骤也可以不设置。

# vim /etc/hosts.allow
   # sshd:IP:allow

2、安装DenyHosts 官方网站为:http://denyhosts.sourceforge.net

# tar -zxvf DenyHosts-2.6.tar.gz
# cd DenyHosts-2.6
# python setup.py install 

3、配置

# cd /usr/share/denyhosts/ 
# cp denyhosts.cfg-dist denyhosts.cfg 
# vi denyhosts.cfg
SECURE_LOG = /var/log/secure  #要读取安全日志路径,默认的不管它
HOSTS_DENY = /etc/hosts.deny #将阻止IP写入到hosts.deny,默认的不管它
PURGE_DENY = 1d #设定过多久后清除已阻止IP (m=分钟,h=小时,d=天,w=周)
BLOCK_SERVICE = sshd #阻止服务名
DENY_THRESHOLD_INVALID = 5 #允许无效用户登录失败的次数
DENY_THRESHOLD_VALID = 10 #允许普通用户登录失败的次数
DENY_THRESHOLD_ROOT = 3 #允许root登录失败的次数
(上面3个登陆失败的次数建议不要设定太苛刻,以免自己输错了被列入黑名单了)
DENY_THRESHOLD_RESTRICTED = 1 #设定 deny host 写入到该资料夹
WORK_DIR = /usr/local/share/denyhosts/data #将deny的host或ip纪录到Work_dir中
SUSPICIOUS_LOGIN_REPORT_ALLOWED_HOSTS=YES #假如设定为YES,那么已经设为白名单中的IP登陆失败也会被设为可疑,也会被列入黑名单中,设定NO的意思就相反。
HOSTNAME_LOOKUP=YES #如果可以的话记录登陆失败的hostname
LOCK_FILE = /var/lock/subsys/denyhosts #将DenyHOts启动的pid纪录到LOCK_FILE中,已确保服务正确启动,防止同时启动多个服务。 
HOSTNAME_LOOKUP=NO #是否做域名反解 
ADMIN_EMAIL = #设置管理员邮件地址 
DAEMON_LOG = /var/log/denyhosts #自己的日志文件 
DAEMON_PURGE = 10m #该项与PURGE_DENY 设置成一样,也是清除hosts.deniedssh 用户的时间。 

4、设置启动脚本

# cp daemon-control-dist daemon-control
# chown root daemon-control
# chmod 700 daemon-control
完了之后执行daemon-contron start就可以了。
# ./daemon-control start
如果要使DenyHosts每次重起后自动启动还需做如下设置:
# ln -s /usr/share/denyhosts/daemon-control /etc/init.d/denyhosts
# chkconfig –add denyhosts
# chkconfig denyhosts on

然后就可以启动了:

# service denyhosts start
# ps -ef|grep denyhosts #检查是否启动

未分类

 # vim /etc/hosts.deny #查看内是否有禁止的IP,有的话说明已经成功了。

未分类

ubuntu安装openssh启用SSH登录服务器

默认情况下,ubuntu系统在安装完成之后,不会安装openssh相关的软件包,所有我们在远程ssh登录到ubuntu系统的时候会失败。

本文将会讲述如何来安装openssh相关软件包来解决无法远程ssh登录到ubuntu系统的问题。

1、执行下面的apt-get命令安装openssh相关软件包。

apt-get install openssh*

ubuntutest@ubuntutest:~$ apt-get install openssh*
E: 无法打开锁文件 /var/lib/dpkg/lock - open (13: Permission denied)
E: 无法对状态列表目录加锁(/var/lib/dpkg/),请查看您是否正以 root 用户运行?
ubuntutest@ubuntutest:~$ sudo apt-get install openssh*
[sudo] password for ubuntutest:
正在读取软件包列表... 完成
正在分析软件包的依赖关系树
正在读取状态信息... 完成
注意,根据Glob 'openssh*' 选中了 'openssh-known-hosts'
注意,根据Glob 'openssh*' 选中了 'openssh-blacklist-extra'
注意,根据Glob 'openssh*' 选中了 'openssh-blacklist'
注意,根据Glob 'openssh*' 选中了 'openssh-sftp-server'
注意,根据Glob 'openssh*' 选中了 'openssh-server'
注意,根据Glob 'openssh*' 选中了 'openssh-client'
注意,根据Glob 'openssh*' 选中了 'openssh-client-ssh1'
openssh-blacklist 已经是最新版 (0.4.1+nmu1)。
openssh-blacklist-extra 已经是最新版 (0.4.1+nmu1)。
openssh-known-hosts 已经是最新版 (0.6.2-1)。
openssh-client 已经是最新版 (1:7.2p2-4ubuntu2.2)。
openssh-server 已经是最新版 (1:7.2p2-4ubuntu2.2)。
openssh-sftp-server 已经是最新版 (1:7.2p2-4ubuntu2.2)。
openssh-client-ssh1 已经是最新版 (1:7.2p2-4ubuntu2.2)。
升级了 0 个软件包,新安装了 0 个软件包,要卸载 0 个软件包,有 128 个软件包未被升级。

2、查看sshd 服务是否启动,执行下面的命令

ps aux | grep sshd

ubuntutest@ubuntutest:~$ ps aux | grep sshd
root      1129  0.0  0.2  65520  5272 ?        Ss   10:23   0:00 /usr/sbin/sshd -D
root      1304  0.0  0.3  95400  7012 ?        Ss   10:24   0:00 sshd: root@pts/0

3、使用非root 用户来验证是否能够SSH远程登录到该Ubuntu系统

ubuntutest@ubuntutest:~$ ssh ubuntutest@localhost
The authenticity of host 'localhost (::1)' can't be established.
ECDSA key fingerprint is SHA256:gy+Rueth1RoavXu2zvs230nZUiCSRIcQZvivSDixfn4.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'localhost' (ECDSA) to the list of known hosts.
ubuntutest@localhost's password:
Welcome to Ubuntu 16.04.2 LTS (GNU/Linux 4.4.0-62-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

128 个可升级软件包。
62 个安全更新。

Last login: Wed Aug  2 10:11:14 2017
ubuntutest@ubuntutest:~$

CentOS 7修改SSH端口并配置iptables防火墙

摘要

昨天我连接 ssh 的时候看到,有 600 多次来自某个阿里云服务器的失败登录,然后赶紧改了密码,顺便再改一下 ssh 的允许端口……万万没想到,网上搜到的大部分教程均不能生效。在找了几个关键信息后得到比较完全的 CentOS 7 SSH 端口修改步骤。

修改 sshd_config 端口

$ vi /etc/ssh/sshd_config

取消 #Port 22 的注释,在下一行添加你需要修改的新端口 Port 2048。(这里不删除 22 端口是为了防止修改后新端口无法访问,造成无法用 ssh 连接服务器。)

Port 22  
Port 2048  

修改保存 sshd_config 文件后重启 sshd 服务:

$  systemctl restart sshd

退出 ssh 会话后,再用新的端口连接:

$ ssh -p 2048 [email protected]
ssh: connect to host 0.0.0.0 port 2048: Connection refused  

好吧,native 了……对于 CentOS 7 这一套修改端口的方法已经不能生效了。

打开 SELinux 端口

SELinux 全称 Security Enhanced Linux (安全强化 Linux),是 MAC (Mandatory Access Control,强制访问控制系统)的一个实现,目的在于明确的指明某个进程可以访问哪些资源(文件、网络端口等)。

对于 ssh,SELinux 默认只允许 22 端口,我们可以用 SELinux 管理配置工具 semanage,来修改 ssh 可访问的端口。

安装 semanage 工具

$ yum provides semanage
$ yum -y install policycoreutils-python

为 ssh 打开 2048 端口

# 为 ssh 添加新的允许端口
$ semanage port -a -t ssh_port_t -p tcp 2048
# 查看当前 SELinux 允许的端口
$ semanage port -l | grep ssh
ssh_port_t                     tcp      2048, 22  

错误处理

当 SELINUX 配置为禁用状态时,使用 semanage 会报错提示无法读取 policy 文件:

SELinux:  Could not downgrade policy file /etc/selinux/targeted/policy/policy.30, searching for an older version.  
SELinux:  Could not open policy file <= /etc/selinux/targeted/policy/policy.30:  No such file or directory  
/sbin/load_policy:  Can't load policy:  No such file or directory
libsemanage.semanage_reload_policy: load_policy returned error code 2. (No such file or directory).  
FileNotFoundError: [Errno 2] No such file or directory  

修改 /etc/selinux/config 配置,启用 SELinux:

$ vi /etc/selinux/config
SELINUX=permissive  
# 重启服务器
$ init 6
# 重启后查看 SELinux 状态
$ sestatus
# if it shows disable, you can run
$ load_policy -qi

检查配置

$ semanage port -a -t ssh_port_t -p tcp 2048
$ semanage port -l | grep ssh
ssh_port_t                     tcp      2048, 22  
# 重启 ssh 服务
systemctl restart sshd  

注:semange 不能禁用 ssh 的 22 端口:

$ semanage port -d -t ssh_port_t -p tcp 22
ValueError: 在策略中定义了端口 tcp/22,无法删除。  

配置防火墙 firewalld

启用防火墙 && 查看防火墙状态

$ systemctl enable firewalld
$ systemctl start firewalld
$ systemctl status firewalld
● firewalld.service - firewalld - dynamic firewall daemon
   Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled)
   Active: active (running) since 二 2016-12-20 02:12:59 CST; 1 day 13h ago
 Main PID: 10379 (firewalld)
   CGroup: /system.slice/firewalld.service
           └─10379 /usr/bin/python -Es /usr/sbin/firewalld --nofork --nopid
$ firewall-cmd --state
running  

查看防火墙当前「默认」和「激活」zone(区域)

$ firewall-cmd --get-default-zone
public  
$ firewall-cmd --get-active-zones
public  
  interfaces: eth0 eth1

若没有激活区域的话,要执行下面的命令。

激活 public 区域,增加网卡接口

$ firewall-cmd --set-default-zone=public
$ firewall-cmd --zone=public --add-interface=eth0
success  
$ firewall-cmd --zone=public --add-interface=eth1
success  

为 public zone 永久开放 2048/TCP 端口:

# 以防新端口不生效,先把 22 端口暴露
$ firewall-cmd --permanent --zone=public --add-port=22/tcp
$ firewall-cmd --permanent --zone=public --add-port=2048/tcp
success  
# 重载防火墙
$ firewall-cmd --reload
# 查看暴露端口规则
$ firewall-cmd --permanent --list-port
443/tcp 80/tcp 22/tcp 2048/tcp  
$ firewall-cmd --zone=public --list-all
public (default, active)  
  interfaces: eth0 eth1
  sources:
  services: dhcpv6-client ssh
  ports: 443/tcp 80/tcp 22/tcp 2048/tcp
  masquerade: no
  forward-ports:
  icmp-blocks:
  rich rules:

退出 ssh 后,尝试连接新端口

$ ssh -p 2048 [email protected]

成功登录的话,就可以做收尾工作了。

禁用 22 端口

删除 ssh 允许端口

$ vi /etc/ssh/sshd_config
#Port 22
Port 2048  
$ systemctl restart sshd
# 用 ss 命令检查 ssh 监听的端口,没有 22 证明修改成功
$ ss -tnlp | grep ssh
LISTEN     0      128                       *:2048                    *:*      users:(("sshd",18233,3))  

防火墙移除 22 端口

$ firewall-cmd --permanent --zone=public --remove-port=22/tcp
success  
$ firewall-cmd --reload
$ firewall-cmd --permanent --list-port
443/tcp 80/tcp 2048/tcp  

ssh 取消监听 22 端口,就已经配置好了,防火墙只不过是在 ssh 外多一层访问限制。如果要做的更好还可以将 22 端口的访问流量转向访问者本地:

$ firewall-cmd --permanen --zone=public --add-forward-port=port=22:proto=tcp:toport=22:toaddr=127.0.0.1
# 配置后重载防火墙,用 ssh -p 22 [email protected] 就会访问到自己本地的 22 端口。

若要删除 forward 配置,可以:

$ firewall-cmd --permanen --zone=public --remove-forward-port=port=22:proto=tcp:toport=22:toaddr=127.0.0.1

检验修改 ssh 端口是否成功:

$ ssh -p 22 [email protected]
# 无响应,因为转到了本地的 22 端口
# 若防火墙未 forward 连接,则会回显 "ssh: connect to host {ip} port 22: Connection refused"
$ ssh -p 2048 [email protected]
# 成功 success

修复使用证书无法ssh登录服务器的问题

引入

服务器一直 ssh 登录正常,突然有段时间发现不能登录了,查看原因

从客户端查看

ssh -v user@host

查看过后,发现问题不在客户端上面

从服务器上查找原因

首先查看 .ssh 目录 和 authorized_keys 的权限

.ssh 为 700,authorized_keys 为 600,正常

查看日志

tailf /var/log/secure

返回结果

Authentication refused: bad ownership or modes for directory /root

查看 root 文件夹权限

发现文件夹被改成了 777,改回 755 即可正常登录