addendum

[mise à jour 23.04.24]

  • correction de sécurité
  • ajout de tailscale
  • ajout de lnaddrd

Après un échange avec le développeur de lnaddrd , il s’avère qu’utiliser l’API REST de lnd sans son certificat généré m’expose à des attaques Man in the Middle. L’article est donc corrigé avec la configuration adapté.

Introduction

Dans mon précédent article sur Nostr j’avais rapidement parlé de Bitcoin et du lightning network. Pour rappel le lightning network est une surcouche (layer 2) à la blockchain Bitcoin, il a été conçu pour être très rapide en fonctionnant off chain. Il existe un grand nombre de littératures à ce sujet, en commencant par lightning.fr , je vous laisse faire votre recherche .

Ici je ne vais pas expliquer les arcanes de cette techno, étant moi même débutant, mais simplement montrer comment déployer et héberger un noeud lnd sur une infra Nomad . Il existe plusieurs implémentations de la spec, comme Eclair en scala (jvm) de la société ACINQ ou bien rust-lightning . J’ai choisi lnd car il est en Go et bien intégré à Zeus un wallet non custodial . Alors on entre ici dans un nouveau monde, celui de la crypto monnaie, qui possède 3 tonnes de termes étranges et de concepts abscons. Il existe de nombreuses chaines Youtube et Twitch dédiées comme celles de Hasheur qui expliquent tout mais quand vous débarquez c’est incompréhensible 🥴
En résumé non custodial veut dire que vous possédez physiquement vos cryptos, mais charge à vous de les sécuriser et les sauvegarder. Custodial veut dire que vous passez par un tiers à qui vous déléguez le stockage et la sécurisation de vos cryptos, comme une banque donc ; c’est plus simple mais vous ne possedez pas votre clé privée. Les anciens clients de Mt.Gox/QuadrigaCX /FTX/NextFuckingScammer comprendront ce que je veux dire 🤧

Zeus

Dans l’article sur Nostr je parlais de Alby qui permet de simplifier la connexion à Nostr, mais propose aussi de gérer vos Satoshis via une adresse @getalby.com. C’est donc un portefeuille custodial, ce qui n’est pas forcément génant pour de petites sommes mais l’objectif ici est d’une part d’être complêtement indépendant et d’autre part participer au développement du réseau lightning.
J’ai débuté avec Zeus en connectant l’application avec mon compte Alby comme expliqué ici . Au final, j’ai reçu quelques Satoshi via Nostr 🤙 en précisant mon adresse fredix@getalby.com dans le client Nostr. Puis je pouvais les consulter sur l’application mobile Zeus et en théorie effectuer des paiements via cette même application dans les commerces acceptant le lightning (je n’ai pas encore eu l’occasion de tester).
Zeus permet d’ajouter plusieurs comptes, comme un noeud embarqué dans l’application, ou un noeud externe LND REST ce que l’on va utiliser.

LND

Pour tout dire, ce qui m’a motivé à me lancer est ce post sur Stacker.news incredibly impressed by LND + Neutrino .

Et surtout

I’ve run Lightning nodes before, but they were always backed by a full archival Bitcoin Core instance, with hundreds of gigabytes of storage required for the full chain and index. This time, I wanted to have a go at running an ultra-light lnd instance on my puny little VPS with as tiny a storage footprint as possible.

Et oui, de base un noeud lightning a besoin de s’interfacer avec un noeud Bitcoin pour écrire dans la blockchain quand c’est nécessaire. Et qui dit noeud Bitcoin dit des centaines de giga de données blockchain (à ce jour environ 563.61 Go d’après ce site ). Or neutrino permet de s’en passer et de n’utiliser que : 322 Mo sur mon instance 😮

⚠️ Attention à ce jour je n’ai aucune idée du comment tout cela fonctionne précisément, de plus la configuration en dessous est faite sur le mainnet, le réseau de prod de Bitcoin. Amusez vous avec de très petites sommes

Pour tester, utilisez les options --bitcoin.testnet --neutrino.connect=faucet.lightning.community ⚠️

