Lean Threat Intelligence Part 2: The foundation
*Este es el segundo artículo de una serie acerca de la inteligencia sobre amenazas eficiente. Consulta la [parte 1](https://www.fastly.com/blog/lean-threat-intelligence-part-1-plan) y la [parte 3](https://www.fastly.com/blog/lean-threat-intelligence-part-3-battling-log-absurdity-kafka).*
En [Inteligencia sobre amenazas eficiente \(parte 1\): el plan](https://www.fastly.com/blog/lean-threat-intelligence-part-1-plan), hablamos sobre el flujo de trabajo general que utiliza el equipo de inteligencia sobre amenazas de Fastly para planificar proyectos. En nuestro ejemplo, tenemos un agregador de syslog central que agrupa los registros de todos nuestros servicios. Ahora podemos utilizar esta topología para avanzar en el diagrama de sistemas y pasar al siguiente paso: seleccionar la tecnología.
![systems](//images.contentful.com/6pk8mg3yh2ee/4MbYA7VwgEyyK0aWOyQsuA/1f88db5a85742f440bfa8ac00d28a04b/systems.png)
En la entrada anterior se daba por hecho que hay un servidor agregador de syslog en este entorno. En nuestro caso, se trata de un servidor rsyslog donde se almacena un conjunto de registros que se someterá a un análisis más profundo. Dicho análisis nunca debería ser `grep A.B.C.D. foo.log;`, sino que deberíamos aprovechar al máximo las tecnologías que permiten escudriñar tal cantidad de datos de forma intuitiva para los humanos. Aquí es donde interviene el gestor de registros.
### «Nosotros no sembramos»: el lema de la nueva «Casa Graylog»
A la hora de seleccionar tecnologías de gestión de registros, deben tomarse algunas decisiones sobre cómo se quieren gestionar desde el punto de vista de la infraestructura. Hay empresas de SaaS que prestan estos servicios de gestión de infraestructura, y hay gestores de registros de código abierto que ofrecen a los usuarios un control granular sobre la infraestructura. Al final, no es más que una cuestión de recursos, que incluyen el presupuesto, las personas que gestionan la infraestructura y la política. ¿Te gastarías el dinero en un proveedor de SaaS? Si no tienes previsto hacerlo, piensa que un stack de código abierto puede ser más costoso en términos de personas e infraestructura. Por último, ¿vas a alojar datos sensibles de registro y auditoría en una infraestructura que no es de tu propiedad?
Después de plantearnos todo esto, decidimos que Graylog sería la tecnología de gestión de registros de Fastly. Y estos son los motivos:
* Queríamos restringir el acceso a registros confidenciales y tener la propiedad de su seguridad para reducir la superficie de ataque.
* En Fastly, contamos con equipos que trabajan estrechamente con el equipo de seguridad para gestionar infraestructuras como esta en el entorno de producción.
* Graylog utiliza el conocido stack tecnológico Elasticsearch que facilita enormemente la indexación y la búsqueda de registros.
* Graylog también usa un competidor de Kibana para la interfaz de su servidor web, que ofrece características útiles como la gestión de cuentas y alertas nativas mediante correo electrónico y PagerDuty.
* Graylog utiliza flujos, que pueden emplearse como un mecanismo de control de acceso para diversos usuarios que ven determinadas subsecciones de registros en función del equipo al que pertenecen. Esto no solo ofrece seguridad, sino que pone nuestro sistema a disposición de varios equipos.
Si enviamos registros a Graylog, resolvemos los casos de usuario uno y dos [de la entrada anterior](https://www.fastly.com/blog/lean-threat-intelligence-part-1-plan). Esto supone una gran ventaja para nosotros como equipo, especialmente porque Graylog es un stack bien mantenido y documentado, y una opción muy interesante dadas nuestras limitaciones.
Ahora que ya hemos elegido Graylog, tenemos que desplegarlo y enviar mensajes de syslog desde el agregador a Graylog. Y así es como lo hacemos.
### La dotación: desplegar Graylog con Chef/Vagrant
Por suerte, el servidor de Graylog incluye casi todo y solo se sirve de Elasticsearch y MongoDB como ayudas externas. Elasticsearch es el índice de Lucene donde se almacenan todos los datos indexados por Graylog; MongoDB proporciona una base de datos para la configuración de Graylog, lo que es especialmente importante cuando se ejecutan varios servidores de Graylog con replicación de MongoDB.
La interfaz de la web de Graylog no depende externamente de nada, y solo se sirve de la API del servidor de Graylog.
Para simplificar, vamos a crear un servidor de Graylog utilizando Vagrant y chef\-solo, lo que nos permite aprovechar el conjunto de funciones `chef-client` sin tener que depender de un servidor Chef. También usaremos las recetas de Chef de acceso público para poner en marcha rápidamente un clúster de Graylog.
Vamos a crear el clúster desde cero: empezaremos con Java, Elasticsearch y MongoDB, seguiremos con el servidor de Graylog y finalizaremos con la web de Graylog.
Para empezar, vamos a instalar Elasticsearch.
Berksfile es un gestor de dependencias de guías de Chef que indica a Vagrant dónde están las recetas. En nuestro caso, como no definimos nada específico para Java, buscará la guía de Java en supermarket.chef.io.
A continuación, Vagrantfile le indica a Vagrant y, por ende, a Chef, cómo desplegar y aprovisionar el nodo. En este ejemplo utilizaremos `chef.add_recipe` para agregar Java y nuestra receta contenedora de Elasticsearch a la ejecución de Chef. Además, especificaremos una configuración básica para las recetas en nuestro bloque `chef.json`. Nuestra sección de Java especifica la versión de Java que queremos instalar \(en este caso, Oracle 8\) y acepta los términos de la descarga. Vagrant establece la interfaz principal en una red solo para host, así que no nos quedará más remedio que configurar Elasticsearch para que escuche en nuestra segunda interfaz. Por suerte, eso solo supone unas cuantas líneas más en nuestro archivo Vagrantfile.
**Berksfile**
source "https://supermarket.chef.io"
cookbook 'java'
cookbook 'elasticsearch', git: 'https://github.com/elastic/cookbook-elasticsearch.git'
cookbook 'blog_elasticsearch', path: '~/cookbooks/blog_elasticsearch'
En este fragmento, vamos a https://supermarket.chef.io y le decimos a Chef que recupere las guías que no encuentra en la ruta local. Extraemos Java de supermarket; elasticsearch, de git; y blog\_elasticsearch se extrae localmente. De momento, no nos preocuparemos de la ruta de blog\_elasticsearch, ya que veremos un ejemplo de guía en los siguientes pasos.
**Vagrantfile**
es_servers = { :elasticsearch1 => '192.168.33.30',
:elasticsearch2 => '192.168.33.31',
:elasticsearch3 => '192.168.33.32'
}
es_servers.each do |es_server_name, es_server_ip|
config.vm.define es_server_name do |es_config|
es_config.vm.box = "ubuntu/trusty64"
es_config.vm.network "private_network", ip: es_server_ip.to_s
es_config.vm.provider "virtualbox" do |v|
v.memory = 1024
v.cpus = 2
end
es_config.vm.hostname = es_server_name.to_s
es_config.vm.provision :chef_solo do |chef|
chef.add_recipe "java"
chef.add_recipe "blog_elasticsearch"
chef.json = {
:java => {
:oracle => {
:accept_oracle_download_terms => true
},
:install_flavor => "oracle",
:jdk_version => "8"
},
:elasticsearch => {
:configuration => {
:network_host => es_server_ip.to_s,
}
}
}
end
end
end
Ahora vamos a crear el contenedor de Elastichsearch. Debemos ir al directorio de guías y ejecutar el siguiente comando:
chef generate cookbook blog_elasticsearch
Se creará un esqueleto de guía que podremos modificar. Tendremos que modificar algunos archivos para que nuestra guía funcione: `metadata.rb` y `recipes/default.rb`.
**metadata.rb**
depends 'java'
depends 'elasticsearch'
**recipes/default.rb**
elasticsearch_user 'elasticsearch'
elasticsearch_install 'my_es_installation' do
type :package # type of install
version '1.7.3'
action :install # could be :remove as well
end
elasticsearch_configure 'elasticsearch' do
configuration ({
'cluster.name' => 'graylog2',
'network.host' => node[:elasticsearch][:configuration][:network_host] || node['ipaddress'],
'discovery.zen.ping.multicast.enabled' => true,
'discovery.zen.minimum_master_nodes' => 1
})
end
elasticsearch_service 'elasticsearch' do
action :start
end
Esta guía extrae recetas de Java y de Elastichsearch de niveles superiores, y creamos nuestra propia receta predeterminada para establecer algunas opciones de configuración básicas. Graylog requiere un nombre de clúster al que conectarse, así que llamaremos a nuestro clúster «graylog2». A continuación, configuramos el host de red de Elasticsearch en una dirección IP establecida por quien esté utilizando esta guía, o la dirección IP del nodo, si no se ha proporcionado una. Por último, para que este clúster sea operativo, nos decidimos por un solo nodo maestro.
Ahora que ya hemos organizado Elasticsearch, por fin podemos empezar a instalar Graylog. Empecemos con el servidor de Graylog. Debemos añadir `cookbook ‘graylog2'` al Berksfile que tenemos.
**Berksfile**
source "https://supermarket.chef.io"
cookbook 'java'
cookbook 'elasticsearch', git: 'https://github.com/elastic/cookbook-elasticsearch.git'
cookbook 'blog_elasticsearch', path: '~/cookbooks/blog_elasticsearch'
cookbook 'graylog2'
A continuación, añadimos el siguiente código después del bloque de Elasticsearch.
**Vagrantfile**
config.vm.define "graylog-server" do |graylogserver|
graylogserver.vm.box = "ubuntu/trusty64"
graylogserver.vm.network "private_network", ip: "192.168.33.20"
graylogserver.vm.network "forwarded_port", guest: 1514, host: 1514, protocol: "tcp"
graylogserver.vm.network "forwarded_port", guest: 1514, host: 1514, protocol: "udp"
graylogserver.vm.provider "virtualbox" do |v|
v.memory = 1024
v.cpus = 2
end
graylogserver.vm.hostname = "graylog-server"
graylogserver.vm.provision :chef_solo do |chef|
chef.add_recipe "java"
chef.add_recipe "blog_elasticsearch"
chef.add_recipe "mongodb"
chef.add_recipe "graylog2"
chef.add_recipe "graylog2::server"
chef.json = {
:java => {
:oracle => {
:accept_oracle_download_terms => true
},
:install_flavor => "oracle",
:jdk_version => "8",
},
:graylog2 => {
:password_secret => "", # pwgen -s 96 1
:root_password_sha2 => "", # echo -n yourpassword | shasum -a 256 | awk '{print $1}'
:elasticsearch => {
:discovery_zen_ping_unicast_hosts => '192.168.33.20:9300',
:network_host => '192.168.33.20'
},
:rest => {
:transport_uri => 'http://192.168.33.20:12900/',
:listen_uri => 'http://192.168.33.20:12900/'
},
:server => {
:java_opts => "-Djava.net.preferIPv4Stack=true"
}
},
:elasticsearch => {
:configuration => {
:network_host => "192.168.33.20",
}
}
}
end
end
El fragmento de Graylog del archivo Vagrantfile parece complejo, pero [graylog2\-cookbook](https://github.com/Graylog2/graylog2-cookbook) nos dará más flexibilidad para definir variables. Como Graylog depende de Elasticsearch y Java, gran parte del archivo Vagrantfile es idéntico. El resto de las opciones de Graylog enlazan Graylog a las interfaces correctas y permiten la comunicación con nuestro clúster de Elasticsearch. Si echamos un vistazo al bloque `chef.json`, vemos que estamos configurando variables específicas para este clúster: las dos contraseñas de graylog2 deben generarse desde el principio, pero pueden cambiarse más adelante. A continuación, configuramos un host de unidifusión para que Graylog pueda detectar nodos ES. Por último, configuramos la API de REST para permitir la comunicación con el servidor web de Graylog.
Y ya podemos instalar la web de Graylog.
config.vm.define "graylog-web" do |graylogweb|
graylogweb.vm.box = "ubuntu/trusty64"
graylogweb.vm.network :private_network, ip: "192.168.33.10"
graylogweb.vm.network "forwarded_port", guest: 9000, host: 9000, proto: "tcp"
graylogweb.vm.provider "virtualbox" do |v|
v.memory = 1024
v.cpus = 2
end
graylogweb.vm.hostname = "graylog-web"
graylogweb.vm.provision :chef_solo do |chef|
chef.add_recipe "java"
chef.add_recipe "graylog2"
chef.add_recipe "graylog2::web"
chef.json = {
:java => {
:oracle => {
"accept_oracle_download_terms" => true
},
:install_flavor => "oracle",
:jdk_version => "8",
},
"graylog2": {
"password_secret": "", # pwgen -s 96 1
"root_password_sha2": "", # echo -n yourpassword | shasum -a 256 | awk '{print $1}'
"web": {
"secret": "",
:server_backends => 'http://192.168.33.20:12900',
:timezone => "Etc/UTC"
}
}
}
end
end
Esta es la sección más corta de nuestro archivo Vagrantfile, ya que el servidor web de Graylog no es más que una interfaz basada en la API del servidor de Graylog. Tenemos que especificar los secretos de nuestro usuario administrador y permitir la comunicación entre los nodos de la web y del servidor.
Ahora que ya tenemos el archivo Vagrantfile y la guía contenedora, podemos ejecutar `vagrant up` en nuestro terminal para poner en marcha el clúster. Puede tardar unos minutos, así que paciencia.
Cuando haya terminado el proceso vagrant up, vamos a la interfaz web en el puerto 9000 \(en nuestro ejemplo es [http://192\.168\.33\.10:9000](http://192.168.33.10:9000)\), donde veremos la siguiente pantalla de bienvenida.
![graylog-login](//images.contentful.com/6pk8mg3yh2ee/5imp5OxrZuggW0COQc0eUO/76ca1d9ad7409c2ab85088282d3de44d/graylog-login.png)
El nombre de usuario será admin y la contraseña será la que se haya generado con el comando `echo -n yourpassword | shasum -a 256 | awk '{print $1}'`.
Después de iniciar sesión, hay que crear una entrada para que el sistema atienda las conexiones de datos. Vamos a System > Inputs y creamos una nueva entrada Syslog UDP. Dado que solo tenemos un servidor de Graylog de nodo único, solo tendremos que generar un nodo. Creamos un título para la entrada y cambiamos el puerto por un número mayor que 1024, puesto que Graylog no se ejecuta como usuario con privilegios. La entrada tendrá este aspecto:
![input-screen](//images.contentful.com/6pk8mg3yh2ee/2OKnVFPQpGUk6aQCYUAs0Q/e7589e85757699ab8d68c5c379b4476c/input-screen.png)
Después, hacemos clic en «launch» y nos aseguramos de que la entrada se inicia correctamente. Debería aparecer un indicador de estado de ejecución de color verde junto al nombre de la entrada.
![global-inputs](//images.contentful.com/6pk8mg3yh2ee/78ACRIlewg0QckwQ8weyya/6dce454fd0a74b446e9b17bcdb6a835c/global-inputs.png)
A continuación, para probar el despliegue, conviene generar algunos datos de prueba para enviarlos a Graylog. En la línea de comandos, ejecutamos el siguiente comando: `nc -w0 -u 192.168.33.20 1514 << < "Test Syslog Message"`. Si hacemos clic en «search», estos datos deberían autocompletarse en el clúster.
![histogram](//images.contentful.com/6pk8mg3yh2ee/4L4c3DbmTY66oMgGAIiWw0/b74420c06e34484383585c4d51258d9d/histogram.png)
Si hacemos clic en el mensaje, obtendremos más información de Graylog.
![timestamp](//images.contentful.com/6pk8mg3yh2ee/3Cf5BwzpMsiu4yw44iwmsY/beef9cd96d13345f0f8fe25e4972cf26/timestamp.png)
Como puedes ver, Graylog indexa y analiza el mensaje de syslog. Lo hace en la mayoría de las entradas y, en el caso de Syslog, funciona especialmente bien. Nuestro mensaje de syslog de ejemplo no contenía mucha información, pero ahora puedes hacer que rsyslog señale a tu máquina virtual de Graylog y probar algunos mensajes para ir conociendo las funciones de análisis de Graylog.
El Vagrantfile muestra una configuración a través de Virtualbox local que no es la ideal para despliegues en entornos de producción. Por suerte, Vagrant tiene proveedores de servicios en la nube como Google Compute Engine y EC2, así como soporte inmediato para Docker y Hyper\-V.
Al final de esta entrada encontrarás el Vagrantfile completo, por si quieres echarle un vistazo.
### Una base sobre la que construir: la cola de mensajes
Con este Vagrantfile y esta configuración, tenemos cubierto el requisito del gestor de registros del diagrama de sistemas. En lugar de ejecutar los comandos tail y grep en todos los mensajes de syslog, se puede utilizar el agregador de syslog para reenviar los registros al despliegue de Graylog. Así se pueden aprovechar los esquemas de alerta de Graylog, o incluso enlazar con Elasticsearch y utilizar el formato de datos estructurados para realizar análisis sin conexión.
Esta configuración del gestor de registros optimiza los procesos que requieren una investigación de registros, como la evaluación posterior a una interrupción del servicio o un incidente de seguridad. En cualquier programa de inteligencia sobre amenazas es importante eliminar tantos procesos manuales como sea posible sin sacrificar la visibilidad de tu infraestructura o datos. Los paneles, las búsquedas guardadas y las alertas, que son todos nativos en Graylog, pueden ayudarte a optimizar la inteligencia sobre amenazas.
Sin embargo, esta configuración presenta algún inconveniente. En primer lugar, no hay mecanismos de enriquecimiento que puedan ayudar con la búsqueda. Por ejemplo, podrías registrar intentos de inicio de sesión a través de SSH y luego realizar búsquedas de reputación IP y de DNS inversas para trasladar a un nivel superior los intentos de inicio de sesión fallidos si tienen carácter malicioso. En segundo lugar, los mensajes de syslog pueden generar demasiado ruido y provocar que las señales relevantes pasen desapercibidas. Si no se cuenta con un procesador de flujos que extraiga la señal, se podría estar perdiendo información vital sobre el estado de la telemetría.
Lee la [parte 3](https://www.fastly.com/blog/lean-threat-intelligence-part-3-battling-log-absurdity-kafka) de esta serie para saber cómo abordamos la parte relativa a la cola de mensajes del diagrama de sistemas. La cola de mensajes aporta lo siguiente a esta configuración:
1. Permite gestionar varios flujos de datos en canales y vías independientes, lo que refuerza la capacidad de Graylog de realizar extracciones o búsquedas en ellos.
2. Sirve como intermediario para insertar datos en una arquitectura de procesamiento de flujos, para su posterior enriquecimiento o análisis.
3. Permite gestionar un flujo de alertas: Graylog puede configurarse para emitir una alerta inmediata cuando llegan datos a través de un flujo específico, lo que simplifica las situaciones de alerta complejas en Graylog y las transfiere al código.
¡No os lo perdáis\!
### Anexo
**Berksfile**
source "https://supermarket.chef.io"
cookbook 'elasticsearch', git: 'https://github.com/elastic/cookbook-elasticsearch.git'
cookbook 'java'
cookbook 'fst_elasticsearch', path: '~/cookbooks/fst_elasticsearch'
cookbook 'graylog2'
**Vagrantfile**
Vagrant.configure(2) do |config|
config.vm.box = "ubuntu/trusty64"
config.berkshelf.enabled = true
if Vagrant.has_plugin?("vagrant-cachier")
config.cache.scope = :box
end
es_servers = { :elasticsearch1 => '192.168.33.30',
:elasticsearch2 => '192.168.33.31',
:elasticsearch3 => '192.168.33.32'
}
es_servers.each do |es_server_name, es_server_ip|
config.vm.define es_server_name do |es_config|
es_config.vm.box = "ubuntu/trusty64"
es_config.vm.network "private_network", ip: es_server_ip.to_s
es_config.vm.provider "virtualbox" do |v|
v.memory = 1024
v.cpus = 2
end
es_config.vm.hostname = es_server_name.to_s
es_config.vm.provision :chef_solo do |chef|
chef.add_recipe "java"
chef.add_recipe "blog_elasticsearch"
chef.json = {
:java => {
:oracle => {
:accept_oracle_download_terms => true
},
:install_flavor => "oracle",
:jdk_version => "8"
},
:elasticsearch => {
:configuration => {
:network_host => es_server_ip.to_s,
}
}
}
end
end
end
config.vm.define "graylog-server" do |graylogserver|
graylogserver.vm.box = "ubuntu/trusty64"
graylogserver.vm.network "private_network", ip: "192.168.33.20"
graylogserver.vm.network "forwarded_port", guest: 1514, host: 1514, protocol: "tcp"
graylogserver.vm.network "forwarded_port", guest: 1514, host: 1514, protocol: "udp"
graylogserver.vm.provider "virtualbox" do |v|
v.memory = 1024
v.cpus = 2
end
graylogserver.vm.hostname = "graylog-server"
graylogserver.vm.provision :chef_solo do |chef|
chef.add_recipe "java"
chef.add_recipe "blog_elasticsearch"
chef.add_recipe "mongodb"
chef.add_recipe "graylog2"
chef.add_recipe "graylog2::server"
chef.json = {
:java => {
:oracle => {
:accept_oracle_download_terms => true
},
:install_flavor => "oracle",
:jdk_version => "8",
},
:graylog2 => {
:password_secret => "", # pwgen -s 96 1
:root_password_sha2 => "", # echo -n yourpassword | shasum -a 256 | awk '{print $1}'
:elasticsearch => {
:discovery_zen_ping_unicast_hosts => '192.168.33.20:9300',
:network_host => '192.168.33.20'
},
:rest => {
:transport_uri => 'http://192.168.33.20:12900/',
:listen_uri => 'http://192.168.33.20:12900/'
},
:server => {
:java_opts => "-Djava.net.preferIPv4Stack=true"
}
},
:elasticsearch => {
:configuration => {
:network_host => "192.168.33.20",
}
}
}
end
end
config.vm.define "graylog-web" do |graylogweb|
graylogweb.vm.box = "ubuntu/trusty64"
graylogweb.vm.network :private_network, ip: "192.168.33.10"
graylogweb.vm.network "forwarded_port", guest: 9000, host: 9000, proto: "tcp"
graylogweb.vm.provider "virtualbox" do |v|
v.memory = 1024
v.cpus = 2
end
graylogweb.vm.hostname = "graylog-web"
graylogweb.vm.provision :chef_solo do |chef|
chef.add_recipe "java"
chef.add_recipe "graylog2"
chef.add_recipe "graylog2::web"
chef.json = {
:java => {
:oracle => {
"accept_oracle_download_terms" => true
},
:install_flavor => "oracle",
:jdk_version => "8",
},
"graylog2": {
"password_secret": "", # pwgen -s 96 1
"root_password_sha2": "", # echo -n yourpassword | shasum -a 256 | awk '{print $1}'
"web": {
"secret": "",
:server_backends => 'http://192.168.33.20:12900',
:timezone => "Etc/UTC"
}
}
}
end
end
end