Etiquetas

jueves, 24 de diciembre de 2015

Access Point / Repetidor Wi-Fi con raspberry PI y dongle RTL8192CU

A veces cuando viajamos queremos tener internet en varios dispositivos: notebooks, smartphones, etc y dependemos de un punto de acceso Wi-Fi distante que no tiene buena cobertura entonces contar con un repetidor que enrute el tráfico entre una interfaz (dongle RTL8192CU por ejemplo) y otra interfaz conectada a al wi-fi remota puede ser útil.
Esta entrada en el blog es más bien un paso a paso en español de información que está disponible en ingles. La primera parada en este camino es crear un AP Wi-Fi que enrutee a una red cableada. Luego modificaremos la configuración para hacer bridge a otra interfaz wireless y que la raspberry pi sirva como repetidor Wi-Fi. Para esto utilizaremos hostapd y eventualmente dnsmasq si hay necesidad de que el router (Rpi) asigne direcciones IP.


Antes de comenzar, unas breves palabras sobre el rendimiento de este dispositivo. La raspberry Pi no es el dispositivo ideal si buscamos un router de alto rendimiento. Inevitablemente, el rendimiento será algo menor que un dispositivo dedicado y diseñado para esa función. Dicho esto, invertir tiempo y algo de dinero en este proyecto de fin de semana tiene varias motivaciones: divertirnos en el proceso (habrá que compilar varios softs si usamos un dongle basado en el chipset RTL8192CU) y quizá dispongamos de alguna Raspberry Pi por ahí disponible para la diversión. Dicho sea de paso, siempre se aprende algo sobre networking con estos proyectos, así sean "just for fun".

Primero, crear un router wi-fi y red cableada. Manos a la obra

Para esta primera etapa, que nos servirá para probaer que hayamos configurado correctamente hostapd lo que haremos será: una interfaz wi-fi (ej wlan0) servirá como Access Point al cual se conectarán "clientes" usando encriptación WPA2 y autenticación basada en password. Muy similar a un router comercial convencional.

Primero, siguen las fuentes que me sirvieron para el paso a paso:

http://wannabe-nerd.tweakblogs.net/blog/10870/wifi-access-point-using-a-realtek-8192cu-based-usb-wifi-dongle-with-a-raspberry-pi.html

https://bogeskov.dk/UsbAccessPoint.html

Y especialmente este post me fue de suma utilidad, para mantener las cosas simples:

https://www.raspberrypi.org/forums/viewtopic.php?f=91&t=29752&start=75


Como dije, en mi caso debía usar un dongle marca TP-LINK modelo TL-WN823N, que está basado en el chipset RTL8192CU.

# lsusb
Bus 001 Device 004: ID 0bda:8178 Realtek Semiconductor Corp. RTL8192CU 802.11n WLAN Adapter

Este chipset tiene la particularidad de que el módulo que viene con el kernel no soporta el modo AP. Por lo que es necesario compilar el módulo.
Antes que nada, el "router" precisará que se establezca un bridge (dispositivo de capa 2 similar a un switch) que creará un puente entre dos interfaces. En esta primera etapa, entre wlan0 y eth0.

Instalamos las herramientas para bridges:
# apt-get install bridge-utils

Luego, configuramos la interfaz eth0 y el puente, editando /etc/network/interfaces:

# The loopback network interface
auto lo
iface lo inet loopback


# Setup bridge con bridge
allow-hotplug eth0
iface eth0 inet manual

auto br0
iface br0 inet static
bridge_ports wlan0 eth0
address 192.168.1.19
netmask 255.255.255.0
network 192.168.1.0
## isp router ip, 192.168.1.1 also runs DHCPD ##
gateway 192.168.1.1
dns-nameservers 192.168.1.1

En esta configuración estamos utilizando una dirección fija para la interfaz eth0 y otro dispositivo en la red (con dir 192.168.1.1) será el gateway hacia internet. También ese otro dispositivo se encargará de correr un servidor dhcp, que será el encargado de otorgar direcciones IP a los dispositos que se conecten al router (Raspberry Pi). Si fuera necesario correr el servidor dhcp en le propia raspberry habría que agregar dnsmasq, por ejemplo.

Compilar el modulo adecuado para el 8192CU

Debemos instalar la misma versión de gcc que se haya usado para compilar el kernel, en este caso podemos verla con:

$ cat /proc/version
Linux version 4.1.13+ (dc4@dc4-XPS13-9333) (gcc version 4.8.3 20140303 (prerelease) (crosstool-NG linaro-1.13.1+bzr2650 - Linaro GCC 2014.03) ) #826 PREEMPT Fri Nov 13 20:13:22 GMT 2015

Instalamos la versión adecuada:

