David

Monitorización del nodo

Monitorización del nodo

La primera herramienta que se suele utilizar para monitorizar el funcionamiento de un nodo recién creado es gLiveView, que forma parte de las CNTOOLS proporcionadas por GUILD Operators.

Un dato que se suele indicar como relevante para comprobar el buen funcionamiento del nodo suele ser el que indica el número de transacciones procesadas y pendientes de procesamiento. En condiciones normales, este número se va incrementando. Sin embargo, no es extraño que al instalar un nodo por primera vez, y dependiendo de la versión de Cardano que hayamos utilizado, nos encontremos con que este número se mantiene invariablemente igual a cero.

Una posibilidad es que el nodo no esté funcionando adecuadamente debido a que los nodos no se están comunicando bien entre sí o con el exterior. Podemos comprobar las conexiones, tanto para el núcleo como para los reles, con esta instrucción:

netstat -pant | grep 6000

Sin embargo, otra causa puede ser, simplemente, que el nodo no esté informando de este parámetro, por lo que gLiveView no puede mostrarlo. Podemos comprobar las métricas informadas por el nodo con el siguiente comando:

curl -s 127.0.0.1:12798/metrics | sort

El que dicho parámetro se informe o no se encuentra en el ajuste del parámetro «TraceMempool»:false que se encuentra en el arcivo de configuración. Siguiendo la estructura de directorios utilizada por CNTOOLS:

nano $CNODE_HOME/files/config.json

Es importante precisar que el ajuste de este parámetro puede influir en el rendimiento del nodo. El ajuste a ‘false’ parece ser que favorece la reducción de memoria y cpu, lo que es interesante cuando las máquinas vay muy ajustadas, por lo que también ser reduce la probabilidad de perder bloques (Éste es el ajuste por defecto, al menos desde la versión 1.31.0. En las anteriores, no me acuerdo). Sin embargo también puede enmascarar un mal funcionamiento del nodo. En general, un criterio que parace razonable es ajustarlo en ‘true’ siempre que nuestro hardware no se vea comprometido, al menos en el núcleo y, en todo caso, ajustarlo a ‘false’ en los relés (Hay una discusión sobre este asunto aquí).


Publicado por David en Cardano, 0 comentarios

Usuarios y grupos

  • Listar usuarios o grupos

Una manera sencilla es consultar el contenido de los archivos /etc/passwd y /etc/group

  • Añadir un usuario o un grupo

Nota: Cada usuario debe pertenecer por lo menos a un grupo principal, que es el que viene especificado en el mismo archivo /etc/passwd. Adicionalmente podrá pertenecer a cuantos grupos más se desee, lo que vendrá indicado en el archivo /etc/group. Si durante la creación del usuario no especificamos ningún grupo se le asignará uno por defecto, el cual variará según el procedimiento utilizado. En unos casos se creará un grupo con el mismo nombre e ID que el usuario. En otros casos se asignará a un grupo especial de sistema llamado nogroup.

Lo primero es no confundir el comando adduser con useradd. Como regla general, y en la mayoría de las situaciones, utilizaremos adduser con las opciones deseadas. Por ejemplo, para añadir un usuario con un uid específico, utilizaremos el siguiente comando.

sudo adduser <USUARIO> --uid <UID>

En este caso, se crea por defecto un grupo con un gid = uid como grupo principal de dicho usuario. Además, se creará el directorio de usuario, se nos pedirá la contraseña y alguna otra información complementaria. Digamos que adduser es la herramienta administrativa por excelencia.

Y si lo que queremos es añadir un grupo con un GID específico:

sudo addgroup <GRUPO> --gid <GID>

 

Los comandos useradd y groupadd se utilizan para trabajar con usuarios y grupos en un más bajo nivel y de manera más granular.

  • Para comprobar uid o gid de un usuario

sudo id -u <usuario>
sudo id -g <usuario>
  • Elevar un usuario a sudoer

Basta con añadirlo al grupo sudo

sudo usermod -aG sudo <usuario>
  • Cambiar contraseña de un usuario

passwd <usuario>
    • Cuando se pasa sin argumentos, se entiende que se desea cambiar la contraseña del usuario actual.
    • Si no se suministra contraseña, no se cambia.
    • Sólo un usuario con privilegios de administrador puede modificar las contraseñas de otros usuarios
    • Las contraseñas se almacenan en formato cifrado en el archivo /etc/shadow
  • Comprobar a qué grupos pertenece un usuario

groups <usuario>
  • Borrar un usuario o un grupo

deluser <usuario>

o…

delgroup <usuario>
  • Borrar a un usuario de un grupo

deluser <usuario> <grupo>
  • Crear un usuario sin directorio de inicio ni capacidad de iniciar sesión

Esto es lo que viene a ser un usuario de sistema, es decir, un usuario que sirve para determinar la propiedad de determinados archivos o procesos pero que no va a trabajar en la máquina en cuestión.

En esta ocasión no se crea ningún grupo por defecto, lo cual es lógico tratándose de un usuario de sistema. Si queremos que se cree un grupo con el mismo nombre y ID deberemos utilizar la opción –group. De lo contrario, el usuario no pertenecerá a ningún grupo, lo que en la práctica significa que quedará asignado al grupo nogroup.

sudo adduser --system --no-create-home --uid <UID> --group <usuario>
Publicado por David en Herramientas, Linux, 0 comentarios
Securizar un stake pool

Securizar un stake pool

Lo primero, el servicio SSH

