wordpress开启Redis扩展教程

本教程中使用到的是宝塔面板(宝塔面板,小白建站的不二之选)Wordpress站点如果时间久了,流量大了,文章多了,前台后台都会变得龟速!

利用Redis将WordPress页面直接缓存在服务器的内存中,这样在避免了PHP重复执行操作的同时,内存的极速响应能够最大限度地提升Wordpress页面的访问速度,实际测试发现页面执行时间可以降低到0.00X秒级别,比没有使用Redis缓存提升几倍甚至十几倍以上。

利用宝塔面板非常简单的就能安装配置成功!

1、首先我们需要进入宝塔面板安装Redis扩展

未分类

2、下载我们要用到的PHP文件

  • PHP文件下载

https://www.ddblog.cc/wp-content/themes/begin/inc/go.php?url=https://page22.ctfile.com/fs/12214022-213374766

  • 下载解压以后我们会得到predis.php和wp-index-redis.php两个文件

  • 上传这两个文件到Wordpress的根目录

3、配置修改及注意事项

  • 如果你想在页面上看到脚本执行时间和缓存加载时间

请打开wp-index-redis.php文件把

// change vars here
$cf = 0;                // set to 1 if you are using cloudflare
$debug = 0;             // set to 1 if you wish to see execution time and cache actions

修改为

// change vars here
$cf = 0;                // set to 1 if you are using cloudflare
$debug = 1;             // set to 1 if you wish to see execution time and cache actions

也就是把$debug = 0;修改为$debug = 1;

  • 如果你使用的是nginx,需要原来的根目录的index.php修改为为任意其它名字,把wp-index-redis.php重命名为index.php

  • 如果你使用的是Apache,则需要把.htaccess中出现的index.php替换成wp-index-redis.php,我命名的是下面图片的名字。

未分类

所有的操作完成后,你就可以刷新一下Wordpress页面,查看Redis缓存效果了

  • 如果开启了缓存插件或cdn请先关闭才能看到效果

删除某一个页面的缓存:发表评论、按下F5刷新、在URL后面加上?r=y回车。

删除整站页面缓存:登录到Wordpress后台,在任意URL后面加上?r=y回车。

wireshark使用教程及过滤语法总结

wireshark介绍

wireshark的官方下载网站: http://www.wireshark.org/

wireshark是非常流行的网络封包分析软件,功能十分强大。可以截取各种网络封包,显示网络封包的详细信息。

wireshark是开源软件,可以放心使用。

Wireshark是一款高效免费的网络抓包分析工具。它可以捕获并描述网线当中的数据,如同使用万用表测量电压一样直观地显示出来。在网络分析软件领域,大多数软件要么晦涩难懂要么价格昂贵,Wireshark改变了这样的局面,它的最大特点就是免费、开源和多平台支持。

Wireshark几乎可以运行于所有流行的操作平台,如MS Windows、Mac OS、Linux、FreeBSD、HP-UX、NetBSD、Solaris/i386、Solaris/sparc等等。尽管Wireshark可以在很多操作平台使用,但它支持的传输媒介主要是Ethernet。只有Linux平台下Wireshark支持802.11及Token Ring、FDDI和ATM。

Wireshark能够对大部分局域网协议进行解析,具有界面简单、操作方便、实时显示捕获数据的优点。但Wireshark并不具有分析功能,当一个网络发生异常的时候,Wireshark只会记录数据,它仅仅是一个测量工具,并不能操作网络,不发送数据包或者做其它的主动动作。

使用wireshark的人必须了解网络协议,否则就看不懂wireshark了。

Wireshark不能做的

为了安全考虑,wireshark只能查看封包,而不能修改封包的内容,或者发送封包。

什么人会用到wireshark

  1. 网络管理员会使用wireshark来检查网络问题

  2. 软件测试工程师使用wireshark抓包,来分析自己测试的软件

  3. 从事socket编程的工程师会用wireshark来调试

  4. 听说,华为,中兴的大部分工程师都会用到wireshark。

总之跟网络相关的东西,都可能会用到wireshark.

wireshark 开始抓包

wireshark是捕获机器上的某一块网卡的网络包,当你的机器上有多块网卡的时候,你需要选择一个网卡。

点击Caputre->Interfaces.. 出现下面对话框,选择正确的网卡。然后点击”Start”按钮, 开始抓包

未分类

未分类

Wireshark 窗口介绍

未分类

WireShark 主要分为这几个界面

  1. Display Filter(显示过滤器),用于过滤
  2. Packet List Pane(封包列表),显示捕获到的封包,有源地址和目标地址,端口号。颜色不同,代表
  3. Packet Details Pane(封包详细信息), 显示封包中的字段
  4. Dissector Pane(16进制数据)
  5. Miscellanous(地址栏,杂项)

Wireshark 显示过滤

未分类

使用过滤是非常重要的, 初学者使用wireshark时,将会得到大量的冗余信息,在几千甚至几万条记录中,以至于很难找到自己需要的部分。搞得晕头转向。

过滤器会帮助我们在大量的数据中迅速找到我们需要的信息。

过滤器有两种,

  • 一种是显示过滤器,就是主界面上那个,用来在捕获的记录中找到所需要的记录

  • 一种是捕获过滤器,用来过滤捕获的封包,以免捕获太多的记录。 在Capture -> Capture Filters 中设置

保存过滤

在Filter栏上,填好Filter的表达式后,点击Save按钮, 取个名字。比如”Filter 102″,

Filter栏上就多了个”Filter 102″ 的按钮。

过滤器的区别

  • 捕捉过滤器(CaptureFilters):用于决定将什么样的信息记录在捕捉结果中。需要在开始捕捉前设置。
  • 显示过滤器(DisplayFilters):在捕捉结果中进行详细查找。他们可以在得到捕捉结果后随意修改。
  • 那么我应该使用哪一种过滤器呢?
  • 两种过滤器的目的是不同的。
  • 捕捉过滤器是数据经过的第一层过滤器,它用于控制捕捉数据的数量,以避免产生过大的日志文件。
  • 显示过滤器是一种更为强大(复杂)的过滤器。它允许您在日志文件中迅速准确地找到所需要的记录。
  • 两种过滤器使用的语法是完全不同的。

捕捉过滤器

语法:Protocol, Direction, Host(s), Value, Logical, Operations, Other expression

