知行合一
全部博文(31)
分类: 系统运维
2009-08-03 14:11:03
Openserctl工具是安装在/usr/sbin上的shell脚本。被用来使用命令行的方式来对OpenSER进行管理。可以用来进行:
l 启动,终止,重启OpenSER
l 展示,授权,撤销ACLs
l 添加,删除,列出别名
l 添加,删除,配置AVP
l 管理LCR(low cost routes)
l 管理RPID
l 添加,删除,列出订阅者
l 添加,删除,展示usrloc表“in-ram”
l 监控OpenSER
我们将在下面的章节中学习一些它的选项.下面是openserctl help命令的输出信息:
/etc/openser# openserctl help
database engine 'MYSQL' loaded
Control engine 'FIFO' loaded
/usr/sbin/openserctl 1.2 - $Revision: 1.3 $
Existing commands:
-- command 'start|stop|restart'
restart ............................ restart OpenSER
start .............................. start OpenSER
stop ............................... stop OpenSER
-- command 'acl' - manage access control lists (acl)
acl show [
acl grant
acl revoke
-- command 'alias_db' - manage database aliases
alias_db show
alias_db list
alias_db add
alias_db rm
alias_db help ...................... help message
-
-
-- command 'avp' - manage AVPs
avp list [-T table] [-u
[-a attribute] [-v value] [-t type] ... list AVPs
avp add [-T table]
avp rm [-T table] [-u
[-a attribute] [-v value] [-t type] ... remove AVP (*)
avp help .................................. help message
- -T - table name
- -u - SIP id or unique id
- -a - AVP name
- -v - AVP value
- -t - AVP name and type (0 (str:str), 1 (str:int),
2 (int:str), 3 (int:int))
-
-
-- command 'db' - database operations
db exec
db show -- command 'lcr' - manage least cost routes (lcr) * lcr * * IP addresses must be entered in dotted quad format e.g. * * e.g. transport '2' is identical to transport 'tcp'. * * scheme: 1=sip, 2=sips; transport: 1=udp, 2=tcp, 3=tls * * Examples: lcr addgw_grp * lcr addgw level3 * lcr addroute +1 % 1 1 * lcr show .................................................................... ............. show routes, gateways and groups lcr reload .................................................................. ............. reload lcr gateways lcr addgw_grp .............. add gateway group, autocreate grp_id lcr addgw_grp ............... add gateway group with grp_id lcr rmgw_grp ............... delete the gw_grp lcr addgw ............... add a gateway lcr addgw ............... add a gateway with prefix lcr addgw ............... add a gateway with prefix and strip lcr rmgw ............... delete a gateway lcr addroute .............. add a route lcr rmroute .............. delete a route -- command 'rpid' - manage Remote-Party-ID (RPID) rpid add rpid rm rpid show -- command 'speeddial' - manage speed dials (short numbers) speeddial show speeddial list speeddial add ........................... add a speedial (*) speeddial rm speeddial help ...................... help message - - - - -- command 'add|mail|passwd|rm' - manage subscribers add passwd rm mail -- command 'cisco_restart' - restart CISCO phone (NOTIFY) cisco_restart -- command 'online' - dump online users from memory online ............................. display online users -- command 'monitor' - show internal status monitor ............................ show server's internal status -- command 'ping' - ping a SIP URI (OPTIONS) ping -- command 'ul|alias' - manage user location or aliases ul show [ ul rm ul add ul add -- command 'fifo' fifo ............................... send raw FIFO command 在版本1.1中,我们介绍一个叫做openserctlrc的资源文件。这个脚本可以在/etc/openser中找到。openserctl工具对其进行语法解析然后使用其配置数据库认证和一些进行沟通需要的参数。通常,它使用FIFO机制来向OpenSER守护进程发送命令。 | 安全起见,改变默认的访问数据库的用户名和密码是很重要的 | To show the file, issue a command: cat /etc/openser/openserctlrc # $Id: openserctlrc,v 1.2 2006/07/05 19:37:20 miconda Exp $ # # openser control tool resource file # # here you can set variables used in the openserctl ## your SIP domain SIP_DOMAIN=voffice.com.br ## database type: MYSQL or PGSQL, by defaulte none is loaded DBENGINE=MYSQL ## database host DBHOST=localhost ## database name DBNAME=openser ## database read/write user DBRWUSER=openser ## database read only user DBROUSER=openserro ## password for database read only user DBROPW=openserro ## database super user DBROOTUSER="root" ## type of aliases used: DB - database aliases; UL - usrloc aliases ## - default: none ALIASES_TYPE="DB" ## control engine: FIFO or UNIXSOCK ## - default FIFO CTLENGINE="FIFO" ## path to FIFO file OSER_FIFO="FIFO" ## check ACL names; default on (1); off (0) VERIFY_ACL=1 ## ACL names - if VERIFY_ACL is set, only the ACL names from below list ## are accepted ACL_GROUPS="local ld int voicemail free-pstn" ## verbose - debug purposes - default '0' VERBOSE=1 ## do (1) or don't (0) store plaintext passwords ## in the subscriber table - default '1' # STORE_PLAINTEXT_PW=0 现在,让我们以一种实践的方式来实现认证。 步骤1:按照上面章节中描述的内容来修改openser.cfg文件。 步骤2:使用命令/etc/init.d/openser restart 来重启OpenSER。 步骤3:使用被openserctl使用的默认的参数来配置openserctlrc。 # $Id: openserctlrc 1827 2007-03-12 15:22:53Z bogdan_iancu $ # # openser control tool resource file # # here you can set variables used in the openserctl ## your SIP domain SIP_DOMAIN=voffice.com.br ## database type: MYSQL or PGSQL, by defaulte none is loaded DBENGINE=MYSQL ## database host DBHOST=localhost ## database name DBNAME=openser ## database read/write user DBRWUSER=openser ## database read only user DBROUSER=openserro ## password for database read only user DBROPW=openserro ## database super user DBROOTUSER="root" ## type of aliases used: DB - database aliases; UL - usrloc aliases ## - default: none ALIASES_TYPE="DB" ## control engine: FIFO or UNIXSOCK ## - default FIFO CTLENGINE="FIFO" ## path to FIFO file OSER_FIFO="/tmp/openser_fifo" ## check ACL names; default on (1); off (0) VERIFY_ACL=1 ## ACL names - if VERIFY_ACL is set, only the ACL names from below list ## are accepted ACL_GROUPS="local ld int voicemail free-pstn" ## presence of serweb tables - default "no" HAS_SERWEB="yes" ## verbose - debug purposes - default '0' VERBOSE=1 ## do (1) or don't (0) store plaintext passwords ## in the subscriber table - default '1' STORE_PLAINTEXT_PW=1 步骤4:使用openserctl工具来配置两个用户帐户。 /sbin/openserctl add 1000 password 1000@voffice.com.br /sbin/openserctl add 1001 password 1001@voffice.com.br | 如果你碰到“复制关键字(Duplicate Keys)”的问题,请检查你是否 | | 安装了SerWEB表。如果你已经装了,那么只要将HAS_SERWEB设 | | 为“yes”即可。 | | 当你被要求密码时,使用openserrw | 你可以使用openserctl的rm命令来删除用户,使用openserctl的passwd命令来修改密码。 步骤5:使用ngrep工具来观察SIP消息: #ngrep -p -q -W byline port 5060 >register.pkt 步骤6:使用用户名和密码注册双方话机: 步骤7:验证话机是否注册上,使用下面的命令: #openserctl ul show 步骤8:你可以使用下面的命令来验证哪些用户在线: #openserctl online 步骤9:你可以使用下面的命令来ping一个用户: #openserctl ping 1000 步骤10:使用ngrep工具来验证认证信息 步骤11:从一个用户向另外一个用户打通电话 步骤12:使用下面命令验证在register.pkt文件中的认证 #pg register.pkt 由SIP代理处理的通话可以被分为: l 内域 l 带外内域 l 带内内域 l 带外到带外 让我们来描述一些目前我们脚本的一些问题: 问题#1:目前,我们没有检查从其他域过来的带外通话的标识。这就使得我们的服务器成为了比较开放的中继。因此,任何人都可以使用我们的服务器了隐藏他们的标识。 问题#2:我们的脚本不接受来自另一个域的来电 问题#3:一个用户可以使用另一个用户的证书来伪造INVITE请求中的FROM头域。 问题#4:一个用户可以使用另一个用户的证书来伪造REGISTER请求中的TO头域。 问题#5:此脚本不会准备对多域(multiple domains)进行管理。 到现在为止,我们已经使用uri==myself这一行指令来对请求进行了校验。然而,这条指令仅仅校验了本地名字和地址。如果我们需要管理多域,我们必须使用域模块和它的is_from_local()和is_uri_host_local()函数。 就像我之前说的那样,域模块导出了两个函数,这两个函数将在我们的脚本中使用。第一个是is_from_local(),用来校验FROM头域是否包含我们代理管理着的域。第二个函数是is_uri_host_local(),用来替代uri==myself指令。这些函数的优点是他们可以检查MySQL表中的域。然后你就可以在你的配置中处理多域了。 | 这个函数需要所有的被管理的域都要被插入数据库中 | | 对于使用这个功能资源的用户来说,一个相当普遍的 | | 错误就是在对话机进行注册之前,忘记将域插入到MySQL | | 数据库。 | 为了简化我们的脚本,我们将创建几个替代路线。我们已经看到脚本可能会变得非常复杂和难懂。为了避免这种情况的发生,我们已经创建了一些和子函数类似的替代路线。使用替代路线,允许我们将代码分片以加强它的可读性。 注册请求(Register Requests)(route[2]) 注册请求路线负责处理所有的注册请求。这段代码对用户进行认证并保存UAC的位置信息。 route[2] { # # -- Register request handler -- # if (is_uri_host_local()) { if (!www_authorize("", "subscriber")) { www_challenge("", "1"); exit; }; if (!check_to()) { sl_send_reply("401", "Unauthorized"); exit; }; save("location"); exit; } else if { sl_send_reply("401", "Unauthorized"); }; } 非注册请求(Non-Register Requests)(route[3]) 非注册请求路线处理所有其他的请求。请求需要再次进行认证。我们已经决定把请求分成下面几个部分: l 带内到带内(route[10]) l 带内到带外(route[11]) l 带外到带内(route[12]) l 带外到带外(route[13]) 通常,你会允许带认证的带内到带内的请求。这时比较普通的情况。带内到带外和带外到带内被用来处理内域请求。大多数的VoIP服务提供商不允许内域之间的交互,因为这回减少其潜在收入。带外到带外更是不会被允许的。大多数情况下,这种交互被认为是一种安全漏洞。 route[3] { # # -- non-register requests handler -- # # Verify the source (FROM) if (is_from_local()){ # From an internal domain -> check the credentials and the FROM if (!proxy_authorize("","subscriber")) { proxy_challenge("","1"); exit; } else if (!check_from()) { sl_send_reply("403", "Forbidden, use From=ID"); exit; }; consume_credentials(); # Verify aliases lookup("aliases"); # Verify the destination (URI) if (is_uri_host_local()) { # -- Inbound to Inbound route(10); } else { # -- Inbound to outbound route(11); }; } else { #Verify aliases, if found replace R-URI. lookup("aliases"); # Verify the destination (URI) if (is_uri_host_local()) { #-- Outbound to inbound route(12); } else { # -- Outbound to outbound route(13); }; }; } 管理我们域中发起的通话(Managing Calls Coming from Our Domain) 我们的脚本openser.cfg现在使用源地址和目的地址来区分通话。使用函数if(is_uri_host_local())来决定目的地,使用if(is_from_local())来决定源地址。 对于带内发起的通话,我们会首先检查标识并删除其证书以避免发送之。我们在检查目的地和前转请求前会对定义的别名进行解析。 如果通话目的地是我们管理域中之一(使用is_uri_host_local()检查),我们将其送至route(10)否则如果是一个外部域则将其送至route(11)。 带内到带内——route[10](Inbound-to-Inbound------route[10]) 带内目的地会被用户位置数据库处理。 route[10] { #from an internal domain -> inbound #Native SIP destinations are handled using the location table append_hf("P-hint: inbound->inbound \r\n"); if (!lookup("location")) { sl_send_reply("404", "Not Found"); exit; }; route(1); } 带内到带外——route[11](Inbound-to-outbound-----route[11]) 我们将使用DNS搜索来将通话路由到外部的目的地. route[11] { # from an internal domain -> outbound # Simply route the call outbound using DNS search append_hf("P-hint: inbound->outbound \r\n"); route(1); } 带外到带内——route[12](Outbound-to-Inbound------route[12]) 我们允许通话从外部域到我们自己的话机。这个配置会让许多垃圾信息传到我们的话机上,但是实际情况却不是这样。我相信这样做的收益远大于风险。以后到底会怎样,谁也不知道。对于我来说比较合理的是,开放的接收通话就应该像开放的接收传统通话,开放的接收emails一样。 route[12] { # From an external domain -> inbound # Verify aliases, if found replace R-URI. lookup("aliases"); if (!lookup("location")) { sl_send_reply("404", "Not Found"); exit; }; route(1); } 带外到带外——route[13](Outbound-to-Outbound------route[13]) 我们不想做一个开放的中继器来对外部消息进行中继。如果下面的配置没有进行,那么其他人就能够使用我们的代理来匿名的路由通话。 route[13] { #From an external domain outbound #we are not accepting these calls sl_send_reply("403", "Forbidden"); exit; } 当操作SIP代理时,你应该确认的是一个有效的帐户不会被一个没有进行认证的用户所使用。Check_to()和check_from()函数被用来将SIP用户通认证用户做映射。SIP用户在FROM和TO头域中,而auth用户仅仅用来进行认证(Authorize头域)并有自己的密码。在当前的例子中,此函数检查SIP用户同auth用户是否相同。这就能够避免一个用户使用另一个用户的认证信息。这些函数通过URI_DB模块进行使能。 有些情况下,你想要允许一个用户拥有多个地址,譬如和一个主地址相关的电话号码。你可以使用别名来实现这个目的。 为了添加别名,使用下面命令: #openserctl alias add flavio@asteriskguide.com sip:1000@asteriskguide.com database engine 'MYSQL' loaded Control engine 'FIFO' loaded MySql password for user 'openser@localhost': lookup("aliases"); 函数lookup(“aliases”)检查数据库中的别名表,如果一个注册者被找到,就将其别名翻译成正规的地址(订阅列表中的地址)。这个特性还能够被用来将DIDs重定向到最终用户。还有一个Alias_db模块。它不是从内存而是数据库中直接对别名进行搜索。牺牲了一点性能的好处就是,它能够简化数据库中别名的直接准备。 按照RFC3261,cancel请求消息和INVITE请求按照同一种方式进行路由。下面的脚本检查CANCEL请求是否与现存的事务相匹配,并且关注所有的必要的路由。有时候,我们需要进行一些与现存事务相关的重传。在这种情况下,函数t_check_trans()将处理之并退出脚本。 #CANCEL processing if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); # ------------------ module loading ---------------------------------- #set module path mpath="//lib/openser/modules/" # Uncomment this if you want to use SQL database loadmodule "mysql.so" loadmodule "sl.so" loadmodule "tm.so" loadmodule "rr.so" loadmodule "maxfwd.so" loadmodule "usrloc.so" loadmodule "registrar.so" loadmodule "textops.so" loadmodule "mi_fifo.so" loadmodule "uri.so" loadmodule "uri_db.so" loadmodule "domain.so" # Uncomment this if you want digest authentication # mysql.so must be loaded ! loadmodule "auth.so" loadmodule "auth_db.so" # ----------------- setting module-specific parameters --------------- # -- mi_fifo params -- modparam("mi_fifo", "fifo_name", "/tmp/openser_fifo") # -- usrloc params -- #modparam("usrloc", "db_mode", 0) # Uncomment this if you want to use SQL database # for persistent storage and comment the previous line modparam("usrloc", "db_mode", 2) # -- auth params -- # Uncomment if you are using auth module # modparam("auth_db", "calculate_ha1", 0) # # If you set "calculate_ha1" parameter to yes, # uncomment also the following parameter) # #modparam("auth_db", "password_column", "password") # -- rr params -- # add value to ;lr param to make some broken UAs happy modparam("rr", "enable_full_lr", 1) # ------------------------- request routing logic ------------------- # main routing logic route{ # initial sanity checks -- messages with # max_forwards==0, or excessively long requests if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; }; if (msg:len >= 2048 ) { sl_send_reply("513", "Message too big"); exit; }; # we record-route all messages -- to make sure that # subsequent messages will go through our proxy; that's # particularly good if upstream and downstream entities # use different transport protocol if (!method=="REGISTER") record_route(); # subsequent messages withing a dialog should take the # path determined by record-routing if (loose_route()) { # mark routing logic in request append_hf("P-hint: rr-enforced\r\n"); route(1); }; #CANCEL processing if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } if (method=="REGISTER") { route(2); } else { route(3); }; } route[1] { # send it out now; use stateful forwarding as it works reliably # even for UDP2TCP if (!t_relay()) { sl_reply_error(); }; exit; } route[2] { # # -- Register request handler -- # if (is_uri_host_local()) { if (!www_authorize("", "subscriber")) { www_challenge("", "1"); exit; }; if (!check_to()) { sl_send_reply("40=3", "Forbidden"); exit; }; save("location"); exit; } else if { sl_send_reply("403", "Forbidden"); }; } route[3] { # # -- INVITE request handler -- # if (is_from_local()){ # From an internal domain -> check the credentials and the FROM if (!proxy_authorize("","subscriber")) { proxy_challenge("","1"); exit; } else if (!check_from()) { sl_send_reply("403", "Forbidden, use From=ID"); exit; }; consume_credentials(); # Verify aliases lookup("aliases"); if (is_uri_host_local()) { # -- Inbound to Inbound route(10); } else { # -- Inbound to outbound route(11); }; } else { # From an external domain -> do not check credentials #Verify aliases, if found replace R-URI. lookup("aliases"); if (is_uri_host_local()) { #-- Outbound to inbound route(12); } else { # -- Outbound to outbound route(13); }; }; } route[10] { #from an internal domain -> inbound #Native SIP destinations are handled using the location table append_hf("P-hint: inbound->inbound \r\n"); if (!lookup("location")) { sl_send_reply("404", "Not Found"); exit; }; route(1); } route[11] { # from an internal domain -> outbound # Simply route the call outbound using DNS search append_hf("P-hint: inbound->outbound \r\n"); route(1); } route[12] { # From an external domain -> inbound # Verify aliases, if found replace R-URI. lookup("aliases"); if (!lookup("location")) { sl_send_reply("404", "Not Found"); exit; }; route(1); } route[13] { #From an external domain outbound #we are not accepting these calls append_hf("P-hint: outbound->inbound \r\n"); sl_send_reply("403", "Forbidden"); exit; } 步骤1:试着用新的配置注册你的话机。你会注意到在你的话机注册时出现了一个错误。 步骤2:上面的配置目前使用的是domain.so模块。现在,为了认证,域必须在MySQL数据库中的域表中。 为了增加一个域,使用openserctl工具。 openserctl domain add your-ip-address openserctl domain add your-domain 对于每一个域重复上面的过程。 步骤3:再次尝试注册话机。现在注册过程将正常。 步骤1:为订阅者1000添加别名 #openserctl alias add john@youripordomain sip:1000@youripordomain database engine 'MYSQL' loaded Control engine 'FIFO' loaded MySql password for user 'openser@localhost': | 使用openserrw作为密码 | 步骤2:从注册为1001的软电话打给John 通话完整么?为什么? 在这一章中,你已经学会了如何将MySQL整合进OpenSER中。现在,我们的脚本可以认证用户,检查TO和FROM头域,按照带内和带外来处理通话了。重要的是要牢记域必须被插进数据库中,因为要支持多域。如果你改变了你的域或IP地址,请记着更新你的数据库。 ..................... display table content
Openserctl资源文件(Openserctl Resource File)
Openserctlrc文件(Opneserctlrc File)
使用带有认证功能的OpenSER(Using OpenSER with Authentication)
增强型脚本(Enhancing the Script)
管理多域(Managing Multiple Domains)
替代路线(Alternative Routes)
函数check_to()和check_from()(The Functions check_to() and check_from())
使用别名(Using Aliases)
处理CANCEL请求和重传(Handling CANCEL requests and retransmissions)
带有上面所有资源的完整脚本(Full Script with All the Resources Above)
实验——加强安全性(Lab——Enhancing the Security)
实验——使用别名(Lab——Using Aliases)
概要(Summary)