Lo primero que tenemos que hacer para estar razonablemente seguros de que nuestro stake pool se encuentra seguro ante posibles intrusiones es ajustar el servicio SSH a nuestras necesidades reales. en nuestro caso, utilizamos una autenticación de dos factores mediante un par de claves pública y privada y el servicio de autenticación de Goggle. Tampoco estaría de más restringir las direcciones desde las cuales vamos a autorizar las conexiones al servicio SSH, siempre que esto sea posible. Lógicamente, si estamos en una red privada o una VPN en la que disponemos de direcciones privadas bien definidas, o nos conectamos desde accesos con IP’s públicas fijas, esta opción es inmejorable. Sin embargo, si necesitamos conectarnos desde lugares más o menos arbitrarios con IP’s públicas o privadas asignadas dinámicamente, esta estrategia no es posible.

Como siempre, no existe un plan de seguridad estándar perfecto. El mejor plan de seguridad es el que se adapta de manera adecuada a la estructura de nuestro sistema y a las circunstancias personales de cada cual.

Después, el firewall

Lo siguiente es restringir el tráfico a nuestros ordenadores mediante el firewall. Como la mayoría de operadores de Stake Pools, he elegido la opción de ufw (Uncomplicated FireWall) frente a la configuración de IPtables. Sé que los más avezados expertos en Linux no tardarán en advertir que si IPtables existe es por algo, y que su mayor complejidad ofrece la contrapartida de un control mucho más preciso y robusto de las conexiones del PC. Y tendrán toda la razón. En mi descargo diré que también es bastante más lioso de configurar y, por lo tanto, mucho más probable que en manos de un zarpas como yo se deslicen gazapos que puedan convertirse en brechas de seguridad.

Es importante entender que la seguridad real implica un compromiso entre la robustez de la herramienta y la habilidad para manejarla de manera correcta. No hay nada más peligroso que un AK-47 en manos de un chimpancé.

Y, la verdad, es que ufw cumple con este cometido, al menos para mí. Si un día me decido a estudiar IPtables a fondo, os prometo que seréis los primeros en saberlo.

Dicho esto, lo primero será asegurarnos de que tenemos ufw instalado en nuestro equipo, lo cual no es difícil porque en la mayoría de distribuciones suele venir por defecto. En cualquier caso, basta con teclear:

sudo apt install ufw

Reading package lists... Done
Building dependency tree       
Reading state information... Done
ufw is already the newest version (0.36-6).
ufw set to manually installed.
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.

Lo cual nos demuestra que, efectivamente, ya está instalado. Cuando hacemos esto, nos encontramos con el mensaje de que el paquete en cuestión ha sido marcado como instalado manualmente, lo cual a mí, que soy un maniático, me ralla un poco. Si quieres saber más, mira esta otra entrada.

Volviendo al firewall, lo primero que comprobamos es su estado

sudo ufw status
Status: inactive

Es decir, que viene deshabilitado por defecto. Lo cual es conveniente para evitar cortar servicios que pudieran estar en ejecución antes de poder configurarlo. Sin ir más lejos, si estamos conectados a la máquina por SSH y habilitamos el firewall ya podéis imaginar lo que iba a pasar (spoiler: la conexión se corta sin posibilidad de volver a conectar hasta poder acceder físicamente)

Así que lo primero de todo, habilitaremos la conexión al servicio ssh

sudo ufw allow ssh
Rules updated
Rules updated (v6)

Como vemos, esta es una manera rápida de habilitar conexiones al ordenador, basándonos en servicios, en lugar de puertos.

Nota: En muchos manuales veremos que es interesante como medida adicional de protección modificar el puerto por defecto utilizado por el servicio ssh (que pertenece a ese grupo de puertos denominados como «bien conocidos» y que corresponde siempre al 22). En mi opinión, se trata de una medida más incómoda que eficaz. Cualquier atacante puede imaginar que el servicio ssh se puede estar ejecutando en cualquier otro puerto y el primer paso antes de probar nada suele ser un escaneo de puertos para averiguar qué tenemos por ahí. Y lo mismo vale para cualquier servicio ejecutado en uno de los susodichos «well known ports»

Ahora sí, habilitamos el firewall

sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup

Y como era de esperar en un programa bien implementado y dirigido a dummies como yo, nos advierte de los peligros antes de hacernos caso.

Comprobemos las reglas que tenemos hasta ahora:

sudo ufw status
Status: active

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW       Anywhere 
22/tcp (v6)                ALLOW       Anywhere (v6)       

Efectivamente, el bien conocido puerto 22 accesible para el protocolo tcp desde cualquier lugar. Eso es lo que ha mantenido nuestra conexión activa. Nuevamente, si sabemos desde qué direcciones nos vamos a conectar, éste sería otro buen lugar para restringir el acceso desde esas direcciones, además de la configuración del propio servicio ssh. Cuantas más puertas, mejor.

Bien, si ya teníamos nuestro stake pool en funcionamiento con un núcleo y un relé hablando entre sí, será interesante lanzar la aplicación gLiveView para ver lo que ha pasado. Y es que, si todo va bien, las dos máquinas ya no podrán hablar entre sí. Al menos, el relé ya no podrá acceder al núcleo, ya que éste está restringiendo todas las conexiones entrantes. Lógicamente, nos interesa añadir una regla que permita esta comunicación. Será la siguiente:

sudo ufw allow proto tcp from <IP_RELAY> to any port <PORT_KERNEL>

En cristiano, permitir toda conexión entrante en protocolo tcp desde la IP del relé al puerto en el cual se ejecuta el nodo del núcleo. Sencillo, ¿no?

Comprobemos…

