FreeSWTICH非权威指南

目标

出于公司要开发呼叫中心相关业务被迫学习了FreeSWITCH orz

然而似乎软交换这东西并不怎么火热,中文的参考资料似乎只有杜金房(Seven Du)
老师的《FreeSWITCH权威指南》以下简称《权威》

官方文档写的凑合,但是一些细节交待的也不清不楚

出于为日后接手此项目的人员能在两周内快速上手,故逐步丰富该系列文档,主要形式是项目中使用或可能使用到的功能,给官方文档的做个翻译,并且加入一些个人的见解。

目前的见解大多是实践所得,并没有进行源码解析

环境

  • Linux: HBSpy.NMS-NG 3.10.0-957.5.1.el7.x86_64 #1 SMP Fri Feb 1 14:54:57 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
  • FreeSWITCH: Version 1.9.0+git20190117T172020Zeb37939b5c~64bit (git eb37939 2019-01-17 17:20:20Z 64bit)

TODO

  • [x] mod_fifo
  • [x] mod_curl
  • [ ] mod_xml_curl
  • [ ] IVR

mod_fifo

About

mod_fifo 是一个呼叫中心APP,通过分配优先级可以自定义呼叫队列。

FIFO 的意思是『先入先出』。当有呼叫进入队列时,它们按序排列,以便于队列中最长等待时间的呼叫将被第一个得到应答。通常,FIFO呼叫队列被用于『先到先得』的呼叫场景,例如客户服务呼叫中心。

一种替代玩法是mod_callcenter,它更像是传统的ACD应用,由FreeSWITCH™社区的成员提供。

Usage

1
<fifo name>[!<importance_number>] [in [<announce file>|undef] [<music file>|undef] | out [wait|nowait] [<announce file>|undef] [<music file>|undef]]

Definitions

记住如下概念将帮助你玩转mod_fifo

  • Consumer - 摘机坐席。 off-hook agent
    一种注册到FIFO队列并等待『消耗』来自队列的传入呼叫的坐席。摘机坐席的电话在他/她登录到队列后就一直在使用。

HBSpy: 坐席服务完客户之后并不挂机,而是在听MOH,当有新客户呼入时直接接听,省去坐席振铃时间

  • Member - 挂机坐席 on-hook agent
    一种注册到FIFO队列后电话处于空闲状态直到有队列中有传入呼入到来的坐席。给坐席振铃后,他/她才会从队列中拿出一个呼叫来接听。一个呼叫也有可能振多个坐席的铃。

HBSpy: 队列中有呼入后,给注册到该队列的坐席们振铃。实践中的是会依次振铃,间隔会受agent的timeout, lag等参数的影响。

  • Caller - 来电者
    已经进入队列的人,来电者要么在『等待』要么在跟坐席通话。

Configuration

mod_fifo的全局配置参数在以下文件中

1
$CONFDIR/autoload_configs/fifo.conf.xml

Agent Callback

通过以下方式注册坐席。类似Asterisk队列成员

1
2
3
4
5
6
7
<configuration name="fifo.conf" description="FIFO Configuration">
   <fifos>
      <fifo name="fifo1@domain.name.com" importance="0">
         <member timeout="60" simo="1" lag="20">{fifo_member_wait=nowait}user/1005@$${domain}</member>
      </fifo>
   </fifos>
</configuration>
  • timout: 在切换到下一个坐席前的振铃超时(秒)

  • simo: 同时振铃数,至少为1

    • 如果你给多部话机注册了同一个分机号,这里控制有多少话机同时振铃
    • 举例:一个分机号有10部话机,设置simo="10"来同时给这10部话机振铃

HBSpy: 《权威》里把simo定义为『最大能服务的呼叫的数量』,好像跟官方说法有些出入?

  • lag: 在开始接收下一个呼叫前的等待时间(秒),类似Asterisk里的wrapup-time
  • fifo_member_wait: 取值为waitnowait

HBSpy: wait与nowait的区别《权威》里解释的也比较清楚,不清楚的也可以联想一下前面的摘机坐席和挂机坐席

提示:如果你想在fifo呼叫坐席时,给来话一个指定的ID,可以设置origination_caller_id_name和origination_caller_id_num变量。可以通过{}在呼叫字符串上设置,或者通过set app在把来话放入fifo队列的dialplan中设置(要在’fifo in’执行之前)。