# apt-get update
# apt-get install -y gcc-4.8 g++-4.8

Si este no es la versión default de gcc podemos alterar el symlink de /usr/bin/gcc

Siguiente instalamos rpi-source que es la herramienta que facilita la descarga del código fuente del kernel a nuestro dir home. Véase que mejor usar un usuario sin privilegios:

$ cd ~
$ sudo wget https://raw.githubusercontent.com/notro/rpi-source/master/rpi-source -O /usr/bin/rpi-source && sudo chmod +x /usr/bin/rpi-source && /usr/bin/rpi-source -q --tag-update

Acto seguido descargamos el source code del kernel

$ rpi-source

En nuestro directorio home, tendremos un directorio con un nombre linux-hash...blah creamos un symlink:

$ cd ~
$ ln -s linux-bc1669c846b629cface0aaa367afb2b9c6226faf linux

Ahora, compilamos el módulo:

cd linux
make mrproper

# config del kernel .config
zcat /proc/config.gz > .config
make modules_prepare

# compilar modulo 8192cu

cd ~/rt8192cu
CONFIG_RTL8192CU=m make -C /home/pi/linux M=`pwd`

Si todo fue bien, instalar el módulo:

sudo install -p -m 644 8192cu.ko /lib/modules/4.1.13+/kernel/net/wireless/
sudo insmod /lib/modules/4.1.13+/kernel/net/wireless/8188eu.ko
sudo depmod -a

Lógicamente ajustar la versión del kernel a tu medida. Este es sin dudas el punto más dificil, que va a requerir insistir si alguna dependencia está faltando.

Compilar hostapd

Como dijimos, el encargado de la magia será hostapd. Este es un daemon de user space para Linux y BSDs que implementa todas las funcionalidades de un AP wifi tales como AP management IEEE 802.11, IEEE 802.1X/WPA/WPA2/EAP Authenticators, cliente RADIUS, EAP server, etc.
Como vamos a usar este dongle basado en 8192CU, tendremos que compilar un hostapd modificado, y no usar el que está disponible vía gestor de paquetes. Para eso, descargamos los drivers del fabricante desde aquí Realtek el que indica linuxLinux Kernel 2.6.18~3.9.

Vamos a compilar simplemente la parte de hostapd, de esta forma

$ unzip RTL8192xC_USB_linux_*.zip
$ tar zxvf RTL8188C_8192C_USB_linux_*/wpa_supplicant_hostapd/wpa_supplicant_hostapd-0.8_rtw_*.tar.gz
$ cd wpa_supplicant_hostapd-0.8_*/hostapd/
$ make
$ sudo cp hostapd hostapd_cli /usr/local/sbin/

Ahora creamos la configuración para hostapd, en /etc/hostapd/hostapd.conf:

interface=wlan0
bridge=br0
driver=rtl871xdrv
logger_syslog=-1
logger_syslog_level=2
logger_stdout=-1
logger_stdout_level=2
dump_file=/tmp/hostapd.dump
ctrl_interface=/var/run/hostapd
ctrl_interface_group=0
ssid=roadconn
country_code=AR
hw_mode=g
channel=6
beacon_int=100
dtim_period=2
max_num_sta=255
rts_threshold=2347
fragm_threshold=2346
macaddr_acl=0
auth_algs=3
ignore_broadcast_ssid=0
wmm_enabled=1
wmm_ac_bk_cwmin=4
wmm_ac_bk_cwmax=10
wmm_ac_bk_aifs=7
wmm_ac_bk_txop_limit=0
wmm_ac_bk_acm=0
wmm_ac_be_aifs=3
wmm_ac_be_cwmin=4
wmm_ac_be_cwmax=10
wmm_ac_be_txop_limit=0
wmm_ac_be_acm=0
wmm_ac_vi_aifs=2
wmm_ac_vi_cwmin=3
wmm_ac_vi_cwmax=4
wmm_ac_vi_txop_limit=94
wmm_ac_vi_acm=0
wmm_ac_vo_aifs=2
wmm_ac_vo_cwmin=2
wmm_ac_vo_cwmax=3
wmm_ac_vo_txop_limit=47
wmm_ac_vo_acm=0
eapol_key_index_workaround=0
eap_server=0
own_ip_addr=127.0.0.1
wpa=2
wpa_passphrase=supersecreta
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
rsn_pairwise=CCMP

Fíjense los parámetros driver=rtl871xdrv que es donde especificamos el driver a usar, ssid= donde definimos el nombre del ssid y wpa_passphrase donde definimos la autenticación.

Se puede probar hostapd con el siguiente comando:

# hostapd -dd /etc/hostapd/hostapd.conf

Si todo va bien podemos crear el script de inicio sysv:

#!/bin/bash
# /etc/init.d/hostapd

### BEGIN INIT INFO
# Provides: hostapd
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Managing hostapd
# Description: This service is used to manage hostapd (WiFi Access Point)
### END INIT INFO


case "$1" in
start)
echo
echo "Starting hostapd..."
echo
if [ ! -d /var/run/hostapd ]; then
rm -rf /var/run/hostapd
mkdir /var/run/hostapd
fi

/usr/local/sbin/hostapd -B -P /var/run/hostapd/wlan0.pid /etc/hostapd/hostapd.conf
;;
stop)
echo
echo "Stopping hostapd..."
echo
if [ -e /var/run/hostapd/wlan0.pid ]; then
read pid < /var/run/hostapd/wlan0.pid if [ x$pid != x ]; then kill $pid fi fi ;; restart) echo echo "Restarting hostapd..." echo if [ -e /var/run/hostapd/wlan0.pid ]; then read pid < /var/run/hostapd/wlan0.pid if [ x$pid != x ]; then kill $pid fi fi if [ ! -d /var/run/hostapd ]; then rm -rf /var/run/hostapd mkdir /var/run/hostapd fi /usr/local/sbin/hostapd -B -P /var/run/hostapd/wlan0.pid /etc/hostapd/hostapd.conf ;; *) echo echo "Usage: /etc/init.d/hostapd start|stop|restart" echo exit 1 ;; esac exit 0


Proximo paso, brevemente es convertir el dispositivo a un repetidor wi-fi, haciendo bridge a una segunda interfaz wifi.
Continuar »

domingo, 3 de mayo de 2015

Unprivileged LXC en Debian Jessie

Linux Containers, o más conocidos como LXC son una joya relativamente reciente -hacen uso de los control groups o cgroups del kernel Linux- que permite correr un sistema operativo Linux dentro de los límites de un contenedor.
Correrlos como root suele ser directo, pero lo más recomendable es ejecutar todo el contenedor como un usuario sin privilegios. Dicha tarea no requiere demasiados esfuerzos en Ubuntu porque esa fue ajustada en detalle. En cambio, en Debian requiere bastante esfuerzo. Varios días, prueba y error compilando paquetes hasta poder conseguirlo ejecutar mis primeros contenedores sin privilegios en Debian. Estas son unas notas sobre el proceso, aún un work-in-progress.


Esta entrada no pretende ser un paso a paso sobre cómo crear contenedores unprivileged, sino más bien listar los prerequisitos necesarios para poder crearlos en Debian Jessie.
Uno de los mejores tutoriales es el de Stéphane Graber en este link: https://www.stgraber.org/2014/01/17/lxc-1-0-unprivileged-containers/
Lo que ocurre es que para poder ejecutar contenedores en Debian debemos realizar algunos ajustes previos:

1) Kernel con las features necesarias

- Un kernel custom con las features necesarios. En este caso compilé un kernel 3.16.6 basandome en la configuración usada por Ubuntu. El archivo config puede encontrarse aquí: config-3.16.0-23-generic

Aquellos que busquen una guía para el build puede encontrarse una aquí: http://www.equiscentrico.com.ar/2012/07/compilar-un-kernel-la-debian.html

También una versión para i386 de esa configuración puede descargarse de: https://www.dropbox.com/s/wazs1fhgru2hyly/linux-image-3.16.6-lxc-rtx_3.16.6-lxc-rtx-10.00.Custom_i386.deb?dl=0

Y los headers pueden encontrarse: https://www.dropbox.com/s/z5udb1stt3q9gor/linux-headers-3.16.6-lxc-rtx_3.16.6-lxc-rtx-10.00.Custom_i386.deb?dl=0

2) systemd parchado para lxc "unprivileged"

Para que el usuario tenga control sobre los cgroups del lxc se requiere una versión bien reciente de systemd, 217-2 en adelante. Yo usé la versión 219, tomandola de la rama experimental de Debian:

Agregar los repo experimantal a APT (/etc/apt/sources.list):
deb http://ftp.debian.org/debian experimental main

Y luego instalar el paquete:

# apt-get update && apt-get -t experimental install systemd

Otro paquete importante es libpam-systemd. Los paquetes instalados pueden verse:

$ dpkg -l | grep systemd
ii libpam-systemd:i386 219-8 i386 system and service manager - PAM module
ii libsystemd0:i386 219-8 i386 systemd utility library
ii systemd 219-8 i386 system and service manager
ii systemd-sysv 219-8 i386 system and service manager - SysV links

3) Versión apropiada de LXC

En este punto es importante hacer notar lo siguiente: debe usarse una versión de LXC de los repos de Debian y no una compilada por uno mismo. Porque se incluyen en la de Debian algunos parches para que LXC se lleve bien con systemd, como puede verse abajo:


dpkg-source: información: extrayendo lxc en lxc-1.0.7
dpkg-source: información: desempaquetando lxc_1.0.7.orig.tar.xz
dpkg-source: información: desempaquetando lxc_1.0.7-3.debian.tar.xz
dpkg-source: información: aplicando «0001-lxcinitdir.patch»
dpkg-source: información: aplicando «0002-sysvinit-directory.patch»
dpkg-source: información: aplicando «0003-sysvinit-lsb-headers.patch»
dpkg-source: información: aplicando «0004-sysvinit-lsb-functions.patch»
dpkg-source: información: aplicando «0005-sysvinit-lsb-lock.patch»
dpkg-source: información: aplicando «0006-lxc-attach-sigint.patch»
dpkg-source: información: aplicando «0007-lxc-patch-shebang.patch»
dpkg-source: información: aplicando «0008-lxc-debian-fuse.patch»
dpkg-source: información: aplicando «0009-lxc-debian-openssh-server.patch»
dpkg-source: información: aplicando «0010-lxc-debian-root-password.patch»
dpkg-source: información: aplicando «0011-lxc-debian-systemd.patch»
dpkg-source: información: aplicando «0012-lxc-debian-sysfs.patch»

Como se ve, esos parches son fundamentales. Pasé mucho tiempo rompiéndome la cabeza con unos deb compilados a partir del upstream de lxc. Esos parches están para eso. Entonces, con la versión que Jessie tiene en sus repos de LXC: 1.0.6 va bien. Lo ideal sería poder usar una un poco más nueva, como 1.0.7 (fuentes de sid), eso voy a intentar dentro de poco.

4) Instalar cgmanager y verificar que esté demonizado

$ dpkg -l cgmanager
ii cgmanager 0.36-1 all

Para cgmanager estoy usando la versión 0.36, que creo que hasta este momento es la más reciente. El DEB que preparé está aquí: https://www.dropbox.com/s/dvkg8sugcpqdc72/cgmanager_0.36-1_all.deb?dl=0

Luego de instalarlo, asegurarse de que quede activado en los scripts de inicio:

c$ systemctl status cgmanager
● cgmanager.service - Cgroup management daemon
Loaded: loaded (/usr/lib/systemd/system/cgmanager.service; enabled; vendor preset: enabled)
Active: active (running) since dom 2015-05-03 20:12:15 ART; 2h 22min ago
Main PID: 814 (cgmanager)
CGroup: /system.slice/cgmanager.service
‣ 814 /usr/sbin/cgmanager -m name=systemd
matias@matuxntbk:~/RamosLinux/lxc$ ps aux | grep cgmanager
root 814 0.0 0.2 2912 2072 ? Ss 20:12 0:00 /usr/sbin/cgmanager -m name=systemd

5) script de inicio y configuración de red lxc-net

También en el dropbox guardé una copia del script de configuración de red, lxc-net que es el encargado de iniciar dnsmasq con la configuración necesaria para utilizar una red tipo "nat", en la que cada contenedor "guest" tendrá una interfaz virtual con una red "interna" que hará nat a la dirección IP del host.

https://www.dropbox.com/s/s4yl5jn30w1apot/lxc-net.tar.gz?dl=0

Luego copiar los archivos, iniciar el script:

# systemctl start lxc-net

Y verificar con bridge utils que la interfaz tipo bridge se haya creado:

matias@matuxntbk:~/RamosLinux/lxc$ /sbin/brctl show
bridge name bridge id STP enabled interfaces
lxcbr0 8000.000000000000 no

6) Preparar el ambiente, siguiendo el tutorial de Stéphane (https://www.stgraber.org/2014/01/17/lxc-1-0-unprivileged-containers/)

6.1) Conviene agregar al usuario que usaremos para los containers al archivo sudoers para que pueda ejecutar cgm:

# /etc/sudoers o visudo
lxcuser ALL=NOPASSWD:/usr/bin/cgm

6.2) preparar los permisos:

$ sudo cgm create all foo
$ sudo cgm chown all foo $(id -u) $(id -g)
$ cgm modepid all foo $$

Aqui utilizamos cgm (cgroup manager) para crear el cgroup foo, y mover a él el pid del running shell. Eso permitirá que los procesos susecuentes se ejecuten bajo ese cgroup.

Notas finales: este es un borrador de un trabajo aún en progresos. Hay que señalar que las versiones que se necesitan por ejemplo de systemd son realmente bleeding-edge. Siendo systemd un proceso crítico (de él depende toda la estabilidad del sistema) conviene probar en profundidad esta configuración antes de aventurarse a hacer un deploy a producción.

Continuar »