json pretty print en consola

En mi día a día manejo bastantes cadenas de json o diccionarios de python como tantos otros y muchas veces necesito tener una visualización más agradable a la vista y acabo complicándome la vida copiando, pegando, etc… El otro día incluso me instalé un paquete ‘aeson-pretty’ para printear jsons, peeeero esta solución es mucho más sencilla y me gusta más

echo "alias j='python -m json.tool'" >> ~/.bash_aliases
source ~/.bashrc
echo '{ "element0" : "lorem", "element1" : "ipsum" }' | j
{
"element0": "lorem",
"element1": "ipsum"
}

Unused variable ‘__class__’ (unused-variable)

Si usas pylint como linter para python puede que lo estés flipando al ver errores como este :


Unused variable '__class__' (unused-variable)

Al parecer el paquete  astroid en su versión 1.6.x introduce por algún motivo este comportamiento. Para solucionarlo basta con instalar la version astroid==1.5.3

Actualizando el paquete con pip debería ser suficiente y recuerda actualizar tu requirements.txt

pip install astroid==1.5.3

Referencias: https://github.com/ahitrin/SiebenApp/commit/ea063d143f140554aaa08606d9c8d58121e8a52c

supervivencia en python usando xml con namespaces

En python hay dos librerías que se usan extensamente xml que viene en el core de python y lxml una librería externa que provee de algunas cosas que xml no.

Vamos a manejar tres clases o conceptons ElementTree, Element y ElemenPath y vamos a jugar siempre con que nuestro xml tiene namespaces ya que tooooodo el uso actual de xml de servicios soap se hace con namespaces, excepto los ficheros de configuración de java

texto

Elment, es un tag, y tiene nombre ( tag, tagname o como quieras llamarlo ), attributos y texto

En estos ejemplos se realiza una acción y se dumpea la salida para comprobar lo que estamos haciendo

import xml.etree.ElementTree as ET

# crear nodos
>>> root = ET.Element('root')
>>> body = ET.SubElement(a, 'body')
>>> ET.dump(root)

# asignar atributos a un tag
#
>>> root = ET.Element('root')
>>> body = ET.SubElement(root, 'body', {'env':'devel'})
>>> ET.dump(body)

# otras formas de manejar los attributos
>>> body.attrib['env']
'devel'
>>> body.attrib['env'] = 'foo'
>>> body.attrib['env']
'foo'

# texto dentro de un Element
>>> body.text = 'this is the text'
>>> ET.dump(body)
this is the text

# importar de fichero
>>> import xml.etree.ElementTree as ET
>>> tree = ET.parse('country_data.xml')
>>> root = tree.getroot()

# importar de string
>>> xml_string = ""
>>> root = ET.fromstring(xml_string)
>>> ET.dump(root)

# namespaces

source = """


irrelevant_request_id
0



"""

>>> xml = ET.fromstring(source)
>>> xml

Dos cosas a tener en cuenta, NO puede haber un salto de linea de en el “””

source = """



irrelevant_request_id
0



"""

>>> import xml.etree.ElementTree as ET
>>> xml = ET.fromstring(source)
>>> xml = ET.fromstring(source)
Traceback (most recent call last):
File “”, line 1, in
File “/usr/lib/python2.7/xml/etree/ElementTree.py”, line 1311, in XML
parser.feed(text)
File “/usr/lib/python2.7/xml/etree/ElementTree.py”, line 1653, in feed
self._raiseerror(v)
File “/usr/lib/python2.7/xml/etree/ElementTree.py”, line 1517, in _raiseerror
raise err
xml.etree.ElementTree.ParseError: XML or text declaration not at start of entity: line 2, column 0
#Usando namespaces los tags se nombran así
{url}TagName

Con lo que las busquedas xpath se hace buscando por {url}TagName. Para buscar lo tendremos que hacer de la siguiente manera, pero antes hay que registar las urls

Podemos usar find o findall para buscar uno o todos los nodos que cumplan un pattern xpath

source = """


irrelevant_request_id
0



"""