HBSpy: 这里的要在’fifo in’执行之前要特别注意,fifo相关的许多变量似乎都是这样的。从fifo执行的debug日志来看,在fifo in之后就会立即从通道变量的值中设置fifo相关的参数值,之后再怎么在执行前修改,比如在坐席接听之前修改fifo_announce,也不会生效。具体是否如此,要等分析完源码之后才能给出定论。所以你想用fifo_announce播放『工号XXX为您服务』似乎是很难的,《权威》中也并没有举这样的例子。

Dialplan Example

把来话压入FIFO队列

1
<action application="fifo" data="myqueue in /tmp/exit-message.wav /tmp/music-on-hold.wav"/>

这会使来话压入一个叫"myqueue"的队列,并且一直播放"/tmp/music-on-hold.wav"的等待音。在来话被出队时,播放"/tmp/exit-message.wav"

HBSpy: 于是这里的exit-message.wav就可以是通常的『为保证服务质量,您的通话将会被录音』,当然你也可以通过设置fifo_announce来实现,本质上是一样的。

把来话压出FIFO队列

1
<action application="fifo" data="myqueue out nowait /tmp/caller-found.wav /tmp/agent-music-on-hold.wav"/>

这会把"myqueue"队列中等待时间最久的来话取出,与坐席在当前通道连接,并给坐席播放"/tmp/caller-found.wav"

如果当前FIFO队列中没有来话,并且你设置的是"wait","/tmp/agent-music-on-hold.wav"就会一直给坐席播放直到有新呼叫到达。并且在通话结束时,坐席不必挂机就会回到FIFO队列

If you have the “nowait” parameter set and there are no calls in the FIFO queue, processing immediately continues past the FIFO queue. If there are one or more calls in the queue, only one call is retrieved and processing continues past the FIFO queue after that call ends.

指定来话出队

如果你想指定一个来话出队,可以设置fifo_bridge_uuid为队列中来话的UDID

设置MOH和放送音

这里有一些方式可以用来给’fifo in’和’fifo out’设置MOH和放送音

1
2
3
4
5
<action application="set" data="fifo_music=<sound path>"/> 
<action application="set" data="fifo_announce=<sound path>" />
<action application="set" data="fifo_orbit_announce=<sound path>" />
<action application="set" data="fifo_orbit_exten=<exten>:[timeout]" />
<action application="set" data="fifo_override_announce=<sound path>" />

HBSpy: 我们说一些在我使用的版本上的实践结果以供参考:

  • fifo_music: 这个是来话在队列中等待时听的音乐,也是摘机坐席在等来话时听的音乐。

  • fifo_announce: 这个是当有坐席摘机时,给来话播放的,此时坐席是听不到的。fifo_music放完之前该来话也没被认为是被服务,也就是说如果在fifo_announce放完之前坐席就把电话挂了,该来话仍在队列中。反应到FIFO-Action上,应该是在consumer_start之后,caller_pop之前(待验证)。然后就是之前说的,目前尚未想到一种好方法,通过此处设置实现播送坐席的工号等信息。

  • fifo_orbit_announce: 这个是来话在队列中等待超过一定时间后的放音。fifo_orbit_*这一系列都是用来控制等待超时的,fifo_orbit_exten中可以定义超时后的跳转exten和超时时长,所以fifo_orbit_announce在没有设置fifo_orbit_exten的超时时长时是不生效的。《权威》中的例子将此设置为『为保证服务质量,您的通话将会被录音』似乎是不合适的。另外还有个设置是fifo_chime_list, fifo_chime_freq在超时放音上也能实现类似的功能,但它其实更看重循环而非超时,并且不能进行exten跳转。

  • fifo_orbit_exten: 如上,超时后的跳转exten和超时时长

  • fifo_override_announce: 这里是给坐席放音的,一般可以用来给坐席提示来电号码。实践中在fifo_announce后播送,并不同时,感觉有些浪费时间,annouce和override_annouce取最长时间之后再双方进行通话不是挺好,不知道是freeSWITCH的BUG还是Feature。另外需注意,由于此设置最终影响的是坐席leg,故你在来话dialplan中设置的tts_engine和tts_voice需要在此通道重新设置。在《权威》中也提到了这个问题,但他的设置方式在我的实践中并没有生效。其它设置方式还可以是坐席的呼叫字符串,全局默认值等。PS: 参考了源码之后发现,缺乏tts_engine和tts_voice变量时,log中的提示也为Invaild Args,没有跟其它情况下的Invaild Args区分,对排错十分不友好。

实现FIFO槽位(直译)

每一个FIFO队列可以有10种优先级槽位(默认优先级是5)。优先级1要高于优先级10。你可以通过如下设置把一个来话放入这10种优先级槽位中