sudo ufw status
Status: active

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW       Anywhere                  
<PORT_KERNEL>/tcp          ALLOW       <IP_RELAY>             
22/tcp (v6)                ALLOW       Anywhere (v6) 

Y para evitar paranoias, nada como un túnel SSH

Cualquier manual básico de Cardano nos explica que la necesidad de disponer un cierto número de relés además del nodo productor de bloques (o núcleo) tiene que ver con una cuestión básica de seguridad: el núcleo jamás debería estar directamente expuesto a la red. De este modo, los únicos nodos que pueden ser comprometidos son los relés, y si disponemos de más de uno, bastaría con apagarlo y usar los otros mientras arreglamos el desaguisado. Pero claro, la primera pregunta que surge es… y si el núcleo no está abierto a la Internet… ¿cómo diablos me conecto a él para configurarlo? Si te has hecho esta pregunta, felicidades: vas por el buen camino (y estás jodido).

Y ahora, veamos las distintas respuestas:

1.- ¡Qué le vamos a hacer!

Dejaremos el puerto ssh abierto con un porrón de medidas de seguridad, especiamente, autenticación multifactorial (MFA) y cruzaremos los dedos.

Bien, no está mal. Conozco a unos cuantos que han seguido esta política y aún no les ha pasado nada. Afortunadamente, un servicio ssh bien configurado es muy seguro.

2.- Restringimos también el acceso ssh a través del relé.

Es decir, nos conectaremos mediante ssh al relé, y de allí al núcleo. Es una medida muy sensata. Y lo único que requiere es modificar un poquito el firewall para que el acceso al núcleo quede restringido únicamente al relé.

Añadimos una regla por aquí… (la que nos permitirá el acceso al núcleo desde el relé)

sudo ufw allow proto tcp from <IP_RELAY> to any port 22

Con lo cual:

Status: active

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW       Anywhere                  
6000/tcp                   ALLOW       <IP_RELAY>             
22/tcp                     ALLOW       <IP_RELAY>             
22/tcp (v6)                ALLOW       Anywhere (v6)   

Y eliminamos otra por allá… (la que permitía el acceso ssh indiscriminado)

sudo ufw delete 1
Deleting:
 allow 22/tcp
Proceed with operation (y|n)? y
Rule deleted

Y ya estaría. Sin embargo, esta estrategia plantea un nuevo problemilla nada trivial. Como comentábamos en el apartado relativo a la conexión segura con SSH, si ya era importante no dejar la llave privada en cualquier máquina local, aún más lo será no dejarla permanentemente en el relé para cuando necesitemos realizar la conexión al núcleo desde allí. La solución de copiarla mediante scp y borrarla cada vez puede ser una opción, pero desde luego resulta lo bastante incómoda como para caer en la muy humana tentación de dejarla almacenada en el relé y confiar en el sistema 2FA. Aunque, si todo este guirigay de relés tienen que ver con considerar la posibilidad de que se estos puedan verse comprometidos, ya no parece tan buena opción, ¿verdad?. Afortunadamente, aún existe una tercera opción (y más que habrá, pero hasta aquí llega mi cacumen por hoy).

3. Crear un túnel ssh desde la máquina local al núcleo a través del relé

Y una vez establecido dicho túnel, conectarnos directamente desde la máquina local al núcleo. Tenemos como prerequisito que la máquina núcleo sea accesible únicamente desde la máquina relé, lo cual ya ha quedado descrito en la opción número 2. La magia vienen a continuación:

En primer lugar, creamos un túnel a través del relé que redirija un puerto de nuestra máquina local a otro puerto de la máquina núcleo.

ssh -L 8022:<IP_CORE>:22 <user_relay>@<IP_RELAY> -i /route-to-key/private-key

Este comando crea un túnel desde el puerto 8022 de mi máquina local al puerto 22 de la máquina core. Utilizamos el puerto local 8022 ya que el puerto 22 suele estar ocupado por el servidor ssh local y es preferible utilizar uno libre para evitar conflictos. Los datos de conexión son los mismos que usamos para conectar al relé: el nombre de usuario y su IP. Y, por supuesto, la clave pública que guardamos en nuestro pendrive, y que se especifica con el parámetro -i (identidad). Si todo va bien, el único dato que nos pedirá será el código del Google Authenticator y el túnel quedará establecido.

Desde ahora, cualquier petición al puerto 8022 a la máquina local será como si la realizáramos directamente al puerto 22 del nucleo, por lo que podremos conectar simplemente así:

ssh  -p 8022 <user>@127.0.0.1 -i /route-to-key/private-key 

Es decir, que en realidad nos estamos conectando a nuestra propia máquina en el puerto 8022. Y, por supuesto, el archivo de clave privada permanece a buen recaudo en nuestro preciado pendrive. ¿A que mola?

Publicado por David en Cardano, Linux, Seguridad, SSH, 0 comentarios

Iniciar un servicio mediante systemd

En este caso se trata de una Raspberry Pi que controla unos paneles de leds que muestran, además de la fecha y la hora, algunos mensajes de información provenientes del controlador domótico mediante el protocolo MQTT, tales como la temperatura de distintas estancias, o la música que se está reproduciendo en ese momento (Y sí, os habéis fijado bien. La fecha viene en Esperanto. Qué le vamos a hacer…). El dispositivo cuenta además con un sensor de luz, el cual le permite, además de regular la luminosidad de los leds en función de la luz ambiente, proporcionar al sistema información sobre el nivel de luminosidad real de la estancia, permitiendo un control preciso de persianas y luces regulables.

Aspecto del reloj con matriz de leds en la parte superior de la librería de mi dormitorio, junto a algunos libros y colecciones de pelis en viejos archivadores de CD’s. Todo un compendio de mis pequeñas pasiones 😉