例子: tcp, dst, 10.1.1.180, and, tcp dst 10.2.2.2 3128

Protocol(协议):

可能的值: ether, fddi, ip, arp, rarp, decnet, lat, sca, moprc, mopdl, tcp and udp.

如果没有特别指明是什么协议,则默认使用所有支持的协议。

Direction(方向):

可能的值: src, dst, src and dst, src or dst

如果没有特别指明来源或目的地,则默认使用 “src or dst” 作为关键字。

例如,”host 10.2.2.2″与”src or dst host 10.2.2.2″是一样的。

Host(s):

可能的值: net, port, host, portrange.

如果没有指定此值,则默认使用”host”关键字。

例如,”src 10.1.1.1″与”src host 10.1.1.1″相同。

Logical Operations(逻辑运算):

可能的值:not, and, or.

否(“not”)具有最高的优先级。或(“or”)和与(“and”)具有相同的优先级,运算时从左至右进行。

例如

“not tcp port 3128 and tcp port 23″与”(not tcp port 3128) and tcp port 23″相同。

“not tcp port 3128 and tcp port 23″与”not (tcp port 3128 and tcp port 23)”不同。

过滤表达式的规则

表达式规则

1、协议过滤

比如TCP,只显示TCP协议。

2、IP 过滤

比如

ip.src ==192.168.1.102 显示源地址为192.168.1.102,
ip.dst==192.168.1.102, 目标地址为192.168.1.102

3、端口过滤

tcp.port ==80,  端口为80的

tcp.srcport == 80,  只显示TCP协议的愿端口为80的。

4、Http模式过滤

http.request.method=="GET",   只显示HTTP GET方法的。

5、逻辑运算符为 AND/ OR

常用的过滤表达式

过滤表达式                                        用途

http                                             只查看HTTP协议的记录
ip.src ==192.168.1.102 or ip.dst==192.168.1.102  源地址或者目标地址是192.168.1.102
eth.addr== 00:11:22:33:44:55                     //过滤目标或源地址是00:11:22:33:44:55的数据包
tcp dst port 3128                                显示目的TCP端口为3128的封包。
ip src host 10.1.1.1                             显示来源IP地址为10.1.1.1的封包。

host 10.1.2.3                                    显示目的或来源IP地址为10.1.2.3的封包。
src portrange 2000-2500                          显示来源为UDP或TCP,并且端口号在2000至2500范围内的封包。


src host 10.7.2.12 and not dst net 10.200.0.0/16 显示来源IP地址为10.7.2.12,但目的地不是10.200.0.0/16的封包。

(src host 10.4.1.12 or src net 10.6.0.0/16) and tcp dst portrange 200-10000 and dst net 10.0.0.0/8  
                                                 显示来源IP为10.4.1.12或者来源网络为10.6.0.0/16,目的地TCP端口号在200至10000之间,并且目的位于网络 10.0.0.0/8内的所有封包。

src net 192.168.0.0/24

src net 192.168.0.0 mask 255.255.255.0           显示来源IP地址为10.1.1.1的封包。

注意事项:

当使用关键字作为值时,需使用反斜杠“/”。

“ether proto /ip” (与关键字”ip”相同).

这样写将会以IP协议作为目标。

“ip proto /icmp” (与关键字”icmp”相同).

这样写将会以ping工具常用的icmp作为目标。

可以在”ip”或”ether”后面使用”multicast”及”broadcast”关键字。

当您想排除广播请求时,”no broadcast”就会非常有用。

Protocol(协议):

您可以使用大量位于OSI模型第2至7层的协议。点击”Expression…”按钮后,您可以看到它们。

比如:IP,TCP,DNS,SSH

String1, String2 (可选项):

协议的子类。

点击相关父类旁的”+”号,然后选择其子类。

Comparison operators (比较运算符):

可以使用6种比较运算符:

英文写法:   C语言写法:  含义:
eq          ==          等于
ne          !=          不等于
gt          >           大于
lt          <           小于
ge          >=          大于等于
le          <=          小于等于

Logical expressions(逻辑运算符):

英文写法:   C语言写法:  含义:
and         &&          逻辑与
or          ||          逻辑或
xor         ^^          逻辑异或
not         !           逻辑非

显示过滤器

语法: Protocol, String 1, String 2, Comparison operator, Value, Logical Operations, Other expression

例子: ftp, passive, ip, ==, 10.2.3.4, xor, icmp.type

例子:

snmp || dns || icmp                         显示SNMP或DNS或ICMP封包。
ip.addr == 10.1.1.1                         显示来源或目的IP地址为10.1.1.1的封包。
ip.src != 10.1.2.3 or ip.dst != 10.4.5.6    显示来源不为10.1.2.3或者目的不为10.4.5.6的封包。

换句话说,显示的封包将会为:

  • 来源IP:除了10.1.2.3以外任意;目的IP:任意
    以及
  • 来源IP:任意;目的IP:除了10.4.5.6以外任意
ip.src != 10.1.2.3 and ip.dst != 10.4.5.6   显示来源不为10.1.2.3并且目的IP不为10.4.5.6的封包。

换句话说,显示的封包将会为:

  • 来源IP:除了10.1.2.3以外任意;同时须满足,目的IP:除了10.4.5.6以外任意
tcp.port == 25          显示来源或目的TCP端口号为25的封包。
tcp.dstport == 25       显示目的TCP端口号为25的封包。
tcp.flags               显示包含TCP标志的封包。
tcp.flags.syn == 0×02   显示包含TCP SYN标志的封包。

如果过滤器的语法是正确的,表达式的背景呈绿色。如果呈红色,说明表达式有误。

wireshark过滤匹配表达式实例

匹配过滤HTTP的请求URI中含有”.gif”字符串,并且以.gif结尾(4个字节)的http请求数据包($是正则表达式中的结尾表示符)

http.request.uri matches ".gif$" 

注意区别:http.request.uri contains “.gif$” 与此不同,contains是包含字符串”.gif$”(5个字节)。匹配过滤HTTP的请求URI中含有”.gif$”字符串的http请求数据包(这里$是字符,不是结尾符)

搜索过滤MAC地址前3个字节是0x001e4f的数据包。

eth.addr[0:3]==00:1e:4f 

搜索按条件过滤udp的数据段payload(数字8是表示udp头部有8个字节,数据部分从第9个字节开始udp[8:])