1
<action application="set" data="fifo_priority=1" />

消费者(坐席)可以通过设置fifo_pop_order来影响选择来话服务的优先级

1
<action application="set" data="fifo_pop_order=1,2" />

可以通过逗号分隔符同时设置多个优先级顺序

坐席/来话举例

这个场景有两个号码:7010是坐席用,会一直听MOH直到有来话;7011是来话用,会一直听MOH直到有空闲坐席

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<extension name="Agent_Wait">
  <condition field="destination_number" expression="^7010$">
    <action application="set" data="fifo_music=$${hold_music}"/>
    <action application="answer"/>
    <action application="fifo" data="myq out wait"/>
  </condition>
</extension>
<extension name="Queue_Call_In">
  <condition field="destination_number" expression="^7011$">
    <action application="set" data="fifo_music=$${hold_music}"/>
    <action application="answer"/>
    <action application="fifo" data="myq in"/>
  </condition>
</extension>

坐席通过拨打7010后等待。来话通过被路由或转移到此号码上时就会在队列中等待,直到有空闲坐席为之服务

停泊超时举例

这个例子中,如果来话停泊时间超过了20秒就会被转移到1004这个号码上

1
2
3
4
5
6
7
extension name="park" continue="true">
<condition field="destination_number" expression="^5900$">
<action application="set" data="fifo_music=$${hold_music}"/>
<action application="set" data="fifo_orbit_exten=1004:20"/>
<action application="fifo" data="5900@$${domain} in"/>
</condition>
</extension>

API Commands

fifo

  • list|list_verbose
1
fifo list|list_verbose [ fifo_name ]

'fifo list’和’fifo list_verbose’都能给出许多关于当前定义过的队列的信息,包括正在队列中等待或是被服务的客户(来话),坐席等。如果提供了队列名,就只会列出指定名称队列的信息

HBSpy: list_verbose比list给出的信息多的多,包含了许多非fifo自身的信息,大多是通道变量

  • count
1
fifo count [ fifo_name ]

示例输出

1
2
fifo_1:0:0:1:0:0
fifo_2:0:0:0:0:0

结果是返回每一个队列的一些统计值,用冒号分隔

1
队列名:消费者数:来话数:成员数:正在振铃的坐席数:空闲坐席数
  • importance
1
fifo importance <fifo_name>

输出指定队列的重要权值

  • reparse [del_all]
1
fifo reparse [del_all]

重新载入mod_fifo的配置;如果你使用的是mod_xml_curl来生成的配置,那么这个操作也会重新请求这个xml

del_all参数会引发内存清理(当队列为空时,并不会自动被删除)。内存在大多数情况下并不是什么问题,每个fifo队列的开销非常少,但在多租户的场景下,可能会是个事。

fifo_member

  • add
1
add <fifo_name> <originate_string> [<simo_count>] [<timeout>] [<lag>] [expires] [taking-calls]

给指定队列添加成员,使用的参数跟在Agent Callback章节里的一致。如果指定名称的FIFO队列不存在,就会自动创建一个。

  • del
1
del <fifo_name> <originate_string>

从指定队列中删除成员。当最后一个成员从队列中移除时,队列并不会被自动删除。

Additional variables

  • fifo_destroy_after_use: 当一个FIFO队列第一次使用时,FreeSWITCH会自动创建之,然后存在内存中。这个变量告诉FreeSWITCH销毁此队列,为了节省内存。用于构建一次性队列。

mod_fifo使用或设置的Caller Leg侧通道变量

  • fifo_chime_list: 当客户在队列中等待人员应答时广播的文件列表
  • fifo_chime_freq: 指定fifo_chime_list播放的频率,单位秒
  • fifo_orbit_exten: (exten:timeout), 在超时后将来话转移到指定exten
  • fifo_orbit_dialplan: 超时转移时指定的dialplan,没指定即为当前dialplan
  • fifo_orbit_context: 超时转移时指定的context,没指定即为当前context
  • fifo_orbit_announce: 超时转移前给来话播放的信息
  • fifo_caller_exit_key: 来话退出键,退出后默认挂断
  • fifo_caller_exit_to_orbit: 和fifo_caller_exit_key配合使用,退出后转移来话到指定exten,而不是挂断
  • fifo_serviced_uuid: ?(这个我也很迷)
  • fifo_status: WAITING, TIMEOUT, ABORTED, DONE
  • fifo_timestamp: 记录caller被bridage到consumer的时间戳
  • fifo_serviced_by: 接听电话的Consumer Leg侧的UUID
  • fifo_serviced_uuid: 接听电话的Consumer Leg侧的UUID,如果没设置fifo_consumer_id,内容和fifo_serviced_by一样

