Kamailio: Proxy Simples para Asterisk

Neimar Avila
4 min readMay 11, 2020

Cada dia mais apaixonado pelo Kamailio. Segue um “pequeno” howto para utilização do mesmo com o Asterisk, fazendo o proxy de todos os pacotes SIP.

Qual o Objetivo? Interceptação de pacotes maliciosos, reescrita de cabeçalhos e Topology Hiding (Opcional).

Este tutorial SOMENTE funciona com o Asterisk e o Kamailio rodando na mesma máquina.
Utilizaremos o cenário em que a mesma estará atrás de NAT.

Não faz parte do escopo deste howto a instalação e a configuração do Asterisk.

Foi utilizado o sistema operacional Centos 7, podendo ser adaptado para qualquer outro sistema operacional.

Utilizando o princípio KISS, o Kamailio será instalado por meio de pacotes RPM disponibilizados em: https://www.kamailio.org/wiki/packages/rpms

Para simplicidade total, não será utilizado RTPProxy (e/ou RTPEngine), e os “SIP 183/200" serão alterados para o IP Público diretamente na configuração.

Como o Asterisk e o Kamailio estarão rodando na mesma máquina, será necessário alterar a porta SIP do asterisk para outra porta.

A alteração deverá ser executada no seguinte arquivo (Escolha a sua configuração preferida):
chan_sip: /etc/asterisk/sip.conf

...
udpbindaddr:0.0.0.0:5062
...

chan_pjsip: /etc/asterisk/pjsip.conf

[system-udp]
type=transport
protocol=udp
bind=0.0.0.0:5062

O Kamailio deverá ser instalado utilizando os seguintes comandos:

cd /etc/yum.repos.dwget http://download.opensuse.org/repositories/home:/kamailio:/v5.3.x-rpms/CentOS_7/home:kamailio:v5.3.x-rpms.repoyum install -y kamailio

Após a instalação, iremos substituir o arquivo /etc/kamailio/kamailio.cfg para o conteúdo abaixo:

#!define KAMAILIO_IP_EXTERNO "xxx.xxx.xxx.xxx"
#!define ASTERISK_IP "yyy.yyy.yyy.yyyy"
#!define ASTERISK_PORTA 5062
#!define DOMINIO "meudominio.meusite.com.br"
#!define FLAG_FROM_ASTERISK 1
#!define FLAG_FROM_USUARIO 2
# ------------------ Carregamento de Modulos ----------------------------------
loadmodule "tm.so"
loadmodule "rr.so"
loadmodule "pv.so"
loadmodule "sl.so"
loadmodule "maxfwd.so"
loadmodule "nathelper.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "path.so"
loadmodule "pike.so"
loadmodule "htable.so"
# Alterar
listen=udp:yyy.yyy.yyy.yyyy:5060 advertise xxx.xxx.xxx.xxx:5060
auto_aliases=no
server_id=0
server_signature=no
sip_warning=0
alias=DOMINIOlog_facility=LOG_LOCAL0
log_prefix="{$mt $hdr(CSeq) $ci} "
modparam("pike", "sampling_time_unit", 2)
modparam("pike", "reqs_density_per_unit", 16)
modparam("pike", "remove_latency", 4)
modparam("htable", "htable", "ipban=>size=8;autoexpire=300;")
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1)
modparam("nathelper|registrar", "received_avp", "$avp(s:rcv)")
route {# Checagem de Sanity
route(VALIDACOES_SEGURANCA);
# Processamento de Mensagens CANCEL
if (is_method("CANCEL")) {
if (t_check_trans()) {
t_relay();
}
exit;
}
# Checar a Origem e Setar as Flags de Acordo
route(CHECK_IP_ORIGEM);
route(WITHINDLG);
t_check_trans();
if (is_method("INVITE|REFER")) {
record_route();
}

if (is_method("REGISTER")) {
add_path();
}
if (isflagset(FLAG_FROM_ASTERISK)) {
t_on_reply("REPLY_EXTERNO");
} else {
t_on_reply("AJUSTAR_SDP_ASTERISK");
$du = "sip:" + ASTERISK_IP + ":" + ASTERISK_PORTA;
}
route(RELAY);}route[VALIDACOES_SEGURANCA]
{
if(src_ip!=myself){
if($sht(ipban=>$si)!=$null)
{
xdbg("ALERTA: REQUISICAO VINDA DE IP JA BLOQUEADO METODO: $rm DE: $fu (IP:$si:$sp)\n");
exit;
}
if (!pike_check_req())
{
xlog("L_ALERT","ALERTA: BANINDO IP: METODO $rm DE $fu (IP:$si:$sp)\n");
$sht(ipban=>$si) = 1;
exit;
}
}
if (!sanity_check()) {
xlog("L_WARN", "$ci|end|CHEGOU UMA MENSAGEM INSANA AQUI.");
exit;
}
if (!mf_process_maxfwd_header("10")) {
xlog("L_WARN", "$ci|end|MUITOS HOPS");
send_reply("483", "Muitos Hops. Desistindo");
exit;
}
if($ua =~ "friendly-scanner|sipcli|VaxSIPUserAgent|iWar|sipvicious|sipsak|sundayddr|pplsip") {
xlog("L_INFO","BLOQUEIO: SCANNER SIP: $rm from $ua (IP:$si:$sp)\n");
# Descartando Pacotes de scanners silenciosamente. Caso Queira Responder, descomente a linha abaixo.
# sl_send_reply("200", "OK");
exit;
}
if($au =~ "(\=)|(\-\-)|(')|(\#)|(\%27)|(\%24)" and $au != $null) {
xlog("L_INFO","BLOQUEIO: SQL INJECTION: (IP:$si:$sp)");
exit;
}
if($(hdr(Record-Route)[0]{nameaddr.uri}) != $si and $(hdr(Record-Route)[0]{nameaddr.uri}) != $null) {
xlog("L_INFO","BLOQUEIO: ATAQUE SPOOFING: (IP:$si:$sp)");
exit;
}
if ((DOMINIO != $fd)&&(!route(FROM_ASTERISK))) {
xlog("L_INFO","ACESSO VINDO PARA IP E NAO PARA O DOMINIO: $si\n");
# Descomentar as Linhas abaixo para Responder (Caso Queira) e BANIR o IP PELO TEMPO DO PIKE (ver htable ipban)
sl_send_reply("494", "OOPS. NAO ACEITO IP. CONFIGURE DOMINIO");
# $sht(ipban=>$si) = 1;
exit;
}
}
route[CHECK_IP_ORIGEM]
{
if (route(FROM_ASTERISK)) {
setflag(FLAG_FROM_ASTERISK);
append_hf("X-Origem: Asterisk\r\n");
if(has_body("application/sdp")){
fix_nated_sdp("2", KAMAILIO_IP_EXTERNO);
}
} else {
setflag(FLAG_FROM_USUARIO);
append_hf("X-Origem: Externo\r\n");
}
}
route[FROM_ASTERISK]
{
if (($si == ASTERISK_IP)&&($sp == ASTERISK_PORTA) ) {
return 1;
} else {
return -1;
}
}
route[WITHINDLG]
{
if (has_totag()) {
if (loose_route()) {
route(RELAY);
} else {
if (is_method("NOTIFY")) {
route(RELAY);
}
if (is_method("SUBSCRIBE") && uri == myself) {
exit;
}
if (is_method("ACK")) {
if (t_check_trans()) {
t_relay();
exit;
} else {
exit;
}
}
sl_send_reply("404","Not here");
}
exit;
}
}
onreply_route[REPLY_EXTERNO]
{
route(TESTE_CORRECAO_NAT);
}
onreply_route[AJUSTAR_SDP_ASTERISK]
{
if(has_body("application/sdp")){
fix_nated_sdp("2", KAMAILIO_IP_EXTERNO);
}
}route[TESTE_CORRECAO_NAT]
{
if (nat_uac_test("3")) {
if (is_method("REGISTER")) {
fix_nated_register();
} else {
fix_nated_contact();
}
force_rport();
}
if (has_body("application/sdp") && nat_uac_test("8")) {
fix_nated_sdp("10");
}
}
route[RELAY]
{
if (!t_relay()) {
sl_reply_error();
}
exit;
}

Os seguintes parâmetros devem ser ajustados para os dados de sua aplicação (O # no início da linha deverá ser mantido):

#!define KAMAILIO_IP_EXTERNO "xxx.xxx.xxx.xxx"
#!define ASTERISK_IP "yyy.yyy.yyy.yyyy"
#!define ASTERISK_PORTA 5062
#!define DOMINIO "meudominio.meusite.com.br"

Além disso, deve ser alterada a linha listen:

listen=udp:yyy.yyy.yyy.yyyy:5060 advertise xxx.xxx.xxx.xxx:5060

Para os dados de sua placa de rede e ip válido.

O Kamailio deve ser iniciado por meio do seguinte comando:

service kamailio start

Verifique se o mesmo está up por meio do seguinte comando:

ps aux |grep kamailio

As mensagens de log/erro serão listadas em:
/var/log/messages

A partir deste momento, você deverá configurar o softphone para que possa fazer uso do domínio definido e não mais por IP. Qualquer tentativa de acesso ao IP Público do Kamailio será desprezada, evitando trafego desnecessário ao Asterisk.

Edit: Para esconder a sua topologia (Esconder o IP do Asterisk), é possível mascarar a mesma utilizando o módulo TOPOH.

Abaixo de loadmodule “htable.so”, adicione a seguinte linha:

loadmodule "topoh.so"

E abaixo de:

modparam("nathelper|registrar", "received_avp", "$avp(s:rcv)")

Adicione as seguintes linhas:

modparam("topoh", "mask_key", "umachavequalquer")
modparam("topoh", "mask_ip", "127.0.0.8")

Atenciosamente,

Neimar L. Avila
neimar.avila@gmail.com

--

--