udp[8]==14                        (14是十六进制0x14)匹配payload第一个字节0x14的UDP数据包

udp[8:2]==14:05                    可以udp[8:2]==1405,且只支持2个字节连续,三个以上须使用冒号:分隔表示十六进制。 (相当于 udp[8]==14 and udp[9]==05,1405是0x1405)

udp[8:3]==22:00:f7                 但是不可以udp[8:3]==2200f7

udp[8:4]==00:04:00:2a,            匹配payload的前4个字节0x0004002a

而udp contains 7c:7c:7d:7d         匹配payload中含有0x7c7c7d7d的UDP数据包,不一定是从第一字节匹配。

udp[8:4] matches "\x14\x05\x07\x18"

udp[8:] matches "^\x14\x05\x07\x18\x14"

搜索按条件过滤tcp的数据段payload(数字20是表示tcp头部有20个字节,数据部分从第21个字节开始tcp[20:])

tcp[20:] matches "^GET [ -~]*HTTP/1.1\x0d\x0a"

等同http matches "^GET [ -~]*HTTP/1.1\x0d\x0a"


tcp[20:] matches "^GET (.*?)HTTP/1.1\x0d\x0a"

tcp[20:] matches "^GET (.*?)HTTP/1.1\x0d\x0a[\x00-\xff]*Host: (.*?)pplive(.*?)\x0d\x0a"

tcp[20:] matches "^GET (.*?)HTTP/1.1\x0d\x0a[\x00-\xff]*Host: "

tcp[20:] matches "^POST / HTTP/1.1\x0d\x0a[\x00-\xff]*\x0d\x0aConnection: Keep-Alive\x0d\x0a\x0d\x0a"

检测SMB头的smb标记,指明smb标记从tcp头部第24byte的位置开始匹配。

tcp[24:4] == ff:53:4d:42

检测SMB头的smb标记,tcp的数据包含十六进制ff:53:4d:42,从tcp头部开始搜索此数据。

tcp contains ff:53:4d:42

tcp matches "\xff\x53\x4d\x42"

检测tcp含有十六进制01:bd,从tcp头部开始搜索此数据。

tcp matches "\x01\xbd"

检测MS08067的RPC请求路径

tcp[179:13] == 00:5c:00:2e:00:2e:00:5c:00:2e:00:2e:00
                 .     .          .     .

封包列表(Packet List Pane)

封包列表的面板中显示,编号,时间戳,源地址,目标地址,协议,长度,以及封包信息。 你可以看到不同的协议用了不同的颜色显示。

你也可以修改这些显示颜色的规则, View ->Coloring Rules.

未分类

封包详细信息 (Packet Details Pane)

这个面板是我们最重要的,用来查看协议中的每一个字段。

各行信息分别为

Frame:                          物理层的数据帧概况

Ethernet II:                    数据链路层以太网帧头部信息

Internet Protocol Version 4:    互联网层IP包头部信息

Transmission Control Protocol:  传输层T的数据段头部信息,此处是TCP

Hypertext Transfer Protocol:    应用层的信息,此处是HTTP协议

未分类

TCP包的具体内容

从下图可以看到wireshark捕获到的TCP包中的每个字段。

未分类

实例分析TCP三次握手过程

看到这, 基本上对wireshak有了初步了解, 现在我们看一个TCP三次握手的实例

未分类

这图我都看过很多遍了, 这次我们用wireshark实际分析下三次握手的过程。

打开wireshark, 打开浏览器输入 http://www.cnblogs.com/tankxiao

在wireshark中输入http过滤, 然后选中GET /tankxiao HTTP/1.1的那条记录,右键然后点击”Follow TCP Stream”,

这样做的目的是为了得到与浏览器打开网站相关的数据包,将得到如下图

未分类

图中可以看到wireshark截获到了三次握手的三个数据包。第四个包才是HTTP的, 这说明HTTP的确是使用TCP建立连接的。

第一次握手数据包

客户端发送一个TCP,标志位为SYN,序列号为0, 代表客户端请求建立连接。 如下图

未分类

第二次握手的数据包

服务器发回确认包, 标志位为 SYN,ACK. 将确认序号(Acknowledgement Number)设置为客户的I S N加1以.即0+1=1, 如下图

未分类

第三次握手的数据包

客户端再次发送确认包(ACK) SYN标志位为0,ACK标志位为1.并且把服务器发来ACK的序号字段+1,放在确定字段中发送给对方.并且在数据段放写ISN的+1, 如下图:

未分类

就这样通过了TCP三次握手,建立了连接。

通过克隆虚拟机磁盘实现快速迁移VirtualBox虚拟机

Windos server上使用VirualBox迁移虚拟机时,可以使用自带的导入导出功能,但是当虚拟机比较大超过几十个G的容量,就会特别慢。一般比较快捷的方式是直接克隆虚拟机磁盘,然后复制虚拟机磁盘到新的机器上,创建一个新的虚拟机,选中克隆的磁盘。

查看虚拟机虚拟磁盘位置:

克隆磁盘命令,使用安装目录下的VBoxManage.exe执行克隆,命令行执行:

C:>F:VirtualBoxVBoxManage clonehd F:win2008_1win2008_1-disk1.vdi F:VM-BACK
win2008_1-247.vdi

克隆完成后会重新生成一个新的UUID,如果直接拷贝原来的虚拟磁盘启动会报错。

在新的虚拟机中创建此虚拟机:

使用克隆的磁盘启动:

启动虚拟机即可,第一次加载会比较慢。克隆虚拟机也是使用相同的方式。

Varnish配置示例说明

Varnish与一般服务器软件类似,分为master(management)进程和child(worker,主要做cache的工作)进程。master进程读入命令,进行一些初始化,然后fork并监控child进程。child进程分配若干线程进行工作,主要包括一些管理线程和很多woker线程。

varnish架构图:

未分类

varnish的配置示例:

1、主配置文件的修改:

varnish作为web服务的反向代理服务器,要监听在80端口,因此要修改配置文件:

vim /etc/varnish/varnish.params

未分类

记得创建你所指定的路径:mkdir /data/varnish/cache

并将属主属组修改为varnish:chown -R varnish:varnish /data/varnish/cache

2、配置后端主机:

vim /etc/varnish/default.vcl

未分类

后端主机安装httpd作为测试

重读default.vcl文件:varnsih_reload_vcl 或者通过交互式方式重载配置varnish配置:varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082