mod_fifo设置的Consumer Leg侧通道变量

这些通道变量是mod_fifo根据该consumer的相关配置设置的。

如果该consumer是静态配置成member的,这些变量的值可以通过在consumer的呼叫字符串前用{}设置。使用{}设置多个变量时,以逗号分隔的 变量=值 形式。

如果consumer是通过执行一个dialplan来从fifo中接听电话的,这些变量也可以在consumer所执行的dialplan中用set应用设置。

HBSpy: 总结一下就是提醒你,Caller Leg和Consumer Leg不是一个Leg,一些在Caller Leg中设置的变量并不一定会自动的设置在Consumer Leg上,想想之前的tts_engine和tts_voice

  • fifo_strategy: 来话出队的策略,“more_ppl"或"waiting_longer”[默认]
  • fifo_consumer_id: 设置了之后,给队列指定consumer uuid,当consumer有不同的uuid时使用
  • fifo_record_template: 设置了之后,这里就是会话录音的文件
  • fifo_status: WAITING or TALKING
  • fifo_target: 这个consumer正在服务的来话uuid
  • fifo_override_announce: 给坐席播放的提示音
  • fifo_consumer_wrapup_sound: 服务结束后给来话播放的内容
  • fifo_consumer_wrapup_key: 触发按键
  • fifo_pop_order: 符号分隔的该consumer接听的优先级
  • fifo_member_wait: 可以设置为’wait’或’nowait’
    • 如果设置为’wait’,则当来话挂断后,Consumer Leg并不挂断[默认]
    • 如果设置为’nowait’,则当来话挂断后,Consumer Leg也挂断

总结

  1. mod_fifo在实现了基本的先入先出队列的基础上,又适当的加入了一些呼叫业务相关的功能,fifo_announce等,再加上FIFO队列创建的高度自由性,于是乎成为了一个相对于mod_callcenter而言更好的轮子。通过与ESL相配合,可以开发出功能丰富的呼叫中心业务,同时又不必过于关心通话层面的复杂逻辑处理。(在FreeSWITCH的文档中提到过,想通过自己的脚本处理大多数通话过程,是自寻死路)[1]
  2. FIFO队列的创建非常自由,fifo_member add时会自动创建;fifo in时也会自动创建
  3. 一些不常用的参数的功能目前有待源码验证,官方文档不全,也说得不清不楚

  1. It is generally best to do most telephony processing in the dialplan, not in your script. The FS team has put a great deal of effort into making sofia handle many edge cases of SIP processing, so trying to do all that in your script would prove hopeless. ↩︎

诡异的每隔7个小时出现的异常高负载

Investigation of regular high load on unused machines every 7 hours

原文如下:

https://blog.avast.com/investigation-of-regular-high-load-on-unused-machines-every-7-hours

这里贴一下结论:

#define LOAD_FREQ (5HZ+1) / 5 sec intervals */

So, contrary to the comment there, the load is not measured every 5s (HZ=1000 in CentOS6) but every 5.001 seconds!

Now, let’s do the math. If we add one ms every five s, how long does it take to add another full five s? In other words, what is the interference period? We need to add one ms 5000x. If we add it every five s, it takes us 25000s. Which, converted to hours, is: six hours, 56 minutes and 40 seconds. So we were slightly wrong, the spikes did not occur every seven hours.

The regular high load was really caused by launching a bunch of monitoring scripts every minute and by the fact that the load measurements slowly and slightly moves with a period of almost 7 hours.

Well, yeah. It turned out that it was really happening everywhere. But to our defense, on a much, much smaller scale. Why? First, most of our machines are much more powerful, so the spike in load is not that big and it does not trigger the threshold for alerts (it is based on load_per_core value). Second, most of the machines actually do something, so you won’t notice a small spike in load occurring every ~seven hours in the plot, as the load curve is not stable anyway. And third, the majority of the hosts only have a few collectd exec plugins configured, so the number of processes executed at one moment is significantly smaller.

附上一篇阿里云上的文章:

https://yq.aliyun.com/articles/484253

从这个函数中可以看到,内核计算load采用的是一种平滑移动的算法,Linux的系统负载指运行队列的平均长度,需要注意的是:可运行的进程是指处于运行队列的进程,不是指正在运行的进程。即进程的状态是TASK_RUNNING或者TASK_UNINTERRUPTIBLE。