Para que el dispositivo funcione de manera autónoma, el programa deberá arrancar automáticamente cuando se encienda la placa, lo cual se puede lograr de varias maneras. Una de ellos, seguramente la más conocida, consiste en añadir la ruta al ejecutable en el archivo /etc/rc.local, aunque también es posible iniciar tareas utilizando el servicio crontab. En ambos casos tenemos un pequeño problema, y es que si el programa se cuelga o experimenta una salida inesperada debida a cualquier excepción no convenientemente capturada, todo el dispositivo se detiene dejando de funcionar.

Para salir al paso, una solución consiste en ejecutar el programa como un servicio. En el  caso que nos ocupa vamos a utilizar el sistema systemd, el cual presenta, entre otras, la gran ventaja de que si se produce un error y el progama se detiene, él mismo se reinicia automáticamente. Algo que a los manazas que vamos dejando rastros de programas mal depurados nos viene que ni de perlas. También es un sistema bastante robusto y que nos permite ajustar las condiciones adecuadas para la ejecución del probrama.

Lo primero que necesitamos es un archivo de script que contenga las instrucciones de inicio de nuestro programa. Si fuera necesario pasar parámetros para el inicio este archivo sería un lugar adecuado para especificarlos. En nuestro caso, es una simple llamada a la ejecución de un script Python:

nano /home/pi/mqtt-matrix/start.sh

#!/bin/bash
#
python3 /home/pi/mqtt-matrix/mqtt-matrix.py

Lo siguiente será la creación del archivo de unidad de servicio. Éste contendrá el nombre con el que queremos que systemd reconozca a nuestro servicio. Aunque sería improbable, no está de más comprobar que no vamos a darle un nombre que ya se encuentra en uso. Para ello, basta con echar un vistazo al contenido de la carpeta que suele contener los archivos de unidad de servicio:

cd /etc/systemd/system/
ls

También podemos probar este comando más específico y que resulta de lo más molón:

sudo systemctl list-unit-files --type=service

En mi caso, como el programa que pretendo ejecutar está en el archivo mqtt-matrix.py, he decicido que el servicio se llame mqtt-matrix.service y listos. Así que creamos el archivo en cuestión:

sudo nano /etc/systemd/system/mqtt-matrix.service

[Unit]
Description=mqtt-matrix
Wants=network.target
After=syslog.target network-online.target

[Service]
Type=simple
ExecStart=/home/pi/mqtt-matrix/start.sh
Restart=on-failure
RestartSec=10
KillMode=process

[Install]
WantedBy=multi-user.target

El archivo que hemos creado contiene las opciones más comunes, la mayoría de las cuales resultan bastante autoexplicativas. Además, podríamos especificar el usuario y el grupo bajo el cual se va a ejecutar nuestro servicio. Si no se especifica nada, por defecto se ejecutará con permisos de administrador, que es exactamente lo que deseamos, por ser uno de los requisitos del modo en el que accedemos a los pines de nuestra raspberry, así que lo dejamos tal cual.

Y ya está. A continuación enumeramos varios de los comandos más utilizados para manejar los servicios systemd

Recargar los archivos de servicio después de alguna modificación

sudo systemctl daemon-reload

Iniciar el servicio

sudo systemctl start mqtt-matrix.service

Detener el servicio

sudo systemctl stop mqtt-matrix.service

Reiniciar el servicio

sudo systemctl restart mqtt-matrix.service

Comprobar el estado del servicio

Además, nos proporciona información adicional, tal como:

  • Avisarnos si tenemos errores en la sintaxis del archivo, aunque el servicio no esté en ejecución
  • Indicarnos la ruta del ejecutable
  • Indicarnos la ubicación del archivo de servicio si no está donde esperábamos
sudo systemctl status mqtt-matrix.service

Y, lo más importante, teniendo en cuenta de qué va todo esto, indicarle al sistema que queremos que el servicio arranque automáticamente al inicio

sudo systemctl enable mqtt-matrix.service

O bien, deshabilitar el arranque al inicio

sudo systemctl disable mqtt-matrix.service
Publicado por David en Domótica, Linux, 0 comentarios
Construir un Stake Pool con CNTools

Construir un Stake Pool con CNTools

GUILD Operators es uno de los muchos grupos de trabajo que rodean al proyecto Cardano. Se han hecho muy populares por desarrollar un conjunto de herramientas que permiten instalar y mantener un nodo, automatizando muchas de las (a menudo) tediosas tareas requeridas para operar a mano. Sin embargo, muchos operadores no las recomiendan por diversos motivos:

  • Al tratarse de una capa de software que envuelve al núcleo del software oficial puede contener errores e, incluso, algunas alteraciones maliciosas introducidas subrepticiamente por algunos colaboradores del proyecto más o menos espontáneos.
  • Su documentación oficial no está muy detallada y no favorece la comprensión de lo que está ocurriendo por debajo.
  • Alimenta la tentación de montar un nodo en cuatro pasos sin conocer con la suficiente profundidad el funcionamiento del software que está en su núcleo, lo que puede dar una falsa sensación de seguridad.

Lo cierto es que ellos mismos aconsejan que nadie utilice sus herramientas como una excusa para ahorrarse el proceso de aprender a manejar el nodo a mano siempre que sea necesario. De hecho, he observado que hay un número no despreciable de gente montando nodos siguiendo tutoriales hechos por operadores (algunos muy bien currados, todo hay que decirlo) al pie de la letra, sin llegar a comprender completamente lo que están haciendo. Huelga decir que cualquier tutorial debe ser utilizado, precisamente, como herramienta de aprendizaje, y nunca como un folleto de instrucciones para poner en marcha una máquina cuyo funcionamiento desconocemos.