命令行下:

vcl.load <configname> <filename> 重载配置文件default.vcl(filename),并将其命名
vcl.use <configname>使用新生成的配置

网页通过http访问此varnish主机得到:

未分类

说明后端主机配置成功

3、VCL: ”域“专有类型的配置语言

VCL有多个状态引擎,状态之间存在相关性,但状态引擎彼此间互相隔离;每个状态引擎可使用return(x)指明关联至哪个下一级引擎;每个状态引擎对应于vcl文件中的一个配置段,即为subroutine

变量类型:

内建变量:

  • req.*:request,表示由客户端发来的请求报文相关;
  • bereq.*:由varnish发往BE主机的httpd请求相关;
  • resp.*:由varnish响应给client相关;
  • obj.*:存储在缓存空间中的缓存对象的属性;只读;

常用变量:

  • bereq.*, req.*:
  • bereq.http.HEADERS
  • bereq.request:请求方法;
  • bereq.url:请求的url;
  • bereq.proto:请求的协议版本;
  • bereq.backend:指明要调用的后端主机;
  • req.http.Cookie:客户端的请求报文中Cookie首部的值;
  • req.http.User-Agent ~ “chrome”
  • beresp.*, resp.*:
  • beresp.http.HEADERS
  • beresp.status:响应的状态码;
  • reresp.proto:协议版本;
  • beresp.backend.name:BE主机的主机名;
  • beresp.ttl:BE主机响应的内容的余下的可缓存时长;
  • obj.*
  • obj.hits:此对象从缓存中命中的次数 >1 表示命中;
  • obj.ttl:对象的ttl值
  • server.*
  • server.ip
  • server.hostname
  • client.*
  • client.ip

介绍几个VCL配置的实例:

(1)添加报文首部:vim default.vcl

obj.hits是内建变量,用于保存某缓存项的从缓存中命中的次数;

在sub vcl_deliver中添加

if (obj.hits>0) {

          set resp.http.X-Cache = “HIT via ” + server.ip;
   } else {
          set resp.http.X-Cache = “MISS via ” + server.ip;
   }

在交互式命令行下手动装载配置,并使用:

vcl.load  test default.vcl

vcl_use test

测试:

未分类

刷新网页,再次访问,会出现“HIT via….”字样

(2)强制对某类资源的请求不检查缓存,在请求报文中包含以/login|admin 为首的不查缓存,直接送到backend(后端服务器):

sub   vcl_recv {
       if (req.url ~ “(?i)^/(login|admin)”) {
          return(pass);
       }
    }

禁止以curl方式访问:

sub   vcl_recv {
       if (req.http.User-Agent ~ “(?i)^/curl”) {
          return(synth(403));
       }
    }

测试:

未分类

(3)对于特定类型的资源,例如公开的图片等,取消其私有标识(cookie),并强行设定其可以由varnish缓存的时长;

sub  vcl_backend_response {

     if (beresp.http.cache-control !~ “s-maxage”) {
        if (bereq.url ~ “(?i).(jpg|jpeg|png|gif|css|js)$”) {
           unset beresp.http.Set-Cookie;
           set beresp.ttl = 3600s;
        }
      }

}

(4)设置后端服务器日志中记录真实的客户端地址:

sub vcl_recv {
    if (req.restarts == 0) {
       if (req.http.X-Fowarded-For) {
          set req.http.X-Forwarded-For = req.http.X-Forwarded-For + “,” + client.ip;
    } else {
         set req.http.X-Forwarded-For = client.ip;
       }
    }

}

并在后端服务器中设置日志格式:

vim  /etc/httpd/conf/httpd.conf

  将格式修改为:LogFormat  “%{X-Forwarded-For}i  %l  %u  %t  ”%r“ %>s %b  ” %{Referer}i” ”%{User-Agent}i”” combined

测试:

未分类

(5)访问控制

缓存对象的修剪:purge, ban

未分类

测试:缓存后,用curl查看网页信息

未分类

裁剪,将缓存删除:curl -X PURGE http://172.16.252.187/index.html

未分类

Banning 在交互式模式 用于临时按需要清理缓存

示例:

未分类

在配置文件中,使用ban()函数,相当于purge的用法;示例:

if (req.method == “BAN”) {
      ban(“req.http.host == ” + req.http.host + ” && req.url == ” + req.url);
      return(synth(200, “Ban added”));
}

清空一个域的缓存:

ban req.url == / && req.http.host ~ “ilinux.io”(慎用)

(6)负载均衡,Director:

使用前需要导入:import directors

示例:

未分类

未分类