Linux内核定义一个长度为3的双字数组avenrun,双字的低11位用于存放负载的小数部分,高21位用于存放整数部分。当进程所耗的 CPU时间片数超过CPU在5秒内能够提供的时间片数时,内核计算上述的三个负载,负载初始化为0。

假设最近1、5、15分钟内的平均负载分别为 load1、load5和load15,那么下一个计算时刻到来时,内核通过下面的算式计算负载:
load1 -= load1 - exp(-5 / 60) -+ n (1 - exp(-5 / 60 ))
load5 -= load5 - exp(-5 / 300) + n (1 - exp(-5 / 300))
load15 = load15 exp(-5 / 900) + n (1 - exp(-5 / 900))
其中,exp(x)为e的x次幂,n为当前运行队列的长度。

有兴趣的同学还可以更加深♂入的了解一下linux的Load Average算法

https://www.teamquest.com/import/pdfs/whitepaper/ldavg2.pdf

WebSphere

  1. the trustAnchors parameter must be non-empty

引起这问题的原因有很多,在这儿先贴一个StackOverflow上的讨论

http://stackoverflow.com/questions/6784463/error-trustanchors-parameter-must-be-non-empty

我们的场景是,在连接一个开启了security的WAS时产生的

使用的是WAS默认的key.p12和trust.p12

/opt/IBM/WebSphere/AppServer/profiles/AppSrv01/config/cells/nullNode01Cell/nodes/nullNode01/key.p12
/opt/IBM/WebSphere/AppServer/profiles/AppSrv01/config/cells/nullNode01Cell/nodes/nullNode01/trust.p12

先贴一份前期知识

https://www.ibm.com/support/knowledgecenter/zh/SSAW57_8.5.5/com.ibm.websphere.nd.doc/ae/csec_7ssldefault_chainedcert_config.html

文中说

在创建概要文件期间,WebSphere Application Server 将创建缺省密钥库文件 key.p12 和缺省信任库文件 trust.p12。另外,还将在 key.p12 文件中创建缺省的链证书。并且会从 key.p12 文件中抽取链式证书的根签署者或公用密钥并将其添加到 trust.p12 文件中。如果在进程启动期间这些文件不存在,那么会在启动期间进行重新创建。
缺省密钥库和缺省信任库的后缀分别为 DefaultKeyStore 和 DefaultTrustStore,您可以凭此确定这两个文件。另外,在 SSL 配置中,必须将 fileBased 属性设置为 true,以便运行时环境只使用缺省密钥库和缺省信任库。

在基本应用程序服务器上,缺省密钥和信任密钥库存储在配置库的节点目录中。例如,缺省 key.p12 和 trust.p12 库是使用 AppSrv01 概要文件名称、myhostNode01Cell 名称和 myhostNode01 节点名创建的。密钥库和信任库分别位于下列目录中:
[AIX Solaris HP-UX Linux Windows] C:\WebSphere\AppServer\profiles\AppSrv01\config\cells\myhostNode01Cell \nodes\myhostNode01\key.p12
[AIX Solaris HP-UX Linux Windows] C:\WebSphere\AppServer\profiles\AppSrv01\config\cells\myhostNode01Cell \nodes\myhostNode01\trust.p12
WebSphere Application Server 生成的所有缺省密钥库的缺省密码都是 WebAS。在进行初始配置之后,您应更改缺省密码,以便获得一个更安全的环境。

然而,并没有!

我们使用keytool查看这两个库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[root@was nullNode01]# keytool -list -v -keystore key.p12 -storetype PKCS12 -storepass WebAS

Keystore type: PKCS12
Keystore provider: SunJSSE

Your keystore contains 1 entry

Alias name: default
Creation date: Dec 20, 2016
Entry type: PrivateKeyEntry
Certificate chain length: 2
Certificate[1]:
Owner: CN=null, OU=nullNode01Cell, OU=nullNode01, O=IBM, C=US
Issuer: CN=null, OU=Root Certificate, OU=nullNode01Cell, OU=nullNode01, O=IBM, C=US
Serial number: 7f412de35e4f
Valid from: Mon Dec 19 10:00:29 CST 2016 until: Tue Dec 19 10:00:29 CST 2017
Certificate fingerprints:
MD5: FA:E4:22:96:D3:4B:6A:AE:5D:70:49:63:87:C8:38:D9
SHA1: 22:2D:53:3C:7B:F3:13:3C:46:06:1D:8E:EF:69:F0:A0:2F:4B:7F:62
SHA256: AB:62:ED:51:D3:63:5F:3E:91:1C:AD:BB:2B:9F:F7:64:10:98:4C:71:36:ED:3C:F4:74:63:C5:4B:B8:83:62:78
Signature algorithm name: SHA1withRSA
Version: 3