>>> xml = ET.fromstring(source)
# como tenemos namespaces esta consulta no funcionará
>>> xml.find('ID')
# esta debería funcionar, pero tampoco funciona por que find busca desde la raiz y no desde cualquier nodo
>>> xml.find('{urn:dslforum-org:cwmp-1-2}ID')
# buscamos con find desde cualquier nodo con una busqueda al estilo xpath
>>> xml.findall('.//{urn:dslforum-org:cwmp-1-2}ID')

# es posible usar etiquetas para los namespaces
>>> ns = {'cwmp': 'urn:dslforum-org:cwmp-1-2',
'soap': 'http://schemas.xmlsoap.org/soap/envelope/'}
>>> xml.find('.//{urn:dslforum-org:cwmp-1-2}ID', ns)

# findall devolerá una lista con los resultados, o una lista vacía si no hay resultado
>>> xml.findall('.//{urn:dslforum-org:cwmp-1-2}ID', ns)
[]

# pip install lxml
# from lxml import etree

# crear nodos
>>> root = etree.Element('root')
>>> root.append(etree.Element('body'))
>>> etree.tostring(root)
''
>>> etree.tostring(root, pretty_print=True)
'\n \n\n'

# asignar atributos a un tag

>>> root = etree.Element('root')
>>> root.append(etree.Element('body', env='devel'))
>>> etree.tostring(root)
''
>>> etree.tostring(root, pretty_print=True)
'\n \n \n\n'

# manejar attributes de forma comoda
>>> root = etree.Element('root')
>>> root.append(etree.Element('body', my_attribute='my_value'))
>>> attributes = root[0].attrib
>>> attributes['new_attr'] = '12345'
>>> etree.tostring(root)
''

# manejar texto
>>> from lxml import etree
>>> root = etree.Element('root')
>>> root.append(etree.Element('body', my_attribute='my_value'))
>>> root.text = 'this is the text'
>>> etree.tostring(root)
'this is the text'

# importar de fichero

>>> from lxml import etree
>>> tree = ET.parse('country_data.xml')
>>> root = tree.getroot()

# importar de string
>>> xml_string = ""
>>> root = ET.fromstring(xml_string)
>>> ET.dump(root)

# namespaces

>>> source = """


irrelevant_request_id
0



"""
>>> root = ET.fromstring(source)

#Buscando nodos, como el xml hay que poner {

# nótese que el {namespace} debe ser exacto al encabezado o no funcionará la búsqueda
>>> print(root.findall('{http://schemas.xmlsoap.org/soap/envelope}Header'))
[]
>>> print(root.findall('{http://schemas.xmlsoap.org/soap/envelope/}Header'))
[]

# para poder obtener los nsmap de un xml debemos usar objectify, hay dos Element

http://lxml.de/api/xml.etree.ElementTree.Element-class.html
http://lxml.de/api/lxml.etree.ElementBase-class.html

>>> root = objectify.fromstring(source)
>>> root.nsmap
{'cwmp': 'urn:dslforum-org:cwmp-1-2', 'soap-enc': 'http://schemas.xmlsoap.org/soap/encoding/', 'xsi': 'http://www.w3.org/2001/XMLSchema-instance', 'soap': 'http://schemas.xmlsoap.org/soap/envelope/', 'xsd': 'http://www.w3.org/2001/XMLSchema'}

# printea con cabecera xml usando xml_declaration=True
root = etree.Element('root')
etree.tostring(root, encoding="utf-8" , xml_declaration=True)
"\n"

# Se pueden crear a mano elmentos de namespaces con ElementMarker pero el trabajo es muy extenso y el código no es muy legible, mi recomendación es usar templates de string importarlos y usarlos en vez de generarlos a mano.