动静分离配置示例:

         import directors

        …..
        backend imgsrv1 {
                .host = “192.168.251.11”;
                .port = “80”;
        }
        backend imgsrv2 {
                .host = “192.168.251.12”;
                .port = “80”;
        }
        backend appsrv1 {
                .host = “192.168.251.13”;
                .port = “80”;
        }
        backend appsrv2 {
                .host = “192.168.251.14”;
                .port = “80”;
        }
    sub vcl_init {
                new imgsrvs = directors.random();
                imgsrvs.add_backend(imgsrv1,10);
                imgsrvs.add_backend(imgsrv2,20);
                new staticsrvs = directors.round_robin();
                appsrvs.add_backend(appsrv1);
                appsrvs.add_backend(appsrv2);
                new appsrvs = directors.hash();
                appsrvs.add_backend(appsrv1,1);
                appsrvs.add_backend(appsrv2,1);
        }
     sub vcl_recv {
                  if (req.url ~ “(?i).(css|js)$” {
                        set req.backend_hint = staticsrvs.backend();
                }
                if (req.url ~ “(?i).(jpg|jpeg|png|gif)$” {
                       set req.backend_hint = imgsrvs.backend();
                } else {
                        set req.backend_hint = appsrvs.backend(req.http.cookie);
                }
      } 

(7)基于cookie的session sticky:

sub vcl_init {
       new h = directors.hash();
       h.add_backend(one, 1); // backend ‘one’ with weight ‘1’
       h.add_backend(two, 1); // backend ‘two’ with weight ‘1’
}
sub vcl_recv {
       set req.backend_hint = h.backend(req.http.cookie);
} 

(8)健康状态检测:

.probe:定义健康状态检测方法;
.url:检测时要请求的URL,默认为”/”;
.request:发出的具体请求;
    .request =
          “GET /.healthtest.html HTTP/1.1”
          “Host: www.magedu.com”
          “Connection: close”
    .window:基于最近的多少次检查来判断其健康状态;
    .threshold:最近.window中定义的这么次检查中至有.threshhold定义的次数是成功的;
    .interval:检测频度;
    .timeout:超时时长;
    .expected_response:期望的响应码,默认为200;

配置示例:

未分类

显示健康状态信息,在交互式界面中:

未分类

4、性能调整:vim /etc/varnish/varnish.params

根据对网页的测试来适当调整数值

未分类

5、varnish日志

1>、varnishstat – Varnish Cache statistics 各种计数器
         -1 批次显示,只显示1次
         -1 -f FILED_NAME
         -l:可用于-f选项指定的字段名称列表;
# varnishstat -1 -f MAIN.cache_hit -f MAIN.cache_miss
# varnishstat -l -f MAIN -f MEMPOOL
2>、varnishtop – Varnish log entry ranking 将日志文件中相关数据逆序排序
              -1 Instead of a continously updated display, print the statistics once and exit.
              -i taglist,可以同时使用多个-i选项,也可以一个选项跟上多个标签;筛选
              -I <[taglist:]regex>
              -x taglist:排除列表
              -X <[taglist:]regex>
varnishtop -i RespStatus 查看响应码
3>、varnishlog – Display Varnish logs 查看实时日志
4>、 varnishncsa – Display Varnish logs in Apache / NCSA combined log format 标准日志格式 

通过Tcpdump抓包 Wireshark分析 找出Wget文件下载失败的原因

在实际工作的过程中,服务程序每日开盘前需要依赖一下静态的文件进行初始化操作,类似版块文件、财务文件、除权文件等文件。由于下载工作比较简单,我们使用linux系统自带的wget,通过shell脚本的方式进行下载,脚本编写完成在测试过程中发现,随着下载频率的增加出现下载不成功的次数会增加,下载到的内容类似:

未分类

从上面的信息可以看到,等到的信息中有类似hs_cc_cookie,初步得出两个怀疑方向:

  • 难道是异常使用cookie导致请求直接返回?
  • 或者是请求被认为是CC攻击,安全设备直接返回了上面的上面的HTML内容??

基于上面的怀疑我们开始问题的排查。

问题排查

1、对wget的请求增加参数

在请求中增加 -S参数,打印详细的响应头,正常下载:

未分类

错误下载:

未分类

然后对请求 增加–no-cookies 参数 ,不使用 cookies ,增加参数后结果一样,多次下载继续出现问题,关于cookies可能性排除,难道真的是安全设备的问题?涉及到跨部门协调,需要拿出证据,那么只能出最后的绝招–网络抓包 ,结果绝对公平公正,童叟无欺。

抓包分析

说到抓包,不得不说说TCP的建立连接的“三次握手”和断开连接的“四次挥手”。
如果觉得有必要回顾一下的同学可以先看看“附录一”,里面会有详细的接收。了然于胸的同学我们继续问题排查。
通过TCPDUMP (LINUX 网络抓包工具)抓取正常下载和异常下载的网络包进行对比分析。

正常下载:

未分类

下载异常:

未分类

下面我们对下载异常的网络包进行分析:

  • 665号包说明服务器认为自己已经完成了服务请求,但客户端却没有主动关闭连接,于是在 20s 后只好主动将连接关闭。665号包显示 “TCP Previous segment not captured” 并且包中 “Seq=640” 而上一个 “Seq=1”我们确实有包没有抓到。

  • 666号包客户端在收到 “Seq=640” 后认为自己收到了一个乱序包,因此试图通过 “TCP Dup ACK 663#1” 和 “Ack=1” 试图让服务器重传缺失的 639 字节。

  • 667号包 出现 “TCP Out-Of-Order” ,产生该问题的原因一般:Packet 可能 Lost,所以重新传送造成。或存在Load Balance 之类的架构,晚送的封包却比早送的到达。

  • 671 号包开始说明客户端由于某种原因还进行了多次针对 “FIN” 的==“TCP Spurious Retransmission”== 。

TCP Previous segment not captured的问题一般有以下几种情况,我们来一一排查。
服务器CPU或网络压力导致上述问题(服务器没有压力)

未分类

  • 杀毒软件、恶意软件监测程序等导致问题(无此问题)。

  • 交换机、路由器和防火墙等网络层问题。

排查到这里,基本可以判断问题是由于网络层相关问题导致,接下来我们从以下两个方向进行问题排查。

1、查看防火墙出口带宽,整个出口我有200M 现在看看只有50M的峰值,和出口带宽无关

未分类

2、查看防火墙关于CC攻击的配置发现开启了IPS防御以及防病毒配置,对于单IP的访问频次过高时,认为是攻击行为会产生丢弃操作,当出现错误请求时有如下报错信息:

未分类

3、调整防火墙配置关闭CC防护,问题解决:

未分类

通过以上的排查我们得出结论:wget在频繁被执行时触发了防火墙的CC攻击防护机制,导致请求被截断从而出现了文章开头的问题。

解决方案:

  • 针对wget下载脚本:
    在增加 中wget 时增加–limit-rate=2048k (限制下载速度为2M)参数,限制下载速度,并增长两次下载过程中的sleep 时间间隔。

  • 针对防火墙:
    适当调高防火墙CC防护的rps值,避免正常请求被防火墙作为CC攻击屏蔽。

CentOS-6修改ssh端口并配置密钥登录

1、导入客户端密钥

$> vi ~/.ssh/authorized_keys #没有就新建
(复制客户端 ~/.ssh/id_rsa.pub 保存文件)
$> chmod 700 ~/.ssh/authorized_keys

2、禁止使用密码登录

$> vi /etc/ssh/sshd_config
修改为: PasswordAuthentication No 

3、修改默认端口

$> vi /etc/ssh/sshd_config
修改为: Port 11233
$> service sshd restart

4、开放端口防火墙权限

$> iptables -I INPUT 4 -p tcp -m state --state NEW -m tcp --dport 11233  -j ACCEPT
$> service iptables save

校验方式:客户端链接方式:

$> ssh [email protected] -p 11233

配置ssh密钥登录(无需密码)

配置密钥可使用户免密ssh方式登录服务器。

配置方法:

生成密钥对

$ ssh-keygen

此处可选择生成密钥的文件路径,默认为~/.ssh/id_rsa
密码为可选。

拷贝公钥到服务器

方式一:

$ ssh-copy-id -i ~/.ssh/id_rsa.pub xxx@host

方式二:

手动拷贝id_rsa.pub到服务器上,并收到添加认证文件。

$ cat id_rsa.pub >> ~/.ssh/authorized_keys

服务器配置

配置服务器上/etc/ssh/sshd_config文件,修改以下配置项

RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile  .ssh/authorized_keys

修改后,重启sshd服务,根据不同的系统使用相应命令

登录

直接登录

$ ssh user@host

如果配置成功,则不需要密码直接登录。

问题

配置成功后,仍提示密码输入

主要原因在于服务器上认证文件及所在目录的权限问题

详情排查步骤可查看文章配置ssh公钥登录提示还是输入密码

解决方法:

$ chmod 755 ~/home/xxxuser

解决ssh登录时很慢的问题

使用ssh客户端(如:putty)连接Linux服务器,可能会等待10-30秒才有提示输入密码。严重影响工作效率。登录很慢,登录上去后速度正常,这种情况主要有两种可能的原因:

1、 DNS反向解析问题

OpenSSH在用户登录的时候会验证IP,它根据用户的IP使用反向DNS找到主机名,再使用DNS找到IP地址,最后匹配一下登录的IP是否合法。如果客户机的IP没有域名,或者DNS服务器很慢或不通,那么登录就会很花时间。

解决办法:在目标服务器上修改sshd服务器端配置,并重启sshd

vi /etc/ssh/sshd_config
UseDNS no

2、 关闭ssh的gssapi认证

用ssh -v user@server 可以看到登录时有如下信息:

debug1: Next authentication method: gssapi-with-mic
debug1: Unspecified GSS failure. Minor code may provide more information

注:ssh -vvv user@server 可以看到更细的debug信息

解决办法:

修改sshd服务器端配置

vi /etc/ssh/ssh_config
GSSAPIAuthentication no

可以使用ssh -o GSSAPIAuthentication=no user@server登录

GSSAPI ( Generic Security Services Application Programming Interface) 是一套类似Kerberos 5的通用网络安全系统接口。该接口是对各种不同的客户端服务器安全机制的封装,以消除安全接口的不同,降低编程难度。但该接口在目标机器无域名解析时会有 问题

使用strace查看后发现,ssh在验证完key之后,进行authentication gssapi-with-mic,此时先去连接DNS服务器,在这之后会进行其他操作

[root@192-168-3-40 ~]# ssh -vvv [email protected]  
OpenSSH_5.3p1, OpenSSL 1.0.1e-fips 11 Feb 2013  
debug1: Reading configuration data /etc/ssh/ssh_config  
debug1: Applying options for *  
debug2: ssh_connect: needpriv 0  
debug1: Connecting to 192.168.3.44 [192.168.3.44] port 22.  
debug1: Connection established.  
debug1: permanently_set_uid: 0/0  
debug1: identity file /root/.ssh/identity type -1  
debug1: identity file /root/.ssh/identity-cert type -1  
debug1: identity file /root/.ssh/id_rsa type -1  
debug1: identity file /root/.ssh/id_rsa-cert type -1  
debug1: identity file /root/.ssh/id_dsa type -1  
debug1: identity file /root/.ssh/id_dsa-cert type -1  
debug1: identity file /root/.ssh/id_ecdsa type -1  
debug1: identity file /root/.ssh/id_ecdsa-cert type -1  
debug1: Remote protocol version 2.0, remote software version OpenSSH_5.3  
debug1: match: OpenSSH_5.3 pat OpenSSH*  
debug1: Enabling compatibility mode for protocol 2.0  
debug1: Local version string SSH-2.0-OpenSSH_5.3  
debug2: fd 3 setting O_NONBLOCK  
debug1: SSH2_MSG_KEXINIT sent  
debug3: Wrote 960 bytes for a total of 981  
debug1: SSH2_MSG_KEXINIT received  
debug2: kex_parse_kexinit: diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1  
debug2: kex_parse_kexinit: [email protected],[email protected],[email protected],[email protected],ssh-rsa,ssh-dss  
debug2: kex_parse_kexinit: aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,arcfour,[email protected]  
debug2: kex_parse_kexinit: aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,arcfour,[email protected]  
debug2: kex_parse_kexinit: hmac-md5,hmac-sha1,[email protected],hmac-sha2-256,hmac-sha2-512,hmac-ripemd160,[email protected],hmac-sha1-96,hmac-md5-96  
debug2: kex_parse_kexinit: hmac-md5,hmac-sha1,[email protected],hmac-sha2-256,hmac-sha2-512,hmac-ripemd160,[email protected],hmac-sha1-96,hmac-md5-96  
debug2: kex_parse_kexinit: none,[email protected],zlib  
debug2: kex_parse_kexinit: none,[email protected],zlib  
debug2: kex_parse_kexinit:  
debug2: kex_parse_kexinit:  
debug2: kex_parse_kexinit: first_kex_follows 0  
debug2: kex_parse_kexinit: reserved 0  
debug2: kex_parse_kexinit: diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1  
debug2: kex_parse_kexinit: ssh-rsa,ssh-dss  
debug2: kex_parse_kexinit: aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,arcfour,[email protected]  
debug2: kex_parse_kexinit: aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,arcfour,[email protected]  
debug2: kex_parse_kexinit: hmac-md5,hmac-sha1,[email protected],hmac-sha2-256,hmac-sha2-512,hmac-ripemd160,[email protected],hmac-sha1-96,hmac-md5-96  
debug2: kex_parse_kexinit: hmac-md5,hmac-sha1,[email protected],hmac-sha2-256,hmac-sha2-512,hmac-ripemd160,[email protected],hmac-sha1-96,hmac-md5-96  
debug2: kex_parse_kexinit: none,[email protected]  
debug2: kex_parse_kexinit: none,[email protected]  
debug2: kex_parse_kexinit:  
debug2: kex_parse_kexinit:  
debug2: kex_parse_kexinit: first_kex_follows 0  
debug2: kex_parse_kexinit: reserved 0  
debug2: mac_setup: found hmac-md5
debug1: kex: server->client aes128-ctr hmac-md5 none  
debug2: mac_setup: found hmac-md5  
debug1: kex: client->server aes128-ctr hmac-md5 none  
debug1: SSH2_MSG_KEX_DH_GEX_REQUEST(1024<1024<8192) sent  
debug1: expecting SSH2_MSG_KEX_DH_GEX_GROUP  
debug3: Wrote 24 bytes for a total of 1005  
debug2: dh_gen_key: priv key bits set: 120/256  
debug2: bits set: 506/1024  
debug1: SSH2_MSG_KEX_DH_GEX_INIT sent  
debug1: expecting SSH2_MSG_KEX_DH_GEX_REPLY  
debug3: Wrote 144 bytes for a total of 1149  
debug3: check_host_in_hostfile: host 192.168.3.44 filename /root/.ssh/known_hosts  
debug3: check_host_in_hostfile: host 192.168.3.44 filename /root/.ssh/known_hosts  
debug3: check_host_in_hostfile: match line 8  
debug1: Host '192.168.3.44' is known and matches the RSA host key.  
debug1: Found key in /root/.ssh/known_hosts:8  
debug2: bits set: 527/1024  
debug1: ssh_rsa_verify: signature correct  
debug2: kex_derive_keys  
debug2: set_newkeys: mode 1  
debug1: SSH2_MSG_NEWKEYS sent  
debug1: expecting SSH2_MSG_NEWKEYS  
debug3: Wrote 16 bytes for a total of 1165  
debug2: set_newkeys: mode 0  
debug1: SSH2_MSG_NEWKEYS received  
debug1: SSH2_MSG_SERVICE_REQUEST sent  
debug3: Wrote 48 bytes for a total of 1213  
debug2: service_accept: ssh-userauth  
debug1: SSH2_MSG_SERVICE_ACCEPT received  
debug2: key: /root/.ssh/identity ((nil))  
debug2: key: /root/.ssh/id_rsa ((nil))  
debug2: key: /root/.ssh/id_dsa ((nil))  
debug2: key: /root/.ssh/id_ecdsa ((nil))  
debug3: Wrote 64 bytes for a total of 1277  
debug1: Authentications that can continue: publickey,gssapi-keyex,gssapi-with-mic,password  
debug3: start over, passed a different list publickey,gssapi-keyex,gssapi-with-mic,password  
debug3: preferred gssapi-keyex,gssapi-with-mic,publickey,keyboard-interactive,password  
debug3: authmethod_lookup gssapi-keyex  
debug3: remaining preferred: gssapi-with-mic,publickey,keyboard-interactive,password  
debug3: authmethod_is_enabled gssapi-keyex  
debug1: Next authentication method: gssapi-keyex  
debug1: No valid Key exchange context  
debug2: we did not send a packet, disable method  
debug3: authmethod_lookup gssapi-with-mic  
debug3: remaining preferred: publickey,keyboard-interactive,password  
debug3: authmethod_is_enabled gssapi-with-mic  
debug1: Next authentication method: gssapi-with-mic  
debug3: Trying to reverse map address 192.168.3.44.  
debug1: Unspecified GSS failure.  Minor code may provide more information  
Cannot determine realm for numeric host address  

debug1: Unspecified GSS failure.  Minor code may provide more information  
Cannot determine realm for numeric host address  

debug1: Unspecified GSS failure.  Minor code may provide more information  


debug1: Unspecified GSS failure.  Minor code may provide more information  
Cannot determine realm for numeric host address  

debug2: we did not send a packet, disable method  
debug3: authmethod_lookup publickey  
debug3: remaining preferred: keyboard-interactive,password  
debug3: authmethod_is_enabled publickey  
debug1: Next authentication method: publickey  
debug1: Trying private key: /root/.ssh/identity  
debug3: no such identity: /root/.ssh/identity  
debug1: Trying private key: /root/.ssh/id_rsa  
debug3: no such identity: /root/.ssh/id_rsa  
debug1: Trying private key: /root/.ssh/id_dsa  
debug3: no such identity: /root/.ssh/id_dsa  
debug1: Trying private key: /root/.ssh/id_ecdsa  
debug3: no such identity: /root/.ssh/id_ecdsa  
debug2: we did not send a packet, disable method  
debug3: authmethod_lookup password  
debug3: remaining preferred: ,password  
debug3: authmethod_is_enabled password  
debug1: Next authentication method: password  
[email protected]'s password:

Linux自动批量配置SSH互信证书脚本

一、脚本

在主机上建立sh文件,名称随意,然后在文件中输入以下内容:

#!/bin/sh
DEST_USER=$1
PASSWORD=$2
HOSTS_FILE=$3
if [ $# -ne 3 ]; then
    echo "Usage:"
    echo "$0 remoteUser remotePassword hostsFile"
    exit 1
fi

SSH_DIR=~/.ssh
SCRIPT_PREFIX=./tmp
echo ===========================
# 1. prepare  directory .ssh
mkdir $SSH_DIR
chmod 700 $SSH_DIR

# 2. generat ssh key
TMP_SCRIPT=$SCRIPT_PREFIX.sh
echo  "#!/usr/bin/expect">$TMP_SCRIPT
echo  "spawn ssh-keygen -b 1024 -t rsa">>$TMP_SCRIPT
echo  "expect *key*">>$TMP_SCRIPT
echo  "send r">>$TMP_SCRIPT
if [ -f $SSH_DIR/id_rsa ]; then
    echo  "expect *verwrite*">>$TMP_SCRIPT
    echo  "send yr">>$TMP_SCRIPT
fi
echo  "expect *passphrase*">>$TMP_SCRIPT
echo  "send r">>$TMP_SCRIPT
echo  "expect *again:">>$TMP_SCRIPT
echo  "send r">>$TMP_SCRIPT
echo  "interact">>$TMP_SCRIPT

chmod +x $TMP_SCRIPT

/usr/bin/expect $TMP_SCRIPT
rm $TMP_SCRIPT

# 3. generat file authorized_keys
cat $SSH_DIR/id_rsa.pub>>$SSH_DIR/authorized_keys

# 4. chmod 600 for file authorized_keys
chmod 600 $SSH_DIR/authorized_keys
echo ===========================
# 5. copy all files to other hosts
for ip in $(cat $HOSTS_FILE)  
do
    if [ "x$ip" != "x" ]; then
        echo -------------------------
        TMP_SCRIPT=${SCRIPT_PREFIX}.$ip.sh
        # check known_hosts
        val=`ssh-keygen -F $ip`
        if [ "x$val" == "x" ]; then
            echo "$ip not in $SSH_DIR/known_hosts, need to add"
            val=`ssh-keyscan $ip 2>/dev/null`
            if [ "x$val" == "x" ]; then
                echo "ssh-keyscan $ip failed!"
            else
                echo $val>>$SSH_DIR/known_hosts
            fi
        fi
        echo "copy $SSH_DIR to $ip"

        echo  "#!/usr/bin/expect">$TMP_SCRIPT
        echo  "spawn scp -r  $SSH_DIR $DEST_USER@$ip:~/">>$TMP_SCRIPT
        echo  "expect *assword*">>$TMP_SCRIPT
        echo  "send $PASSWORDr">>$TMP_SCRIPT
        echo  "interact">>$TMP_SCRIPT

        chmod +x $TMP_SCRIPT
        #echo "/usr/bin/expect $TMP_SCRIPT" >$TMP_SCRIPT.do
        #sh $TMP_SCRIPT.do&

        /usr/bin/expect $TMP_SCRIPT
        rm $TMP_SCRIPT
        echo "copy done."                
    fi
done

echo done.

二、建立主机列表文件

新建主机列表文件,一行一个需要建立互信的IP。

示例如下:

192.168.1.2  
192.168.1.3  
192.168.1.4  
192.168.1.5

三、启动程序及参数

运行刚才新建的.sh文件,脚本接受三个参数,远程机器用户名、密码和host文件名(相对路径或绝对路径均可)。

例如:

./auto_auth.sh app app ./hostList.cfg

脚本会生成pub公钥文件,并将公钥拷贝至指定主机。

未分类

使用sshpass expect实现非交互登录ssh远程执行命令

有时侯,利用 ssh 在本地执行远程机器的命令可以便捷地处理某些重复工作。我们希望做到:

  • 免手工输入密码
  • 支持执行多个命令,执行 shell 脚本
  • 支持执行 sudo 的命令

免手工输入密码

我们可以使用 ssh 互信,sshpass 和 expect 等工具来避免手工输密码。使用过程可能会碰到如下需要手工输入 yes 的繁琐场景:

$ ssh username@hostname
The authenticity of host ... can't be established.
ECDSA key fingerprint is ...
Are you sure you want to continue connecting (yes/no)?

为了避免出现上述场景,往 ssh 命令添加如下参数:

$ ssh -o "StrictHostKeyChecking no" username@password

SSH 互信

SSH 互信的配置非常简单,首先生成 ssh key:

$ ssh-keygen

把 public key 拷贝到信任方中:

$ ssh-copy-id -i ~/.ssh/id_rsa.pub username@hostname

之后免密执行命令:

$ ssh -o "StrictHostKeyChecking no" username@password cmd

sshpass

sshpass 是一个用于非交互的 ssh 密码验证工具,使用前先安装:

$ yum install sshpass

使用如下:

$ sshpass -p password ssh -o "StrictHostKeyChecking no" username@hostname cmd

expect

Expect 是用来进行自动化控制和测试的软件工具。虽然学习成本较高,但是 expect 的功能强大,利用 expect 可以方便的执行远程命令。使用前先安装:

$ yum install expect

例如:

#!/usr/bin/expect

spawn ssh -o "StrictHostKeyChecking no" username@hostname
expect "*assword*"
send "passwordn"
expect "*$*"
send "commandn"
expect "*$*"
send "exitn"
expect eof

Expect 不仅支持 ssh,还支持 scp, ftp 等工具。

支持多命令和脚本

执行多条命令

sshpass 和 expect 在支持多条命令上非常类似,只需用 && 连接命令即可:

# ssh trust
$ ssh -o "StrictHostKeyChecking no" username@password "cmd1 && cmd2"

例如:

# sshpass
$ sshpass -p password ssh -o "StrictHostKeyChecking no" username@password "ls -a && mkdir test"

# expect
......
expect "*$*"
send "ls -a && mkdir testn"
......

执行本地脚本

对于执行本地脚本,ssh 和 sshpass 的用法类似。

# ssh trust
$ ssh -o "StrictHostKeyChecking no" username@password bash -s < shell_script.sh

# sshpass
$ sshpass -p password ssh -o "StrictHostKeyChecking no" username@password bash -s < shell_script.sh

对于 expect,首先需要把脚本拷贝到远程主机,然后在远程主机执行该脚本,步骤如下:

...
# Copy script to remote host
spawn scp -o "StrictHostKeyChecking no" shell_script.sh username@hostname:~/
expect "*assword*"
send "passwordn"
expect "*100%*"
expect eof

# Execute the shell script at remote host
spawn ssh -o "StrictHostKeyChecking no" username@hostname
expect "*assword*"
send "passwordn"
expect "*$*"
send "sh shell_script.shn"
......

支持执行 sudo 命令

有些命令需要 sudo 权限才能执行,但是我们不希望重复的输入密码,我们可以把每条命令修改为如下:

cmd ---> 'echo password | sudo -S cmd'

例如:

$ sshpass -p password ssh -o "StrictHostKeyChecking no" username@password "echo password | sudo -S mkdir /newdir"

对于如 echo, dd 等部分命令,有时会出现如下失败场景:

$ sshpass -p password ssh -o "StrictHostKeyChecking no" username@password 'echo password | sudo -S echo hello > /newdir/newfile'
bash: /newdir/newfile: 权限不够

解决办法如下:

cmd ---> 'echo password | sudo -S sh -c "cmd"'

# For example
$ sshpass -p password ssh -o "StrictHostKeyChecking no" username@password 'echo WSfdl097018= | sudo -S sh -c "echo hello >  /newdir/newfile"'

如果采用 expect,需要把脚本拷贝到远程主机,然后在远程主机采用 sudo 执行该脚本,相对 sshpass 更简便和健壮:

...
# Copy script to remote host
spawn scp -o "StrictHostKeyChecking no" shell_script.sh username@hostname:~/
expect "*assword*"
send "passwordn"
expect "*100%*"
expect eof

# Execute the shell script at remote host
spawn ssh -o "StrictHostKeyChecking no" username@hostname
expect "*assword*"
send "passwordn"
expect "*$*"
send "sudo sh shell_script.shn"
expect "*assword*"
send "passwordn"
......