Extensions:

#1: ObjectId: 2.5.29.17 Criticality=false
SubjectAlternativeName [
RFC822Name: ProfileUUID:AppSrv01-BASE-6a02d09f-b69d-4ad5-ab40-6dcfc5a4353d
]

正常

1
2
3
4
5
6
7

[root@was nullNode01]# keytool -list -v -keystore trust.p12 -storetype PKCS12 -storepass WebAS

Keystore type: PKCS12
Keystore provider: SunJSSE

Your keystore contains 0 entry

说好的放进去呢

解决方案有两种,一种我附个别人的

http://www.wenhq.com/article/view_711.html

用http进行连接的,客户端代码比较简单,直接使用URL类进行连接并获取输入流即可。https不需要客户端证书,并且服务器端的证书是已经受信任的也同http一样容易。当验证的证书每年的费用得好几千元,为了省钱,这里主要是说java访问未验证证书的https的访问。
通常有两种方案:1、将证书导入到TrustStore文件中;2、修改X509证书信任管理器类的实现。推荐使用第一种方案,我们可以尽量让服务器的证书稳定,不在使用中修改就可以了。
第一步、导出服务器端证书。用ie连接地址,然后出现了证书确认的提示框,点击查看证书-详细信息,点击复制到文件,选择base64编码,导出保存文件为test.cert。
第二步、把证书从其它文件导入到TrustStore文件中。
keytool -import -file test.cer -keystore test_store
第三步、设置java的javax.net.ssl.trustStore的系统属性
System.setProperty(“javax.net.ssl.trustStore”, “D: \test_store”);//注意是绝对路径
这样通过HttpClient应用接口就可以访问我们自己的https服务了。
常见错误:javax.net.ssl.SSLException: java.lang.RuntimeException: Unexpected error: Java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty 异常,有两种情况:
1、没有设置或者文件路径设置错误;
2、证书就采用keytool的默认jks类型就可以,否则也会报错,参考内容
An implementation of PKCS12 as JCA keystore type “pkcs12″. Storing trusted anchors in PKCS12 is not supported. Users should store trust anchors in JKS format and save private keys in PKCS12 format.
来自http://www.cs.nyu.edu/artg/internet/Spring2006/readings/JSSERefGuide.html

No related posts.

第二种可以在WAS里进行配置,直接把key.p12里的证书导入trust.p12

Security > SSL certificate and key management > Key stores and certificates > NodeDefaultTrustStore > Personal certificates
Manages personal certificates.

Import certificates from a key file or key store

生成新trust.p12之后再次查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[root@was ~]# keytool -list -v -keystore trust.p12 -storetype PKCS12 -storepass WebAS

Keystore type: PKCS12
Keystore provider: SunJSSE

Your keystore contains 1 entry

Alias name: default
Creation date: Dec 20, 2016
Entry type: PrivateKeyEntry
Certificate chain length: 2
Certificate[1]:
Owner: CN=null, OU=nullNode01Cell, OU=nullNode01, O=IBM, C=US
Issuer: CN=null, OU=Root Certificate, OU=nullNode01Cell, OU=nullNode01, O=IBM, C=US
Serial number: 7f412de35e4f
Valid from: Mon Dec 19 10:00:29 CST 2016 until: Tue Dec 19 10:00:29 CST 2017
Certificate fingerprints:
MD5: FA:E4:22:96:D3:4B:6A:AE:5D:70:49:63:87:C8:38:D9
SHA1: 22:2D:53:3C:7B:F3:13:3C:46:06:1D:8E:EF:69:F0:A0:2F:4B:7F:62
SHA256: AB:62:ED:51:D3:63:5F:3E:91:1C:AD:BB:2B:9F:F7:64:10:98:4C:71:36:ED:3C:F4:74:63:C5:4B:B8:83:62:78
Signature algorithm name: SHA1withRSA
Version: 3

Extensions:

#1: ObjectId: 2.5.29.17 Criticality=false
SubjectAlternativeName [
RFC822Name: ProfileUUID:AppSrv01-BASE-6a02d09f-b69d-4ad5-ab40-6dcfc5a4353d
]

这时候应该是跟key.p12内容差不多的,别问我为什么,不是很懂Java系