Le projet propose des documentations pour démarrer avec Docker ce qui m’a permis d’écrire la version en HCL. Grâce au script start-lnd.sh et au fichier de configuration d’exemple sample-lnd.conf j’ai pu déduire les informations nécessaires à cette migration.
De base le service utilise 2 ports externes, le 9735 pour se connecter au réseau P2P lightning et le 8080 (j’utilise le 8081 mais peu importe) pour l’API REST. C’est via cette API que l’on va pouvoir connecter le client mobile Zeus et elle m’a posé quelques problèmes. En effet par sécurité lnd génère des certificats tls pour accéder à l’API, certificat à ajouter dans son smartphone Android pour que Zeus puisse se connecter, donc c’est super lourdingue.
Or j’utilise déjà des certificats Let’s Encrypt générés par Caddy qui renvoie vers l’API, la connexion est donc sécurisée. Caddy doit donc nécessairement se connecter en HTTP sur le port dans le conteneur. Tout va bien, lnd propose de désactiver le TLS avec l’option --no-rest-tls , sauf que si on lui demande d’écouter sur toutes les interfaces réseau avec --restlisten=0.0.0.0:8081 il refuse de se lancer (même soucis que chpio )

detected RPC server listening on publicly reachable interface 0.0.0.0:8081 with encryption disabled! Refusing to start with –no-rest-tls specified

Par sécurité il n’est pas souhaitable que l’API REST soit accessible depuis Internet. De plus je suis le seul utilisateur de cette API, je vais donc configurer lnd pour que le port REST écoute uniquement sur l’interface réseau de tailscale.

Je lance le conteneur Docker en utilisant un template Nomad afin d’injecter l’IP du conteneur (même si dans l’absolue 0.0.0.0 aurait suffit)

template {
    data = <<EOH
    #!/bin/bash
    CONTAINER_IP=$(ip route get 1.2.3.4 | awk '{print $7}')
	lnd --bitcoin.mainnet --bitcoin.active --bitcoin.node=neutrino 
	--neutrino.addpeer=btcd0.lightning.engineering 
	--feeurl=https://nodes.lightning.computer/fees/v1/btc-fee-estimates.json 
	--restlisten=${CONTAINER_IP}:8081 --tlsextraip=IP_TAILSCALE_NODE1 
	--tlsextradomain=lnd --externalip=VOTRE_PUBLIC_IP --alias=VOTREALIAS	
	EOH
	destination = "local/file.sh"
}      

(Attention les options tlsextraip et tlsextradomain sont très importantes car c’est ce qui va permettre à votre certificat d’être valide dans Zeus et dans lnaddrd).
Pour forcer le lancement de ce script au démarrage du conteneur il faut écraser l’entrypoint de l’image, qui lance normalement la commande lnd, par la commande bash:

entrypoint = [
	"bash",
	"-c",
	"chmod 777 /local/file.sh && ./local/file.sh",
]

Voici le fichier HCL final

lnd.hcl

job "lnd" {
  datacenters = ["dc1"]
  type = "service" 
  group "app" {
     count = 1

     network {
				port "tcp" {
				  to     = 9735 # container port the app runs on
				  static = 9735   # host port to expose
				}
				port "rest" {
					to     = 8081 # container port the app runs on
					static = 8081   # host port to expose
					host_network = "tailscale"
			  }		  				

				dns {
				  servers = ["172.17.0.1", "8.8.8.8", "8.8.4.4"]
				}			
      }


     task "lnd" {
     		driver = "docker"

				constraint {
				 attribute = "${attr.unique.hostname}"
				 value     = "node1"
				}

				template {
				    data = <<EOH
				    	#!/bin/bash
				      CONTAINER_IP=$(ip route get 1.2.3.4 | awk '{print $7}')
					  lnd --bitcoin.mainnet --bitcoin.active --bitcoin.node=neutrino --neutrino.addpeer=btcd0.lightning.engineering --feeurl=https://nodes.lightning.computer/fees/v1/btc-fee-estimates.json --restlisten=${CONTAINER_IP}:8081 --tlsextraip=IP_TAILSCALE_NODE1 --tlsextradomain=lnd --externalip=EXTERNAL_PUBLIC_IP --alias=VOTREALIAS
					EOH
				    destination = "local/file.sh"
				}      

				config {
					image = "lightninglabs/lnd:v0.17.4-beta"
				  entrypoint = [
				  	"bash",
				  	"-c",
				  	"chmod 777 /local/file.sh && ./local/file.sh",
				  ]

					volumes = [
						"/votre_volume/lnd:/root/.lnd"
					]				
					 ports = [
					    "tcp", "rest"
					 ]
					}

					resources {
					 cpu = 500
					 memory = 600
					}

					service {
						 name = "lnd"
						 tags = ["global", "app"]
						 provider = "nomad"
						 port = "tcp"
					}
	
					service {
						 name = "lnd"
						 tags = ["global", "app"]
						 provider = "nomad"
						 port = "rest"
					}						

     	}

  }
}