from lxml import etree
from lxml.builder import ElementMaker
soap = ElementMaker(namespace=”http://schemas.xmlsoap.org/soap/envelope/”, nsmap={‘soap’ : “http://schemas.xmlsoap.org/soap/envelope/”})

# Extras : xmlwitch
xmlwitch sólo lee y no escribe, pero lo hace de manera muy cómodo

entornos virtuales en python

Las aplicaciones cada vez son más complejas y por suerte más estructuradas, por ello los pequeños bloques de código reutilizable se extraen y se paquetizan, con uno u otro nombre dependiendo de cada lenguaje. El siguiente paso necesario es una herramienta que gestione los paquetes, instalar, listar, eliminar, instalar desde una fuente remota, etc… Bien pues aquí es donde llega la necesidad de usar un entorno virutal. ¿Por qué? Por varios motivos, uno es que cada aplicación puede requerir una serie de paquetes con versiones concretas que no son necesarias con otras aplicaciones o que incluso que sean incopatibles entre sí.
Usando un entorno virtual podemos instalar estas dependencias en una ruta exclusiva para nuestra aplicación y no tenemos que instalarlas en el sistema. Esto es genial ya que no necesitaremos permisos elevados para instalar los paquetes ni tendremos que andar instalando o desinstalando paquetes para poder desarrollar o instalar una aplicación. Además podemos deshacernos fácilmente de estos paquetes eliminando la ruta donde se almacenan y volver a instalarlos para probar el despliegue o simplemente por si sospechamos que hay ‘algo’ que no está correcto en nuestras dependencias.

En el caso de python hay dos entornos virtuales que se usan extensivamente: virtualenvwrapper y virtuaenv.

La primera vez que empecé a leer sobre esto me resultó muy confuso por que parecía que se hablaba de un sólo software para crear el entorno virutal y no de dos. El uso de estos dos nombres muy parecidos favorece la confusión, pero son dos proyectos que al fin y al cabo hacen lo mismo: proveernos de un entorno virtual.

A grandes rasgos las características principales de virtualenvwrapper vs virtualenv en Debian son:
* virtualenvwrapper sólo soporta python2.x ( ojo, en paquete Debian, ver READE.debian) y virutalenv soporta 2.x y 3.x
* virtualenvwrapper centraliza el almacenamiento de dependencias en $WORKON_HOME Y virutalenv no, debes especificar el directorio cada vez que creas el entorno.

Ahora, como comenzar a usar un entorno virtual en python:

Virtualenvwrapper


sudo apt-get install virtualenvwrapper
mkvirtualenv my_project
workon my_project
...
deactivate

Se crea por defecto en `~/.virtualenvs`

 Virtualenv

Puedes preparar el entorno para la version de python que quieras siempre que la tengas instalada en el systema ( -p )

sudo apt-get install python-virtualenv
virtualenv -p /usr/bin/python2 venv
source venv/bin/activate
...
deactivate

instalar dependencias: pip

El gestor de dependencias por defecto en python es pip, y con el puedes instalar, desintalar, instalar desde remoto, etc…
Con estos entornos ya puedes instalar tus ficheros requirements.txt o requirements-dev.txt de pip sin tener que usar poderes de supervaca. La manera ‘standard’ para instalar las dependencias de un proyecto:


pip install pip --upgrade
pip install -r acs/requirements-dev.txt --upgrade
pip install -r acs/requirements.txt --upgrade

Finalmente podrías configurar cada paquete que disponga de setup.py

for package in $(ls -d */); do pushd $package; if [ -e setup.py ]; then python setup.py develop; fi; popd; done

Extraer imagen jffs2

Cuando accedemos a un dispositivo embebido una de las cosas que solemos hacer es echar un vistazo a /proc/mtd para ver los montajes de los bloques mtd y suele haber un bloque que es el contiene la imagen del sistema embedibo.

Uno de los formatos de ficheros con el que habitualmente nos encontramos es jffs2 [1]

Para extraer el contenido tenemos varias herramientas que podemos usar:

vía script

Podemos copiar el contenido a un dispositivo mtd local y montarlo como jffs de la siguiente manera. ( Estoy usando image.jffs2)


modprobe mtdram
modprobe jffs2
modprobe mtdchar
modprobe mtdblock
dd if=image.jffs2 of=/dev/mtd0
mkdir -p jffs-root
mount -t jffs2 /dev/mtdblock0 jffs-root/

unjffs2 de firmware mod kit

Es básicamente el mismo script pero mejorado, ya que puede darse que tengamos una image ‘big endian’ este script puede convertir la imagen para su correcta extracción. Firmware mod kit[2] es un conjunto de herramientas empaquetadas para todo este tipo de tareas. Requiere el paquete mtd-utils que provee jffs2dump y debe ejecutarse como root ya que jffs2dump sólo es ejecutable por el usuario root.

apt-get install mtd-utils
git clone https://github.com/mirror/firmware-mod-kit
sudo src/jffs2/unjffs2 image.jffs2 jffs-root

jefferson

Es una herramienta de extracción de jffs2 hecha en python

git clone https://github.com/sviehb/jefferson
sudo python setup.py install
sudo pip install cstruct
sudo apt-get install python-lzma

Extraemos la imagen así
jefferson image.jffs2 -d jffs-root

Personalmente el mejor resultado ha sido con el script de firmware mod kit, aquí dejo algunos enlaces de interés que pueden ayudarte si ninguna de estas herramientas ha funcionado [3] [4]


[1] https://es.wikipedia.org/wiki/JFFS2
[2] https://github.com/mirror/firmware-mod-kit
[3] https://www.owasp.org/index.php/IoT_Firmware_Analysis
[4] http://wiki.securityweekly.com/wiki/index.php/Reverse_Engineering_Firmware_Primer

vim soporte de portapapeles en entornos X

Por defecto en Debian, el paquete vim no soporta copiar y pegar texto de forma que se use el portapapeles para el entorno gráfico. Esto es debido a que vim puede ser compilado con una gran cantidad de opciones distintas, y para ello disponemos de muchos paquetes que actualizarán el binario con más o menos opciones activadas.

En este caso, necesitamos activar ‘clipboard’, pero… ¿ cómo podemos ver las opciones activas de nuestro vim ? con vim --version

$ vim --version
VIM - Vi IMproved 7.4 (2013 Aug 10, compiled Nov 17 2016 06:26:47)
Included patches: 1-488, 576
Extra patches: 8.0.0056
Modified by pkg-vim-maintainers@lists.alioth.debian.org
Compiled by jamessan@debian.org
Huge version with GTK2 GUI. Features included (+) or not (-):
+acl +farsi +mouse_netterm +syntax
+arabic +file_in_path +mouse_sgr +tag_binary
+autocmd +find_in_path -mouse_sysmouse +tag_old_static
+balloon_eval +float +mouse_urxvt -tag_any_white
+browse +folding +mouse_xterm +tcl
++builtin_terms -footer +multi_byte +terminfo
+byte_offset +fork() +multi_lang +termresponse
+cindent +gettext -mzscheme +textobjects
+clientserver -hangul_input +netbeans_intg +title
+clipboard +iconv +path_extra +toolbar
+cmdline_compl +insert_expand +perl +user_commands
+cmdline_hist +jumplist +persistent_undo +vertsplit
+cmdline_info +keymap +postscript +virtualedit
+comments +langmap +printer +visual
+conceal +libcall +profile +visualextra
+cryptv +linebreak +python +viminfo
+cscope +lispindent -python3 +vreplace
+cursorbind +listcmds +quickfix +wildignore
+cursorshape +localmap +reltime +wildmenu
+dialog_con_gui +lua +rightleft +windows
+diff +menu +ruby +writebackup
+digraphs +mksession +scrollbind +X11
+dnd +modify_fname +signs -xfontset
-ebcdic +mouse +smartindent +xim
+emacs_tags +mouseshape -sniff +xsmp_interact
+eval +mouse_dec +startuptime +xterm_clipboard
+ex_extra +mouse_gpm +statusline -xterm_save
+extra_search -mouse_jsbterm -sun_workshop +xpm
system vimrc file: "$VIM/vimrc"
user vimrc file: "$HOME/.vimrc"
2nd user vimrc file: "~/.vim/vimrc"
user exrc file: "$HOME/.exrc"
system gvimrc file: "$VIM/gvimrc"
user gvimrc file: "$HOME/.gvimrc"
2nd user gvimrc file: "~/.vim/gvimrc"
system menu file: "$VIMRUNTIME/menu.vim"
fall-back for $VIM: "/usr/share/vim"
Compilation: gcc -c -I. -Iproto -DHAVE_CONFIG_H -DFEAT_GUI_GTK -pthread -I/usr/include/gtk-2.0 -I/usr/lib/x86_64-linux-gnu/gtk-2.0/include -I/usr/include/gio-unix-2.0/ -I/usr/include/cairo -I/usr/include/pango-1.0 -I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/pixman-1 -I/usr/include/libpng12 -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/libpng12 -I/usr/include/pango-1.0 -I/usr/include/harfbuzz -I/usr/include/pango-1.0 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/freetype2 -g -O2 -fPIE -fstack-protector-strong -Wformat -Werror=format-security -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -I/usr/include/tcl8.6 -D_REENTRANT=1 -D_THREAD_SAFE=1 -D_LARGEFILE64_SOURCE=1
Linking: gcc -L. -Wl,-z,relro -L/home/pere/src/debian/ruby/ruby2.1/debian/lib -fstack-protector -rdynamic -Wl,-export-dynamic -Wl,-E -fPIE -pie -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -o vim -lgtk-x11-2.0 -lgdk-x11-2.0 -lpangocairo-1.0 -latk-1.0 -lcairo -lgdk_pixbuf-2.0 -lgio-2.0 -lpangoft2-1.0 -lpango-1.0 -lgobject-2.0 -lglib-2.0 -lfontconfig -lfreetype -lSM -lICE -lXpm -lXt -lX11 -lXdmcp -lSM -lICE -lm -ltinfo -lnsl -lselinux -lacl -lattr -lgpm -ldl -L/usr/lib -llua5.2 -Wl,-E -fstack-protector -L/usr/local/lib -L/usr/lib/x86_64-linux-gnu/perl/5.20/CORE -lperl -ldl -lm -lpthread -lcrypt -L/usr/lib/python2.7/config-x86_64-linux-gnu -lpython2.7 -lpthread -ldl -lutil -lm -Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions -L/usr/lib/x86_64-linux-gnu -ltcl8.6 -ldl -lz -lpthread -lieee -lm -lruby-2.1 -lpthread -lgmp -ldl -lcrypt -lm

Para afinar un poco más :

$ vim --version | grep clipboard
+clipboard +iconv +path_extra +toolbar
+eval +mouse_dec +startuptime +xterm_clipboard

En este caso vemos ‘+clipboard‘ por lo que tengo la opción activada. Si no es así podemos instalar el paquete gtk para activar el soporte
apt-get install vim-gtk

Ahora ya podemos entrar a vim, escribir algo y probar a seleccionar texto y utilizar las combinaciones típicas de copiar y pegar entre vim y otra aplicación. Si no es posible copiar el texto, podemos mapear la combinación de teclas ‘control+shift+c’ en vim de la siguiente manera:

vmap <C-S-C> "y+<CR>"

… y ya podemos hacer copy+paste de vim a cualquier otro sitio. Recuerda que puedes hacer el mapping persistente en tu ~/.vimrc

Usando tftpd para extraer ficheros de un router

Cuando consigues acceso a un sistema embedido lo más práctico es poder sacar ficheros, o el sistema de ficheros completo fuera del router para poder analizarlo detenidamente.
No siempre hay herramientas como ssh, telnet o un puerto usb donde volcar los datos, pero casi siempre está disponible el comando tftp, un servicio de transferencia de ficheros sin autenticación.

Por lo que nuestro objetivo será conectar desde la consola (busybox) de nuestro router a un host externo que tenga un servidor tftpd y dejar ahí nuestro botín.

Tras algunas pruebas me he decantado por atftpd

sudo apt-get install atfpd

Preparamos la ruta que alberga el contenido

mkdir /srv/tftp/
chonw nobody.nogroup -R /srv/tftp

Desde mi consola tengo este cliente tftp que puede ser o no el tenga tu firmware


# tftp
BusyBox v1.15.3 () multi-call binary

Usage: tftp [OPTIONS] HOST [PORT]

Transfer a file from/to tftp server

Options:
-l FILE Local FILE
-r FILE Remote FILE
-g Get file
-p Put file

 

Un ejemplo para volcar un fichero de bloques mtd a mi host es este
tftp -p -l /dev/mtd6 -r mtd6 192.168.0.155

Y algo un poco más chulo para volcar casitodo el sistema de ficheros sería esto:
for file in `find /bin/ /etc/ lib/ mnt/ opt/ root/ sbin/ shares/ srv/ tmp/ usr/ var/ vmlinux.lz nocomprlist `; do tftp -p -l "$file" -r "$file"
192.168.0.155; done ;

excluyo los directorios /dev /sys /proc por que tienen ficheros en uso, ficheros de bloques, etc… por que no se pueden copiar de la manera habitual

Como nota, revisar /var/syslog para ver si la subida de ficheros no se está realizando correctamete y por qué. Por defecto el directorio es /srv/tftp el usuario nobdoy y el group nogroup. Recuerda revisar los permisos.

ftp mirroring con lftp

He necesitado hacer un mirror de unos directorios que sólo estaban disponibles por ftp y necesito lanzar sincronizaciones periódicas de forma sencilla y cómoda, para ello he usado lftp una vieja herramienta que tenía ya enterrada.

Instalar lftp

apt-get install lftp

Lo más útil de lftp es la capacidad de copiar directorios de forma recursiva ya que por defecto el cliente ftp de toda la vida de las distrubicones de linux no lo soporta.

Como puedo hacer un mirroring:

lftp -u usuario,password ftp.midominio.tld
mirror /ruta/ftp/origen /ruta/local/b

 

ls: Fatal error: Certificate verification: Not trusted

Este error aparece cuando nuestro lftp no es capaz de verificar que el certificado remoto sea válido por lo que no nos permite continuar realizando operaciones en la máquina remota. Esto está bien, si el certificado es correcto, pero habitualmente nos encontraremos con certificados incorrectos o caducados, por lo que podremos desactivarlo, ya que no nos ofrece ningún nivel de seguridad, si el certificado no está correctamente instalado.

Para desactivarlo, yo he usado esta opción con éxito ( aunque hay varias variantes buscando en google ):

set ftp:ssl-allow no;

A lo que tengo que añadir que después de ejecutar este comando con el debug activo:

set debug 10

me ha sido imposible retornar el listado de directorios, y parece ser un bug. Este caso se observa al lanzar el comando `ls` y ver infinitamente el texto: Delaying before reconnect.

Desactivado el debug, funciona correctamente

set debug 0

Mirroring sites using ftp

Aquí os dejo un pequeño script para hacer migración de sitios usando ftp de una forma bastante rápida:

FTP_USER="tu usuario"
FTP_PASSWORD="tu password"
FTP_HOST=ftp.dominio.ltd
SOURCE=/ruta/remota/origen
DEST=/ruta/remota/destino
lftp -u"${FTP_USER},${FTP_PASSWORD}" -e "set ftp:ssl-allow no; mirror ${SOURCE} ${DEST} --only-newer --verbose --use-pget-n=10; quit" ${FTP_HOST}

A tener en cuenta:

  • -u”usuario,password” Es necesario usar las comillas ya que de esa forma escapamos los caracteres especiales que pueda tener el password
  • -e“ejecuta comandos una vez que abre la conexión”
  • set ftp:ssl-allow no No valida el certificado ssl del servidor ftp
  • –only-newer Sólo ficheros nuevos
  • –use-pget n=10 Usa 10 conexiones concurrentes

Configura wordpress con SSL

La seguridad en las comunicaciones es necesaria, y más vale tarde que nunca. Ahora podemos usar SSL de forma gratuíta gracias a Let’s Encrypt.

Let’s Encrypt es un proyecto de la Linux Foundation con el que colaboran grandes empresas como Cisco y organizaciones como Electronic Frontier Foundation que tiene como objetivo proveer de manera gratuíta certificados de seguridad para todos. Hasta ahora y aún sigue siendo así los certificados cuestan entre 60 y 300 euros dependiendo del tipo de certificado, seguro, etc… con Let’s Encrypt podremos usar un certificado sin tener que gastarnos dinero, el único incoveniente es que caduca a los 90 días y hay que renovarlo. Aunque esto no quiere decir que lo tengas que hacer manualmente, ¡ que trabaje el CRON !

He instalado este certificado para mi dominio, de manera muy sencilla por que uso Plesk y existe un plugin[2] y he cambiado la configuración de wordpress en Ajustes / Generales / Dirección de WordPress (URL) y Dirección del sitio (URL) a https://www.senin.org

Te animo a que tambien uses SSL y muevas tu blog a ssl

[1]https://letsencrypt.org/getting-started/
[2]https://devblog.plesk.com/2015/12/lets-encrypt-plesk/