使用新的trust.p12作为trustStore去连接,然后就正常了

理论上看起来好像有一些问题,但他的确正常了,有需要的可以参考

pyOpenSSL

  1. Cannot import pyOpenSSL

这是一次我认为并不常见的Cannot import pyOpenSSL的问题的处理

1
2
3
4
5
6
7
8
9
10
11
12
[root@NMS winrm]# python test.py 
Traceback (most recent call last):
File "test.py", line 1, in <module>
import winrm
File "build/bdist.linux-x86_64/egg/winrm/__init__.py", line 6, in <module>
File "build/bdist.linux-x86_64/egg/winrm/protocol.py", line 11, in <module>
File "build/bdist.linux-x86_64/egg/winrm/transport.py", line 42, in <module>
File "/usr/local/lib/python2.7/site-packages/requests_credssp/__init__.py", line 1, in <module>
from .credssp import HttpCredSSPAuth
File "/usr/local/lib/python2.7/site-packages/requests_credssp/credssp.py", line 8, in <module>
raise Exception("Cannot import pyOpenSSL")
Exception: Cannot import pyOpenSSL

尝试手动执行

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@NMS OpenSSL]# python /usr/local/lib/python2.7/site-packages/OpenSSL/__init__.py
Traceback (most recent call last):
File "/usr/local/lib/python2.7/site-packages/OpenSSL/__init__.py", line 8, in <module>
from OpenSSL import rand, crypto, SSL
File "/usr/local/lib/python2.7/site-packages/OpenSSL/__init__.py", line 8, in <module>
from OpenSSL import rand, crypto, SSL
File "/usr/local/lib/python2.7/site-packages/OpenSSL/rand.py", line 12, in <module>
from OpenSSL._util import (
File "/usr/local/lib/python2.7/site-packages/OpenSSL/_util.py", line 6, in <module>
from cryptography.hazmat.bindings.openssl.binding import Binding
File "/usr/local/lib/python2.7/site-packages/cryptography/hazmat/bindings/openssl/binding.py", line 14, in <module>
from cryptography.hazmat.bindings._openssl import ffi, lib
ImportError: /usr/local/lib/python2.7/site-packages/cryptography/hazmat/bindings/_openssl.so: undefined symbol: EC_GROUP_new_curve_GF2m

EC_GROUP_new_curve_GF2这东西应该是openssl提供的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@NMS OpenSSL]# ldd /usr/local/lib/python2.7/site-packages/cryptography/hazmat/bindings/_openssl.so
linux-vdso.so.1 => (0x00007ffe68b58000)
libssl.so.10 => /usr/lib64/libssl.so.10 (0x00007f179b317000)
libcrypto.so.10 => /usr/lib64/libcrypto.so.10 (0x00007f179af33000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f179ad15000)
libc.so.6 => /lib64/libc.so.6 (0x00007f179a981000)
libgssapi_krb5.so.2 => /lib64/libgssapi_krb5.so.2 (0x00007f179a73d000)
libkrb5.so.3 => /lib64/libkrb5.so.3 (0x00007f179a455000)
libcom_err.so.2 => /lib64/libcom_err.so.2 (0x00007f179a251000)
libk5crypto.so.3 => /lib64/libk5crypto.so.3 (0x00007f179a025000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f1799e20000)
libz.so.1 => /usr/lib64/libz.so.1 (0x00007f1799c0a000)
/lib64/ld-linux-x86-64.so.2 (0x000000321cc00000)
libkrb5support.so.0 => /lib64/libkrb5support.so.0 (0x00007f17999ff000)
libkeyutils.so.1 => /lib64/libkeyutils.so.1 (0x00007f17997fb000)
libresolv.so.2 => /lib64/libresolv.so.2 (0x00007f17995e1000)
libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f17993c1000)

libcrypto.so.10 => /usr/lib64/libcrypto.so.10 (0x00007f179af33000)

1
2
[root@NMS OpenSSL]# nm /usr/lib64/libcrypto.so.10
nm: /usr/lib64/libcrypto.so.10: no symbols

问题应该出在libcrypto.so.10上,由于这系统不是我亲自一点点编译的,所以暂时不知道为什么

但重新编译openssl是少不了的

而且要注意加-fPIC shared

1
./config -fPIC shared

再nm一下

1
2
[root@NMS openssl-1.0.1e]# nm libcrypto.so | grep EC_GROUP_new_curve_GF2m 
00000000000bb960 T EC_GROUP_new_curve_GF2m

