As soluções mais conhecidas atualmente para geração de relatórios do Squid são Sarg e SquidAnalyzer. São excelentes soluções, mas a geração estática, em formato HTML, é pouco flexível para ambientes mais complexos ou distribuídos. Buscando uma opção mais robusta, implementei uma solução baseada no Dashboard Kibana. Compartilharei algumas fontes de pesquisa e configuração adotada.
A lógica de configuração foi baseada no artigo de miteshshah e eticent.net.nz. Felizmente, a instalação do ELK pode ser simplificada. O repositório “artifacts” permite a instalação de todos os serviços necessários, reduzindo a intervenção manual.
Nesta primeira parte demonstrarei como instalar e configurar o Dashboard padrão. Na segunda, a instalação e configuração dos agentes filebeat nos nós distribuídos para exportar os logs ao servidor central. E, para finalizar, veremos como aplicar controle de acesso granular (direto no kibana, sem x-pack ou nginx).
Disponibilizei para download um pacote com os arquivos essenciais de configuração: elk.tar
Instalação e configuração do ELK (Kibana 5.1.2)
As etapas descritas, a seguir, foram executadas em um sistema Ubuntu. Para instalação em outras distribuições, recomendo a leitura da documentação disponível no site oficial.
1. Instalação do Java da Oracle (não é compatível com a versão 9)
sudo add-apt-repository -y ppa:webupd8team/java sudo apt-get update sudo apt-get -y install oracle-java8-installer
2. Instalação da chave pública do repositório em artifacts.elastic.co
sudo wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
3. Configuração do repositório e instalação dos serviços logstash, elasticsearch e kibana
sudo echo "deb https://artifacts.elastic.co/packages/5.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-5.x.list sudo apt-get update # Instalando os serviços sudo apt-get install logstash sudo apt-get install elasticsearch sudo apt-get install kibana # Habilitando os serviços no boot sudo systemctl enable logstash sudo systemctl enable elasticsearch sudo systemctl enable kibana
4. Configuração do Logstash (se fizer download, renomeie o arquivo para squid.conf)
vim /etc/logstash/conf.d/squid.conf
input { file { type => "squid" start_position => "beginning" path => [ "/var/log/squid/access.log" ] } } filter { if [type] == "squid" { grok { break_on_match => true match => [ "message", "%{NUMBER:timestamp}\s+%{NUMBER:response_time} %{IPORHOST:src_ip} %{WORD:squid_request_status}/%{NUMBER:http_status_code} %{NUMBER:reply_size_include_header:int} %{WORD:http_method} %{WORD:http_protocol}://%{HOSTNAME:dst_host}:%{NUMBER:tcp.port}%{NOTSPACE:request_url} %{NOTSPACE:user} %{WORD:squid}/(?:-|%{IP:dst_ip}) %{NOTSPACE:content_type}" ] match => [ "message", "%{NUMBER:timestamp}\s+%{NUMBER:response_time} %{IPORHOST:src_ip} %{WORD:squid_request_status}/%{NUMBER:http_status_code} %{NUMBER:reply_size_include_header:int} %{WORD:http_method} %{WORD:http_protocol}://%{HOSTNAME:dst_host}%{NOTSPACE:request_url} %{NOTSPACE:user} %{WORD:squid}/(?:-|%{IP:dst_ip}) %{NOTSPACE:content_type}" ] match => [ "message", "%{NUMBER:timestamp}\s+%{NUMBER:response_time} %{IPORHOST:src_ip} %{WORD:squid_request_status}/%{NUMBER:http_status_code} %{NUMBER:reply_size_include_header:int} %{WORD:http_method} %{HOSTNAME:dst_host}:%{NUMBER:tcp.port} %{NOTSPACE:user} %{WORD:squid}/(?:-|%{IP:dst_ip}) %{NOTSPACE:content_type}" ] add_tag => ["squid"] } geoip { source => "dst_ip" } } } output { elasticsearch { hosts => localhost } }
Concluída a alteração, reinicie o serviço com o comando:
service logstash restart ou systemctl restart logstash
5. Ajustes de permissão para acesso aos logs
Certifique-se de que o arquivo access.log esteja acessível ao usuário logstash.
chmod 644 /var/log/squid/access.logInclua o usuário logstash ao grupo elasticsearch.
usermod -g logstash -G elasticsearch logstash
6. Configuração do Kibana
Pela interface do Kibana, crie um index em “Management->Index Patterns->Add New“. Ou seja, em “index name or pattern” cadastre “logstash-*”. Vale lembrar que o botão “Create” ficará disponível desde que já exista um índice válido no elasticsearch.
vim /etc/kibana/kibana.yml
# Specifies the address to which the Kibana server will bind. IP addresses and host names are both valid values. # The default is 'localhost', which usually means remote machines will not be able to connect. # To allow connections from remote users, set this parameter to a non-loopback address. server.host: "ip_do_servidor" ... # The URL of the Elasticsearch instance to use for all your queries. elasticsearch.url: "http://localhost:9200"
Para importar os templates, acesse a url “http://ip_do_servidor:5601” e selecione “Management -> Saved objects“, clique no botão “Import” e selecione os arquivos.
Os templates estão disponíveis a partir dos links abaixo.
Antes de importar, renomeie os arquivos para remover a “extensão .txt”.- Download da definição de visualização: squid-visualization.json - Dowload da definição do dashboard: squid-dashboard.json
A estrutura do arquivo de visualização é a seguinte:
[ { "_id": "TOP-10-Requisições-MISS", "_type": "visualization", "_source": { "title": "TOP 10 Requisições MISS", "visState": "{\"title\":\"TOP 10 Requisições MISS\",\"type\":\"pie\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"isDonut\":false,\"legendPosition\":\"right\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"split\",\"params\":{\"filters\":[{\"input\":{\"query\":{\"query_string\":{\"query\":\"squid_request_status: 'TCP_MISS'\",\"analyze_wildcard\":true}}}}],\"row\":true}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"dst_host.keyword\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"TOP 10 Requisições MISS\"}}],\"listeners\":{}}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"filter\":[]}" } } }, { "_id": "Método-de-conexão", "_type": "visualization", "_source": { "title": "Método de conexão", "visState": "{\"title\":\"Método de conexão\",\"type\":\"pie\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"isDonut\":false,\"legendPosition\":\"right\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"split\",\"params\":{\"filters\":[{\"input\":{\"query\":{\"query_string\":{\"query\":\"type: 'squid'\",\"analyze_wildcard\":true}}}}],\"row\":true}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"http_method.keyword\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Método de conexão\"}}],\"listeners\":{}}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}" } } }, { "_id": "Top-10-Mais-acessados", "_type": "visualization", "_source": { "title": "Top 10 Mais acessados", "visState": "{\"title\":\"Top 10 Mais acessados\",\"type\":\"pie\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"isDonut\":false,\"legendPosition\":\"right\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"split\",\"params\":{\"filters\":[{\"input\":{\"query\":{\"query_string\":{\"query\":\"type: 'squid'\",\"analyze_wildcard\":true}}}}],\"row\":true}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"dst_host.keyword\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"TOP 10 Mais acessados\"}}],\"listeners\":{}}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}" } } }, { "_id": "Consumo-de-banda-slash-Endereço", "_type": "visualization", "_source": { "title": "Consumo de banda / Endereço", "visState": "{\"title\":\"Consumo de banda / Endereço\",\"type\":\"histogram\",\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"defaultYExtents\":false,\"legendPosition\":\"right\",\"mode\":\"stacked\",\"scale\":\"linear\",\"setYExtents\":false,\"shareYAxis\":true,\"times\":[],\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"sum\",\"schema\":\"metric\",\"params\":{\"field\":\"reply_size_include_header\",\"customLabel\":\"Bytes\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"src_ip.keyword\",\"size\":20,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Endereço\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"src_ip.keyword\",\"size\":20,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Endereço\"}}],\"listeners\":{}}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"filter\":[]}" } } }, { "_id": "HTTP-Status-Code", "_type": "visualization", "_source": { "title": "HTTP Status Code", "visState": "{\"title\":\"HTTP Status Code\",\"type\":\"pie\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"isDonut\":true,\"legendPosition\":\"right\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"split\",\"params\":{\"filters\":[{\"input\":{\"query\":{\"query_string\":{\"query\":\"type: 'squid'\",\"analyze_wildcard\":true}}}}],\"row\":true}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"http_status_code.keyword\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"HTTP Status Code\"}}],\"listeners\":{}}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}" } } }, { "_id": "HTTP-Request-Status", "_type": "visualization", "_source": { "title": "HTTP Request Status", "visState": "{\"title\":\"HTTP Request Status\",\"type\":\"pie\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"isDonut\":true,\"legendPosition\":\"right\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"filters\",\"schema\":\"split\",\"params\":{\"filters\":[{\"input\":{\"query\":{\"query_string\":{\"query\":\"type: 'squid'\",\"analyze_wildcard\":true}}}}],\"row\":true}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"squid_request_status.keyword\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"HTTP Request Status\"}}],\"listeners\":{}}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}" } } }, { "_id": "Incidentes-slash-Destino", "_type": "visualization", "_source": { "title": "Incidentes / Destino", "visState": "{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"3\",\"params\":{\"filters\":[{\"input\":{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"type: 'squid'\"}}}}],\"row\":true},\"schema\":\"split\",\"type\":\"filters\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"autoPrecision\":true,\"field\":\"geoip.location\",\"precision\":2},\"schema\":\"segment\",\"type\":\"geohash_grid\"}],\"listeners\":{},\"params\":{\"addTooltip\":true,\"heatBlur\":15,\"heatMaxZoom\":16,\"heatMinOpacity\":0.1,\"heatNormalizeData\":true,\"heatRadius\":25,\"isDesaturated\":false,\"legendPosition\":\"bottomright\",\"mapCenter\":[15,5],\"mapType\":\"Scaled Circle Markers\",\"mapZoom\":2,\"wms\":{\"enabled\":false,\"options\":{\"attribution\":\"Maps provided by USGS\",\"format\":\"image/png\",\"layers\":\"0\",\"styles\":\"\",\"transparent\":true,\"version\":\"1.3.0\"},\"url\":\"https://basemap.nationalmap.gov/arcgis/services/USGSTopo/MapServer/WMSServer\"}},\"title\":\"Incidentes / Destino\",\"type\":\"tile_map\"}", "uiStateJSON": "{\"mapCenter\":[14.944784875088372,5.09765625]}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"filter\":[]}" } } }, { "_id": "Acessos-bloqueados", "_type": "visualization", "_source": { "title": "Acessos bloqueados", "visState": "{\"title\":\"Acessos bloqueados\",\"type\":\"histogram\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"dst_host.keyword\",\"size\":30,\"order\":\"desc\",\"orderBy\":\"1\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"dst_host.keyword\",\"size\":30,\"order\":\"desc\",\"orderBy\":\"1\"}}],\"listeners\":{}}", "uiStateJSON": "{}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"squid_request_status:TCP_DENIED\",\"analyze_wildcard\":true}},\"filter\":[]}" } } }, { "_id": "Registro-de-acessos", "_type": "visualization", "_source": { "title": "Registro de acessos", "visState": "{\"title\":\"Registro de acessos\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showMeticsAtAllLevels\":false,\"showPartialRows\":false,\"showTotal\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"bucket\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{},\"customLabel\":\"Horário\"}},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"host.keyword\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Servidor Proxy\"}},{\"id\":\"4\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"user.keyword\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Usuário\"}},{\"id\":\"5\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"src_ip.keyword\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Endereço de origem\"}},{\"id\":\"6\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"dst_host.keyword\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Domínio FQDN\"}},{\"id\":\"7\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"bucket\",\"params\":{\"field\":\"squid_request_status.keyword\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\",\"customLabel\":\"Requisição\"}}],\"listeners\":{}}", "uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}", "description": "", "version": 1, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}},\"filter\":[]}" } } }
Hello, thanks for your provided the detailed document, I just configured ELK and Squid as your document, but now I have two questions.
1). I changed “output” on Squid.conf file, it’s:
output {
if [type] == “squid-log” {
elasticsearch {
index => “squid-log”
hosts => [“localhost:9200”]
}
stdout { codec => rubydebug }
}
}
And when created index(squid-log) on Kibana, the error information is: Unable to fetch mapping. Do you have indices matching the pattern?
2). The second questions is, what does the useful of squid-visualization.json file ? I just imported squid-dashboard.json.
Thanks for your time.
Hi,
Check if your index was created correctly:
This command should return something like this:
If you intend to use the “curator” tool (for retention periods), you should setup your index with a time id, for example:
index => “logstash-%{type}-%{+YYYY.MM.dd}”
The squid-visualization json script will define the graphs structure that are showed in squid-dashboard. So, you need squid-visualization too!
Muito obrigado pelo tutorial.
Estou tendo alguns problemas:
1 – erro constante “agg.schema is undefined”
2 – quando clico em “Dashboard” nenhuma informação é carregadas nos campos. “Top 10…” stc estão lá, mas ficam em “loading…” e nada aparece.
Rodei o comando
curl ‘localhost:9200/_cat/indices?v’ 2>/dev/null | head -10
Mas não tive absolutamente nenhuma resposta.
Espero que possa ajudar. Obrigado!
Boa tarde,
Primeiro, valide o funcionamento do logstash:
tail -f /var/log/logstash/logstash-plain.log
“Preferencialmente, reinicie o serviço antes do comando tail. Assim, qualquer erro de configuração será exibido logo no início“.
Caso seja constatado erro de conexão com o elasticsearch, reinicie o serviço elasticsearch e faça outro tail:
tail -f /var/log/elasticsearch/kibana-squidproxy.log
No meu caso, o nome com “kibana-squidproxy” vem da opção cluster.name (em /etc/elasticsearch/elasticsearch.yml)
Você também pode usar o comando netstat para verificar se os serviços estão disponíveis:
root@elk-server:~# netstat -ntlp | grep java
tcp6 0 0 10.x.y.70:9200 :::* OUÇA 1003/java
tcp6 0 0 127.0.0.1:9200 :::* OUÇA 1003/java
tcp6 0 0 10.x.y.70:9300 :::* OUÇA 1003/java
tcp6 0 0 127.0.0.1:9300 :::* OUÇA 1003/java
tcp6 0 0 :::5044 :::* OUÇA 943/java
tcp6 0 0 127.0.0.1:9600 :::* OUÇA 943/java
Amigo , poderia adaptar esse mesmo procedimento ao ELK 6 ?
A princípio, a lógica se mantém. Ainda não estou trabalhando na versão 6, mas farei um lab para atualização do projeto e qualquer coisa compartilho as diferenças. Já mudei bastante coisa e otimizei para trabalhar em cluster também.