Así que, después de practicar y aprender los fundamentos del funcionamiento de los diversos conceptos de Cardano, algunos de los cuales voy vertiendo por aquí, finalmente me he decidido a probar estas famosas herramientas. Lo que viene a continuación son algunas de las anotaciones que he ido haciendo a medida que avanzaba en la configuración, puesta en marcha y mantenimiento del nodo. No pretende ni puede ser una guía exhaustiva de cómo usar las CNTools y demás herramientas. Tan sólo un bloc de notas con aquellos aspectos que me ha parecido relevante destacar.

Leerse la documentación de cabo a rabo

Ésta es mi primera recomendación. Además, no es muy larga. La propia documentación no está escrita en modo tutorial, por lo que hay muchos detalles e informaciones importantes que aparecen después de que hubiera sido necesario tenerlos en cuenta.

Descargar el repositorio y construir el nodo

Este paso no plantea ninguna dificultad en especial. Existen algunos parámetros que pueden ser configurados, pero viene bien explicado en la documentación. En caso de duda, las opciones por defecto dan un buen resultado.

Es interesante familiarizarse con la estructura de directorios creada por la herramienta, ya que necesitaremos navegar por ella para editar, configurar y ejecutar los diferentes archivos de script. Aunque todo esto ya lo cuentan ellos mismos.

Por ejemplo, en el directorio de scripts existe un archivo, llamado env en el que aparecen muchas variables de entorno. Una de las principales (y más útiles en el día a día) es CNODE_HOME, que es la que contiene la ruta en la que se instalará el nodo. Podemos ahorrarnos mucho tecleo cada vez que queremos cambiar a dicha ruta tecleando

cd $CNODE_HOME

en lugar de

cd /opt/cardano/cnode

o cualquier otra ruta de la que se trate

Instalar como un servicio systemd

Aquí viene la primera peculiaridad. Las herramientas contienen un script que automatiza los pasos necesarios para instalar el programa como un servicio del sistema, copiando los scripts de inicio y creando los enlaces simbólicos adecuados. No es algo que resulte difícil de hacer a mano, pero es necesario comprender de qué se trata, lo cual queda fuera del objeto de esta entrada. Para ello, mejor echar un vistazo aquí.

Además, a la hora de ejecutarse el script nos pregunta si queremos instalar un servicio adicional llamado topologyUpdater. Este servicio es el que se encarga (de manera provisional, según el equipo oficial de Cardano, a falta de un servicio de autodescubrimiento P2P que formará parte en un futuro del propio nodo), de descargar una lista de los nodos a los que conectarse, más allá del enlace por defecto a la red de Cardano). Pues bien. Aquí conviene contestar NO al nodo productor de bloques (al que también llamaremos núcleo, que es más corto) y YES al nodo relé.

Por cierto, que el script se llama deploy-as-systemd.sh

Una vez instalado podemos encontrar los scripts de los servicios en las siguientes ubicaciones

Para el productor de bloques:

ls /etc/systemd/system/ | grep cnode
cnode.service

Y, para el relé:

ls /etc/systemd/system/ | grep cnode
cnode-tu-fetch.service
cnode-tu-push.service
cnode-tu-push.timer
cnode-tu-restart.service
cnode-tu-restart.timer
cnode.service
cnode.service.wants

El servicio correspondiente al nodo es el que se llama simplemente cnode.service. Todos los que contienen la expresión ‘-tu-‘ son los referidos al topologyUpdater

Notar que en este punto ambos nodos mostrarán el mismo aspecto desde la herramienta gLiveView y que ambos están funcionando como relés. De hecho, ni siquiera se comunican entre sí, sino que se lo hacen directamente con la red Cardano, sincronizando la cadena de bloques. Por cierto, no es mala idea dejar que terminen de sincronizar antes de continuar. Así que vete a tomar un café o a dar una vuelta, ya que el proceso durará varias horas y hay más mundo ahí fuera que la pantalla del ordenador.

Configurar parámetros de los nodos

Puerto y nombre del pool (sólo en el núcleo) se ajusta en $CNODE_HOME\scripts\env

Nº de núcleos se ajusta en $CNODE_HOME\scripts\cnode.sh

 

Conectar los nodos entre sí (Actualizar la topología)

Éste es uno de los pasos que no sale en la documentación, pero para que los nodos se comuniquen entre sí es necesario especificar sus direcciones IP en los archivos de topología. Dichos archivos se encuentran en:

 nano $CNODE_HOME/files/topology.json

Veremos que en este momento estos archivos contienen información diferente para el núcleo y para el relé. En el caso del núcleo tendremos la configuración de topología por defecto que proporciona un enlace estándar para conectar con los relés de la red Cardano. En el caso del relé, tendremos una expresión algo más compleja pero que si la miramos con detalle veremos que contiene básicamente lo mismo, más un mensaje de error que nos dice que aún no estamos autorizados para descargar la lista de relés. No hay que alarmarse, todo va bien. Simplemente, el acceso a esta lista está reservado a los nodos registrados, y el nuestro aún no lo está. Todo llegará.

De momento, actualizaremos la topología para permitir que los nodos puedan hablar entre sí (y no sólo con la red Cardano, cosa que sólo el relé debería hacer, por cierto).

En el núcleo, actualizaremos el archivo de topología manualmente, poniendo la IP y el puerto del relé:

nano $CNODE_HOME/files/topology.json