XXX: 需要注意,openssl相关的太底层,与许多基础功能相关,尤其是你SSH到这台服务器上进行操作,一不小心sshd就会跪,就再也SSH不上了,处理的时候一定要小心再小心

PyWinRM

  1. Double Hop/Second Hop
1
2
3
4
5
6
7
8
9
Active Directory ?  ????????'EXCHANGE\administrator'????????
At line:3 char:1
+ Get-Mailbox
+ ~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], ADInvalidCredentialExcepti
on
+ FullyQualifiedErrorId : [Server=WIN-2KOQERQDJNK,RequestId=1cdf09dc-9711-
4dbb-99a7-555fbf562855,TimeStamp=2016/12/20 6:38:06] [FailureCategory=Cmdl
et-ADInvalidCredentialException] E5C063D1

登录到本机的PowerShell执行一些命令是没问题的,当需要加载Exchange的PowerShell时,就会遇到Double Hop或者叫Second Hop问题
目前PyWinRM提供的支持Double Hop的方式是起用WinRM CredSSP

https://github.com/diyan/pywinrm#enabling-winrm-on-remote-host

Enable WinRM CredSSP authentication. This allows double hop support so you can authenticate with a network service when running command son the remote host. This command is run in Powershell.

1
2
Enable-WSManCredSSP -Role Server -Force
Set-Item -Path "WSMan:\localhost\Service\Auth\CredSSP" -Value $true

同时需要pip install requests-credssp和pip install pywinrm[credssp]

但是目前pip库中的pywinrm是有问题的,是不提credssp组件的。所以需要从git源码python setup.py install

然后在winrm.Session的时候添加transport=“credssp”

credssp: Will use CredSSP authentication for both domain and local accounts. Allows double hop authentication. This only works over a HTTPS endpoint and not HTTP.

1
-bash: ./dronline: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory

是因为64位系统中安装了32位程序
解决方法:

1
yum install glibc.i686

Can’t locate ExtUtils/MakeMaker.pm

1
yum install perl-ExtUtils-CBuilder perl-ExtUtils-MakeMaker

CentOS 升级 gcc 和 g++ 的方法

  1. 使用 redhat developer toolset 1.1 的repo,安装GCC
1
2
3
cd /etc/yum.repos.d
wget http://people.centos.org/tru/devtools-1.1/devtools-1.1.repo
yum --enablerepo=testing-1.1-devtools-6 install devtoolset-1.1-gcc devtoolset-1.1-gcc-c++
  1. 替换系统中原来的GCC
1
2
ln -s /opt/centos/devtoolset-1.1/root/usr/bin/* /usr/local/bin/
hash -r

命令或脚本

  1. extTable .1.3.6.1.4.1.2021.8
    只可以SNMP查询出单行(首行)返回结果

  2. exec/extend 指定MIBOID
    可以SNMP查询出多行(全部)返回结果)

exec无效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# Extensible sections.
#

# This alleviates the multiple line output problem found in the
# previous executable mib by placing each mib in its own mib table:

# Run a shell script containing:
#
# #!/bin/sh
# echo hello world
# echo hi there
# exit 35
#
# Note: this has been specifically commented out to prevent
# accidental security holes due to someone else on your system writing
# a /tmp/shtest before you do. Uncomment to use it.
#
# exec .1.3.6.1.4.1.2021.50 shelltest /bin/sh /tmp/shtest

# % snmpwalk -v 1 localhost -c public .1.3.6.1.4.1.2021.50
# enterprises.ucdavis.50.1.1 = 1
# enterprises.ucdavis.50.2.1 = "shelltest"
# enterprises.ucdavis.50.3.1 = "/bin/sh /tmp/shtest"
# enterprises.ucdavis.50.100.1 = 35
# enterprises.ucdavis.50.101.1 = "hello world."
# enterprises.ucdavis.50.101.2 = "hi there."
# enterprises.ucdavis.50.102.1 = 0

# Now the Output has grown to two lines, and we can see the 'hi
# there.' output as the second line from our shell script.
#
# Note that you must alter the mib.txt file to be correct if you want
# the .50.* outputs above to change to reasonable text descriptions.

# Other ideas:
#
# exec .1.3.6.1.4.1.2021.51 ps /bin/ps
# exec .1.3.6.1.4.1.2021.52 top /usr/local/bin/top
# exec .1.3.6.1.4.1.2021.53 mailq /usr/bin/mailq

snmpd -v 5.5以上使用extend

1
extend .1.3.6.1.4.1.2021.51 ps /bin/ps
0%