Il faut bien sur changer la section volume avec votre répertoire de destination. Puis:

nomad job run lnd.hcl

lncli

Le conteneur est normalement lancé, maintenant il faut créer son wallet pour pouvoir recevoir et envoyer de la crypto. Pour cela il faut se connecter dans le conteneur sur le host:

docker exec -it ID_DU_CONTENEUR bash

et lancer le commande de création du wallet

lncli create

A ce moment là il faut donner un mot de passe et une passe phrase que je vous conseille de sauvegarder dans un gestionnaire de mots de passe . Bref suivez les consignes demandées par lncli.

Enfin il faut unlock le wallet avec votre mot de passe:

lncli unlock

au bout de quelques minutes vérifiez que votre noeud est bien relié au lightning network:

lncli getinfo

vous devriez voir:

"synced_to_chain": true,
"synced_to_graph": true,

Sortir du conteneur, et dernière action avec le macaroon. C’est un espèce de cookie/token qui autorise la connexion à l’API REST. Il faut l’afficher en hexa et le fournir à Zeus:

Host node1

xxd -p -c2000 /votre_volume/lnd/data/chain/bitcoin/mainnet/admin.macaroon

Copier la sortie

Tailscale

Maintenant que le serveur lnd expose son API REST sur l’interface réseau de tailscale il faut installer le client tailscale sur le smartphone. Une fois tout configuré et le VPN activé on devrait voir comme ici le serveur Node1.

tailscale

Zeus

Retour à Zeus pour terminer. Il est nécessaire d’envoyer sur le smartphone le certificat tls généré par lnd. Il se trouve sur votre serveur (Node1) dans /votre_volume/lnd/tls.cert ; le copier en tls.crt puis l’envoyer avec par exemple rymdport et recevoir via Wormhole William . Ensuite aller dans les paramètres Android, Sécurité et confidentialité / (tout en bas) Sécurité et confidentialité renforcées / Chiffrement et identifiants / Installer un certificat / Certificat CA / Installer quand même (valider) / Sélectionner le fichier tls.crt.

Dans Zeus ajoutez un noeud LND REST et fournir vos informations, dont l’URL de votre domaine géré par Caddy, btc.DOMAIN.tld, l’IP Tailscale de Node1, l’hexa du macaroon et le port 8081. Sauvegardez et revenez à l’accueil, il devrait y avoir 2 barres horizontales, Lightning et On-chain, en cliquant sur ce dernier vous pourrez obtenir une adresse BTC avec laquelle vous pourrez recevoir votre premier versement en étant très patient.

Concernant lightning je n’ai pas pu envoyer de Satoshis de mon compte Alby vers mon noeud, il y a une notion de canal à ouvrir, mais c’est en cours d’exploration 🤔

zeus

lnaddrd

Alby c’est bien mais il serait top d’avoir une adresse@sondomain.tld qui renvoie vers son noeud lnd.
Or j’ai découvert il y a peu ce projet de conduition , lnaddrd qui permet d’avoir sa propre adresse lightning et générer des factures en se connectant à l’instance lnd. Voici le fichier Nomad hcl. A savoir qu’il doit être instancié sur le réseau tailscale afin de pouvoir contacter l’API lnd. Et qu’il a besoin d’accéder en lecture seule au volume de lnd pour lire le macaroon invoices et le certificat lnd.

ln.hcl