{
  "Producers": [
    {
      "addr": "RELAY_IP_ADDRESS",  
      "port": RELAY_PORT,  
      "valency": 1
    }
  ]
}

Si tenemos más de un relé, bastará con ponerlos todos uno a continuación de otros separados por comas, de acuerdo con la sintaxis JSON: Cada máquina está representada por un objeto dentro de un vector que da valor a la variable «Producers».

Y en el relé, como su topología se actualiza automáticamente, lo que editaremos es el script:

nano $CNODE_HOME/scripts/topologyUpdater.sh

Ahora buscaremos la línea con el parámetro CUSTOM_PEERS y la descomentaremos (aunque personalmente yo prefiero copiar la línea debajo sin el #; de esta manera conservo los comentarios originales) y especificaremos la dirección y puerto de nuestro núcleo con la sintaxis indicada en los comentarios del archivo. Si conocemos más relés de otros nodos de sean de nuestra confianza (es decir, de operadores amiguetes nuestros) los colocaremos también aquí.

Si reiniciamos el relé y comprobamos el aspecto del archivo de topología comprobaremos que su contenido se ha modificado (además de su aspecto, mucho más legible) y que ahora también incluye la información de conexión al núcleo.

Nota: En este momento, en teoría bastaría con reiniciar el servicio en ambas máquinas para que todo empezara a funcionar, pero he observado que para que las nuevas topologías surtan efecto puede ser necesario reiniciarlas.

Si ahora comprobamos la información en gLiveView, veremos que ambos nodos han cambiando la información de sus pares. El núcleo ya sólo tiene uno (el relé), y el relé tiene uno más (el núcleo). A partir de ahora, todo el tráfico que llegue al núcleo pasará necesariamente por el relé, así que sería un buen momento para activar el firewall y restringir las conexiones entrantes al núcleo a las provinientes del relé. Todo, lógicamente, dependiendo de nuestro plan de seguridad, según como tengamos estructurada la red.

Si lo que queremos es actualizar las CNTOOLS (Y eventualmente el nodo)

Lo primero es actualizar el script prereqs.sh y ejecutarlo con la opción -f, tal y como viene indicado en la página de GUILD Operators

La opción -f sobreescribirá los scripts y archivos de configuración, pero guardará una copia de aquellos que hayan sido modificados. Entre estos, los más importantes son:

/scripts/env    donde se especifica el puerto y el nombre del pool (sólo para el núcleo)

/scripts/cnode.sh  donde especificamos el nº de núcleos a utilizar

/files/topology.json (sólo para el núcleo, tal y como se ha descrito más arriba)

files/topologyUpdater.sh (sólo para el relé, tal y como se ha descrito más arriba)

 

Publicado por David en Cardano, 0 comentarios

Tipos de direcciones en Cardano

Explicado de una manera muy sencilla, una dirección en Cardano es básicamente un número que puede contener fondos. En este sentido, podríamos verlo como el número de cuenta bancaria en la cual mantenemos nuestro dinero. Los fondos pueden transferirse entonces de unas direcciones a otras mediante unas operaciones llamadas transacciones (el equivalente a una transferencia bancaria)

La cadena de bloques sería la base de datos en la cual quedan registradas de manera inalterable todas las transacciones en que las distintas direcciones se ven implicadas. De esta manera, consultando la cadena de bloques es posible conocer el saldo de cualquier dirección, así como todo el histórico de transferencias

En Cardano existen básicamente dos tipos de direcciónes, a las que nos referiremos como direcciones de pago (payment) y de stake.

Cada uno de este tipo de dirección está asociada a un par de claves, una pública y otra privada. A la dirección pública la llamamos de verificación y a la privada de firma.

La clave de verificación, abreviadamente vkey (que es pública), es la que permite que cualquiera pueda verificar su existencia y su saldo en la cadena de bloques. La clave de firma, abreviada como skey (que es privada) servirá para firmar las transacciones que permiten transferir fondos de una dirección a otra.

Generación de claves y direcciones

El proceso de generación de cualquiera de las claves es muy similar: Primero se solicita a la herramienta cardano-cli la creación del par de claves, vkey y skey, y, a partir de la primera, se genera la clave en cuestión.

Generación de las claves y la dirección de payment:

cardano-cli address key-gen \
--verification-key-file payment.vkey \
--signing-key-file payment.skey

A partir de la clave de verificación generamos la dirección:

cardano-cli address build \
--payment-verification-key-file payment.vkey \
--out-file payment.addr \
--testnet-magic $MAGIC_NUM

Generación de las claves y la dirección de stake:

cardano-cli stake-address key-gen \
--verification-key-file stake.vkey \
--signing-key-file stake.skey

A partir de la clave de verificación generamos la dirección::

cardano-cli stake-address key-gen \
--verification-key-file stake.vkey \
--testnet-magic $MAGIC_NUM

Un par de observaciones:

  • Las claves son generadas con independencia de la red en la cual vayan a ser utilizadas. Sin embargo, para generar la dirección, es necesario especificar a qué red va a pertenecer (testnet o mainnet)
  • Aunque cada dirección lleva asociadas dos claves, una pública y otra privada, la generación de la dirección se realiza utilizando únicamente la clave pública.

Diferencias entre claves de payment y de stake

  • La clave de payment es una clave que sirve, básicamente, para almacenar, recibir y transferir fondos.
  • La clave de stake sirve para tomar el control en la participación en el protocolo. Permite delegar fondos en los diferentes Stake Pools y también es donde se reciben las recompensas por ello. Será necesaria, también, si queremos crear nuestro propio Stake Pool. No es posible, sin embargo, transferir fondos a una dirección de Stake, aunque sí lo es transferir las recompensas recibidas hacia otras direcciones, evidentemente.

Es importante señalar que una clave de payment generada como hemos visto, aunque perfectamente funcional, no permite la delegación de fondos en un stake pool. Para que esta delegación sea posible, será necesario que la dirección de payment se encuentre vinculada a una dirección de stake, la cual, a su vez, será la que nos permitirá realizar la delegación. Este procedimiento será el que estudiaremos en detalle en las próximas entradas.

Publicado por David en Cardano, 0 comentarios

Registrar Stake Pool

En primer lugar, podemos ya probar a ejecutar nuestro stake pool con las claves y el certificado:

cardano-node run \
--topology testnet-topology.json \
--database-path ./db \
--socket-path ./db \
--host-addr 127.0.0.1 \
--port 3000 \
--config testnet-config.json \
--shelley-kes-key kes.vkey \
--shelley-vrf-key vrf.vkey \
--shelley-operational-certificate node.cert

Si bien la salida que obtendremos no será más que un conjunto de mensajes no demasiado reveladores, sí que podremos comprobar que cuando se ejecutan el productor y algún relé simultáneamente, realmente se están comunicando entre sí. Si queremos ver algo más descriptivo de cómo está funcionando nuestro nodo necesitaremos instalar alguna de las herramientas proporcionadas por GUILD Operators, concretamente, gLiveView.

  • Crear certificados

Crear el archivo de metadatos

blah, blah

Crear el certificado de registro

cardano-cli stake-pool registration-certificate \
    --cold-verification-key-file node.vkey \
    --vrf-verification-key-file vrf.vkey \
    --pool-pledge 1000000000 \
    --pool-cost 345000000 \
    --pool-margin 0.01 \
    --pool-reward-account-verification-key-file stake.vkey \
    --pool-owner-stake-verification-key-file stake.vkey \
    --testnet-magic $MAGIC_NUM \
    --single-host-pool-relay <rele.dominio.net> \
    --pool-relay-port 3000 \
    --metadata-url https://miweb.net/metadata \
    --metadata-hash <metadatahash> \
    --out-file pool-registration.cert

Aquí hemos definido algunos parámetros básicos, como la cantidad que estamos dispuestos a comprometer en nuestro pool (1000A), el margen de ganancias (1%), el coste de operación (345A). También pondremos la IP pública (o el nombre de dominio) y el puerto público al que conectarse. Si estamos detrás de un router NAT no olvidemos redirigir este puerto a nuestra IP privada.

También vemos que la dirección de stake para recibir recompensas y para la delegación de los propietarios (que no es otra cosa que la delegación necesaria para cubrir el pledge), son la misma, si bien podrían ser direcciones diferentes. A este respecto, señalar que para proporcionar fondos para el pledge es necesario transferir los mismos a la dirección payment.addr asociada a esta dirección de stake. Para ello creamos el siguiente…

Certificado de delegación

cardano-cli stake-address delegation-certificate \
--stake-verification-key-file stake.vkey \
--cold-verification-key-file cold.vkey \
--out-file pool-delegation.cert

Esto crea un certificado que delega los fondos de la dirección asociada a stake.vkey al pool asociado a la clave node.vkey

  • Registrar el Stake Pool

Este paso consiste en realizar la transacción que enviará nuestros certificados de registro y de delegación a la cadena de bloques. Se trata de una transacción en la que, además de la tarifa ordinaria, deberemos incluir el depósito de registro del pool. Sucesivos re-registros (para cambiar parámetros como el pledge o el archivo de metadatos) requerirán una transacción similar pero sin incluir este depósito.

Borrador de la transacción

cardano-cli transaction build-raw \
--tx-in <TxHash>#<TxIx> \
--tx-out $(cat payment.addr)+0 \
--invalid-hereafter 0 \
--fee 0 \
--out-file tx.draft \
--certificate-file pool-registration.cert \
--certificate-file pool-delegation.cert

Es similar al borrador de una transacción estándar salvo que en las últimas líneas especifican las opciones para incluir los certificados.

Cálculo de la tarifa mínima

cardano-cli transaction calculate-min-fee \
--tx-body-file tx.draft \
--tx-in-count 1 \
--tx-out-count 1 \
--witness-count 3 \
--byron-witness-count 0 \
--mainnet \
--protocol-params-file protocol.json

Igual que siempre

Cálculo del cambio

En este caso, para calcular el cambio debemos deducir, no sólo la tarifa, sino también el depósito de registro. Dicho depósito lo obtenemos de los parámetros del protocolo (esos que antes hemos obtenido y guardado convenientemente en un archivo llamado protocol.json). Como no recuerdo exactamente el nombre del parámetro, que además va cambiando de una versión a otra, tecleo…

cat protocol.json | grep Deposit
    "stakePoolDeposit": 500000000,
    "stakeAddressDeposit": 2000000,

En este caso, el valor que nos interesa es el primero: 500 A

Ya sólo nos queda calcular el cambio como <Balance de UTxO> – tarifa – 500

Construir transacción preliminar

cardano-cli transaction build-raw \
--tx-in <TxHash>#<TxIx> \
--tx-out $(cat payment.addr)+<Cambio> \
--invalid-hereafter <ttl> \
--fee <tarifa> \
--out-file tx.raw \
--certificate-file pool-registration.cert \
--certificate-file pool-delegation.cert

Firmarla

cardano-cli transaction sign \
--tx-body-file tx.raw \
--signing-key-file payment.skey \
--signing-key-file stake.skey \
--signing-key-file cold.skey \
--mainnet \
--out-file tx.signed

Y enviarla

cardano-cli transaction submit \
--tx-file tx.signed \
--testnet-magic $MAGIC_NUM
Publicado por David en Cardano, 0 comentarios

Generar claves y certificado de operación del Stake Pool

El Stake Pool necesita tres pares de claves, a saber:

  • Node (Cold) Keys: Son las claves del nodo propiamente dicho. A menudo también se les llama Cold Keys por la importancia de ser mantenidas siempre almacenadas en un entorno frío, es decir, fuera del alcance de una conexión a internet, ya que con estas claves se puede obtener el control del Stake Pool. Idealmente, cualquier uso de estas claves debería hacerse en una máquina desconectada de internet, copiando posteriormente los archivos generados al nodo.
  • VRF Keys (Verification Random Function Keys): Son las claves que nos permitirán participar en el procedimiento de Staking.
  • KES Keys (Key Evolving Signature Keys): Claves que van cambiando cada cierto periodo de tiempo para evitar que alguien pueda hacer trampas introduciendo bifurcaciones en la cadena de bloques a partir de bloques anteriores.

Además de estos pares de claves necesitaremos generar un Certificado Operacional

  • Claves del Nodo (Cold)

cardano-cli node key-gen \
    --cold-verification-key-file node.vkey \
    --cold-signing-key-file node.skey \
    --operational-certificate-issue-counter node.counter
  • Claves VRF

cardano-cli node key-gen-VRF \
    --verification-key-file vrf.vkey \
    --signing-key-file vrf.skey
  • Claves KES

cardano-cli node key-gen-KES \
    --verification-key-file kes.vkey \
    --signing-key-file kes.skey

Como vemos, estos tres pares de claves se generan de la nada, es decir, no necesitan ningún archivo previo y su creación no reviste ninguna dificultad. Cada par de claves, como es habitual, consta de una clave de firma (skey) y una clave de verificación (vkey).

  • Certificado operacional

Este certificado es el que nos permitirá ejecutar nuestro nodo de manera que sea candidato para firmar bloques. Para generar este archivo sí que necesitamos contar con algo de información previa, tanto del protocolo como del estado actual de la cadena de bloques. Concretamente, necesitamos saber el nº de periodo KES en el que nos encontramos, y para ello debemos dividir el nº del Slot actual por el nº de slots por periodo

Consultamos el nº de slot por periodo KES a partir de los archivos de información:

cat testnet-shelley-genesis.json | grep KES

"slotsPerKESPeriod": 129600,
"maxKESEvolutions": 62,

De esta salida nos interesa el primer parámetro, en nuestro caso: 129600

Y consultamos el slot actual (con el nodo completamente sincronizado)

 cardano-cli query tip --testnet-magic $MAGIC_NUM
{
    "epoch": 166,
    "hash": "b7331f9a6ec5de5871a74d02807d3fe811cd3f03258cd7542e6002cba592f310",
    "slot": 41772106,
    "block": 3049366,
    "era": "Alonzo",
    "syncProgress": "100.00"
}

Entonces, dividiento slot / slotsPerKESPeriod

expr 41772106 / 129600
322

Que será el nº de periodo KES a partir del cual será válido nuestro certificado. Con este dato ejecutamos:

cardano-cli node issue-op-cert \
    --kes-verification-key-file kes.vkey \
    --cold-signing-key-file node.skey \
    --operational-certificate-issue-counter node.counter \
    --kes-period 322 \
    --out-file node.cert

Y obtenemos el certificado operacional del nodo. Con este archivo ya podemos ejecutar nuestro nodo activamente, es decir, participando en la firma de bloques.

Como medida de seguridad, las claves dejan de ser válidas tras un periodo de 90 días, por lo que tienen que ser regeneradas antes de ese plazo para que el pool siga operando. El proceso es tan sencillo como generar un nuevo par de claves KES y crear otro certificado operacional a partir de la nueva kes.vkey. Despues, basta con reiniciar el productor de bloques y el nodo ya se ejecutará con el nuevo certificado.

Publicado por David en Cardano, 0 comentarios
Funciones útiles con el Subsistema Linux en Windows 10 (WSL)

Funciones útiles con el Subsistema Linux en Windows 10 (WSL)

  • Acceder a una memoria USB desde WSL

En la estructura de archivos del subsistema de Linux vienen por defecto varios directorios con las típicas letras de unidad de disco de windows. En efecto, si miramos el contenido de la carpeta de montaje:

ls /mnt

c d e f

Sin embargo, cuando entramos en el directorio correspondiente a la letra de unidad donde tenemos insertada la memoria, resulta que está vacío. Al parecer, las unidades externas no se montan automáticamente, sino que hay que hacerlo explícitamente mediante el comando mount . En el siguiente ejemplo, vemos los parámetros con los que ejecutar este comando cuando montamos la unidad presente en E:

sudo mount -t drvfs E: /mnt/e

Y para desmontar, exactamente igual que en Linux…

sudo sudo umount /mnt/e
  • Acceder a los archivos del espacio WSL desde Windows

El espacio de archivos utilizado por WSL está disponible directamente desde el explorador de Windows 10 introduciendo esta ruta en la barra de direcciones del explorador de archivos: \\wsl$\Ubuntu

Nota: Si nos fijamos en la notación, veremos que Windows 10 maneja esta ruta como un sitio de red. Para que la ubicación de red esté montada, será necesario que en ese momento se esté ejecutando la sesión correspondiente en el WSL

Publicado por David en Blog, Linux, 0 comentarios