job "ln" {
  datacenters = ["dc1"]
  type = "service" 
  group "app" {
     count = 1

     network {
				port "http" {
				  to     = 3441 # container port the app runs on
				  static = 3441
				  host_network = "tailscale"				  
				}
      }

     task "ln" {
     		driver = "docker"

				constraint {
				 attribute = "${attr.unique.hostname}"
				 value     = "node1"
				}

				config {
					image = "fredix/lnaddrd"

					labels = {
						"caddy" = "ln.VOTREDOMAIN.TLD"
	        			"caddy.reverse_proxy" = "http://IP_TAILSCALE_NODE1:3441"
						# remove the following line when you have verified your setup
						# Otherwise you risk being rate limited by let's encrypt
						"caddy.tls.ca" = "https://acme-v02.api.letsencrypt.org/directory"
					}

					extra_hosts = ["lnd:IP_TAILSCALE_NODE1"]

	        mounts = [
		          {
	                type = "bind"
	                target = "/usr/share/lnaddrd"
	                source = "/votre_volume/lnaddrd"
	                readonly = true
	                bind_options = {
	                  propagation = "rshared"
	                }
	            },
						 {
	                type = "bind"
	                target = "/usr/share/lnd"
	                source = "/votre_volume/lnd"
	                readonly = true
	                bind_options = {
	                  propagation = "rshared"
	                }
	            }	            
            ]

					 ports = [
					    "http"
					 ]
					}

					resources {
					 cpu = 300
					 memory = 64
					}

					service {
						 name = "ln"
						 tags = ["global", "app"]
						 provider = "nomad"
						 port = "http"

						 check {
						    type = "http"
						    name = "app_health"
						    path = "/.well-known/lnurlp/VOUS"
						    interval = "20s"
						    timeout = "10s"
						}
					}	

     	}

  }
}

(ce hcl utilise mon image docker “fredix/lnaddrd” que vous pouvez utiliser, il n’y a rien de spécifique, ou bien générer la votre , PR en attente ).
Ce fichier hcl a besoin d’un fichier lnaddrd.yaml à déposer dans /votre_volume/lnaddrd ainsi qu’une image png de votre profil.

# This configures how the webserver will bind and expose its HTTP stack.
# By default it serves unencrypted HTTP. Specify a TLS cert+key to serve
# clients over HTTPS instead.
webserver:
  bind_address: 0.0.0.0:3441              # required
  # tls_cert_file: /path/to/server.tls.cert # optional
  # tls_key_file: /path/to/server.tls.key   # optional

lnurl:
  # This must be the base URL of your server.
  url_authority: https://ln.VOTREDOMAIN.tld # required

  # Both of these will be included in the pay request metadata array.
  # The icon_file can be either a PNG or a JPEG file.
  short_description: "Donation to VOUS" # optional
  icon_file: /usr/share/lnaddrd/icon.png                # required
  
  # Determines the range of acceptable payment amounts.
  max_pay_request_sats: 5000000000 # required
  min_pay_request_sats: 100       # required

  # Determines the expiry time of BOLT11 invoices we create.
  # Defaults to whatever the remote LND instance uses by default.
  invoice_expiry: "1h" # optional
  # invoice_expiry: "20m"
  # invoice_expiry: "100s"

# Accept lightning address requests for the following usernames.
lightning_address_usernames: # optional
  - VOUS

# Configure a connection to LND's REST API.
#
# You can find invoices.macaroon in:     ~/.lnd/data/chain/bitcoin/mainnet/invoices.macaroon
# You can find LND's TLS certificate in: ~/.lnd/tls.cert
#
# Note that your LND certificate MUST have the 'host' field listed as a SAN.
# (hint: use the 'tlsextradomain' option in lnd.conf)
lnd:
  host: lnd:8081                  # required
  macaroon_file: /usr/share/lnd/data/chain/bitcoin/mainnet/invoices.macaroon # required
  tls_cert_file: /usr/share/lnd/tls.cert      # required
nomad job run ln.hcl

Si vous allez sur l’URL de votre instance : https://ln.VOTREDOMAIN.tld/.well-known/lnurlp/VOUS vous devriez voir un gros json qui contient votre png en base64, exemple https://ln.fredix.xyz/.well-known/lnurlp/fredix

Les banques ?

Comment se positionnent certaines banques ? A priori qui dit banque dit pas copain avec un système décentralisé. Cependant hormis les réactionnaires impossible de faire l’impasse sur l’océan de $ que le bitcoin génère. Je ne vais pas faire un tour des néo-banques mais juste en citer 3 qui font plus ou moins de la crypto.

  • N26 s’est lancé depuis peu. Mais c’est juste pour faire du traiding, achat-revente dans leur plateforme. Il n’est pas possible de recevoir de la crypto et d’en envoyer ce qui limite pas mal l’usage.
  • Revolut alors là c’est la fête du slip. On peut acheter-vendre mais aussi recevoir et envoyer. J’ai acheté un petit montant de BTC, et j’ai pu en envoyer dans mon wallet Zeus, sur mon noeud embedded mais aussi sur mon noeud LND REST 👍
  • deblock un petit dernier qui vient de sortir. Par des anciens de Revolut, en France et se disent non-custodial . Prometteur sur le papier, il y a une liste d’attente :)

asi0’s Bitcoin Guides

A lire