diff --git a/.env.development b/.env.development index 8f86f19f..e4c87983 100644 --- a/.env.development +++ b/.env.development @@ -156,8 +156,9 @@ AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY PERSISTENCE=1 SKIP_SSL_CERT_DOWNLOAD=1 -# tor -TOR_PROXY=http://127.0.0.1:7050/ +# tor proxy +TOR_PROXY=http://tor:7050/ +grpc_proxy=http://tor:7050/ # lnbits LNBITS_WEB_PORT=5001 diff --git a/api/lnd/index.js b/api/lnd/index.js index 3e49ce74..abc39e82 100644 --- a/api/lnd/index.js +++ b/api/lnd/index.js @@ -1,6 +1,7 @@ import { cachedFetcher } from '@/lib/fetch' import { toPositiveNumber } from '@/lib/validate' -import { authenticatedLndGrpc, getIdentity, getHeight, getWalletInfo, getNode } from 'ln-service' +import { authenticatedLndGrpc } from '@/lib/lnd' +import { getIdentity, getHeight, getWalletInfo, getNode } from 'ln-service' const lnd = global.lnd || authenticatedLndGrpc({ cert: process.env.LND_CERT, diff --git a/docker-compose.yml b/docker-compose.yml index baae9a40..ff247705 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -40,6 +40,18 @@ services: labels: CONNECT: "localhost:5431" cpu_shares: "${CPU_SHARES_IMPORTANT}" + tor: + build: + context: ./docker/tor + container_name: tor + restart: unless-stopped + volumes: + - tordata:/tordata/ + cpu_shares: "${CPU_SHARES_LOW}" + env_file: *env_file + healthcheck: + <<: *healthcheck + test: ["CMD-SHELL", "bash /tor.sh check"] app: container_name: app stdin_open: true @@ -359,8 +371,13 @@ services: healthcheck: <<: *healthcheck test: ["CMD-SHELL", "lncli", "getinfo"] - depends_on: *depends_on_bitcoin + depends_on: + tor: + condition: service_healthy + restart: true + <<: *depends_on_bitcoin env_file: *env_file + entrypoint: /tor-entrypoint command: - 'lnd' - '--noseedbackup' @@ -369,6 +386,7 @@ services: - '--externalip=stacker_lnd' - '--tlsextradomain=stacker_lnd' - '--tlsextradomain=host.docker.internal' + - '--tlsextradomain=$${ONION_DOMAIN}' - '--listen=0.0.0.0:9735' - '--rpclisten=0.0.0.0:10009' - '--rpcmiddleware.enable' @@ -394,6 +412,7 @@ services: - "${STACKER_LND_GRPC_PORT}:10009" volumes: - stacker_lnd:/home/lnd/.lnd + - tordata:/home/lnd/.tor labels: ofelia.enabled: "true" ofelia.job-exec.stacker_lnd_channel_cron.schedule: "@every 1m" @@ -452,7 +471,11 @@ services: healthcheck: <<: *healthcheck test: ["CMD-SHELL", "su clightning -c 'lightning-cli --network=regtest getinfo'"] - depends_on: *depends_on_bitcoin + depends_on: + tor: + condition: service_healthy + restart: true + <<: *depends_on_bitcoin env_file: *env_file command: - 'lightningd' @@ -471,6 +494,7 @@ services: - "${STACKER_CLN_REST_PORT}:3010" volumes: - stacker_cln:/home/clightning/.lightning + - tordata:/home/clightning/.tor labels: ofelia.enabled: "true" ofelia.job-exec.stacker_cln_channel_cron.schedule: "@every 1m" @@ -528,8 +552,8 @@ services: condition: service_healthy restart: true volumes: - - ./docker/lnd/stacker:/app/.lnd - nwc_send:/app + - stacker_lnd:/app/.lnd environment: - RUST_LOG=info entrypoint: @@ -537,7 +561,7 @@ services: - '--relay' - 'wss://relay.primal.net' - '--macaroon-file' - - '/app/.lnd/regtest/admin.macaroon' + - '/app/.lnd/data/chain/bitcoin/regtest/admin.macaroon' - '--cert-file' - '/app/.lnd/tls.cert' - '--lnd-host' @@ -562,8 +586,8 @@ services: condition: service_healthy restart: true volumes: - - ./docker/lnd/stacker:/app/.lnd - nwc_recv:/app + - stacker_lnd:/app/.lnd environment: - RUST_LOG=info entrypoint: @@ -571,7 +595,7 @@ services: - '--relay' - 'wss://relay.primal.net' - '--invoice-macaroon-file' - - '/app/.lnd/regtest/invoice.macaroon' + - '/app/.lnd/data/chain/bitcoin/regtest/invoice.macaroon' - '--cert-file' - '/app/.lnd/tls.cert' - '--lnd-host' @@ -595,16 +619,22 @@ services: ports: - "${LNBITS_WEB_PORT}:5000" depends_on: - - stacker_lnd + tor: + condition: service_healthy + restart: true + stacker_lnd: + condition: service_healthy + restart: true environment: - LNBITS_ADMIN_UI=true - LNBITS_BACKEND_WALLET_CLASS=LndWallet - LND_GRPC_ENDPOINT=stacker_lnd - LND_GRPC_PORT=10009 - LND_GRPC_CERT=/app/.lnd/tls.cert - - LND_GRPC_MACAROON=/app/.lnd/regtest/admin.macaroon + - LND_GRPC_MACAROON=/app/.lnd/data/chain/bitcoin/regtest/admin.macaroon volumes: - - ./docker/lnd/stacker:/app/.lnd + - stacker_lnd:/app/.lnd + - tordata:/app/.tor labels: CONNECT: "localhost:${LNBITS_WEB_PORT}" cpu_shares: "${CPU_SHARES_LOW}" @@ -618,3 +648,4 @@ volumes: s3: nwc_send: nwc_recv: + tordata: diff --git a/docker/lnbits/data/database.sqlite3 b/docker/lnbits/data/database.sqlite3 index a8463c7f..fcdf5dda 100644 Binary files a/docker/lnbits/data/database.sqlite3 and b/docker/lnbits/data/database.sqlite3 differ diff --git a/docker/lnd/Dockerfile b/docker/lnd/Dockerfile index 87ffafb6..14c570aa 100644 --- a/docker/lnd/Dockerfile +++ b/docker/lnd/Dockerfile @@ -9,4 +9,7 @@ RUN apt-get update -y \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* COPY ["./$LN_NODE_FOR/regtest/*", "/home/lnd/.lnd/data/chain/bitcoin/regtest/"] -COPY ["./$LN_NODE_FOR/tls.*", "/home/lnd/.lnd/"] \ No newline at end of file +COPY ["./$LN_NODE_FOR/tls.*", "/home/lnd/.lnd/"] + +ADD tor-entrypoint.sh /tor-entrypoint +RUN chmod +x /tor-entrypoint \ No newline at end of file diff --git a/docker/lnd/stacker/tls.cert b/docker/lnd/stacker/tls.cert deleted file mode 100644 index b098192b..00000000 --- a/docker/lnd/stacker/tls.cert +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICRzCCAe2gAwIBAgIQc06vWIBuP9uKeQNHKbFllDAKBggqhkjOPQQDAjA4MR8w -HQYDVQQKExZsbmQgYXV0b2dlbmVyYXRlZCBjZXJ0MRUwEwYDVQQDEww4Y2M4NDFk -MjY2MzgwHhcNMjQwMzA3MTcwMjE5WhcNMjUwNTAyMTcwMjE5WjA4MR8wHQYDVQQK -ExZsbmQgYXV0b2dlbmVyYXRlZCBjZXJ0MRUwEwYDVQQDEww4Y2M4NDFkMjY2Mzgw -WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQT/nwvMHaVCfdVaeIgv8MKS+SHAS9c -Elif7Xqa7qsVvPiW7Vnh4MDVEBlM5rg0nkaH6V17sCC3rse/OqPLfVY1o4HYMIHV -MA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8E -BTADAQH/MB0GA1UdDgQWBBQmamVn/KcRqHoNR9dk9C1g2M+jSTB+BgNVHREEdzB1 -ggw4Y2M4NDFkMjY2MziCCWxvY2FsaG9zdIILc3RhY2tlcl9sbmSCFGhvc3QuZG9j -a2VyLmludGVybmFsggR1bml4ggp1bml4cGFja2V0ggdidWZjb25uhwR/AAABhxAA -AAAAAAAAAAAAAAAAAAABhwSsGwAGMAoGCCqGSM49BAMCA0gAMEUCIFD273WBcMKz -UPoOL8bwq15JXtrSGePKpAeN1TblY4Q5AiEAvKtuk+ssx9WQFZBEiWxCSjW5geKk -6HB7TdxsU+ZbfLg= ------END CERTIFICATE----- diff --git a/docker/lnd/stacker/tls.key b/docker/lnd/stacker/tls.key deleted file mode 100644 index af1cbce6..00000000 --- a/docker/lnd/stacker/tls.key +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN EC PRIVATE KEY----- -MHcCAQEEIOxH9uY8mpnlo/X5gRAAVOzOuEPIAOuHHlezkba3vIuHoAoGCCqGSM49 -AwEHoUQDQgAEE/58LzB2lQn3VWniIL/DCkvkhwEvXBJYn+16mu6rFbz4lu1Z4eDA -1RAZTOa4NJ5Gh+lde7Agt67Hvzqjy31WNQ== ------END EC PRIVATE KEY----- diff --git a/docker/lnd/tor-entrypoint.sh b/docker/lnd/tor-entrypoint.sh new file mode 100644 index 00000000..8d3d834d --- /dev/null +++ b/docker/lnd/tor-entrypoint.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +ONION_DOMAIN="" + +if [ -f /home/lnd/.tor/hidden_service/hostname ]; then + ONION_DOMAIN=$(cat /home/lnd/.tor/hidden_service/hostname) +fi + +# expand the cmd arguments +args=$(echo "$@" | sed -e "s/\${ONION_DOMAIN}/$ONION_DOMAIN/g") + +# Execute the original entry point script with the modified command line`` +/entrypoint.sh $args \ No newline at end of file diff --git a/docker/tor/Dockerfile b/docker/tor/Dockerfile new file mode 100644 index 00000000..13e3094d --- /dev/null +++ b/docker/tor/Dockerfile @@ -0,0 +1,15 @@ +FROM debian:bookworm + +RUN apt-get update -y \ + && apt-get install -y tor bash openssl netcat-traditional \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +ADD torrc /etc/tor/torrc.template +ADD tor.sh /tor.sh +ADD services.conf /services.conf +RUN mkdir -p /tordata && groupadd -g 1000 tor && useradd -u 1000 -g 1000 -m tor && chown -R tor:tor /tordata +EXPOSE 9050 9051 7050 +VOLUME "/tordata" +USER tor +ENTRYPOINT [ "bash", "/tor.sh" ] diff --git a/docker/tor/services.conf b/docker/tor/services.conf new file mode 100644 index 00000000..6da6d0da --- /dev/null +++ b/docker/tor/services.conf @@ -0,0 +1,3 @@ +HiddenServicePort 10009 stacker_lnd:10009 +HiddenServicePort 3010 stacker_cln:3010 +HiddenServicePort 5000 lnbits:5000 diff --git a/docker/tor/tor.sh b/docker/tor/tor.sh new file mode 100644 index 00000000..e866b884 --- /dev/null +++ b/docker/tor/tor.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +function initialize { + ####################### generate and save control password ######################## + cp -f /etc/tor/torrc.template /tordata/torrc + TOR_PASSWORD="" + if [ -f /tordata/.env.torpass ]; then source /tordata/.env.torpass; fi + + if [ -z "$torPassword" ]; then + TOR_PASSWORD=$(openssl rand -hex 32) + echo "TOR_PASSWORD=$TOR_PASSWORD" > /tordata/.env.torpass + fi + + TOR_PASSWORD_HASH=$(tor --hash-password "$TOR_PASSWORD" 2>/dev/null | tail -n 1) + echo "Replacing %HashedControlPassword% with $TOR_PASSWORD_HASH" + sed -i "s|%HashedControlPassword%|$TOR_PASSWORD_HASH|g" /tordata/torrc + ################################################################################## +} + +function mergeServices { + cat /services.conf >> /tordata/torrc +} + +# There is a circular dependency between tor and stacker_lnd: +# <-> tor needs stacker_lnd to be running to resolve the hidden service target +# <-> stacker_lnd needs to wait for tor to start and generate the hidden service address +# Afaik there isn't an "official" solution for this issue. +# +# This workaround starts tor the first time without the lnd hidden service +# and then re-start tor with the full configuration after the lnd service is ready. + + +if [ -f /tordata/start.timestamp ]; +then + # Remove leftovers from a previous run + rm /tordata/start.timestamp +fi + +if [ "$1" = "check" ]; +then + if [ ! -f /tordata/start.timestamp ]; then + # if still initializing we just check if the hidden service was generated and use this as a healthcheck + if [ -f /tordata/hidden_service/hostname ]; then exit 0; else exit 1; fi + else + # run the real healthcheck + echo -e 'AUTHENTICATE "'$TOR_PASSWORD'"\nGETINFO status/circuit-established\nQUIT' | nc 127.0.0.1 9051 | grep OK || exit 1 + exit 0 + fi +else + # Step 1: we start tor with a fake hidden service that points to port 8080, + # just to get it to generate the hidden service data, then we kill it immediately after + echo "Initializing..." + initialize + tor -f /tordata/torrc & + pid=$! + sleep 60 + kill $pid + + # debug + ls /tordata/hidden_service/ + + # Step 2: we merge the service configuration and start tor again + echo "Starting tor..." + initialize + mergeServices + date +%s > /tordata/start.timestamp + tor -f /tordata/torrc +fi diff --git a/docker/tor/torrc b/docker/tor/torrc new file mode 100644 index 00000000..bfac6649 --- /dev/null +++ b/docker/tor/torrc @@ -0,0 +1,260 @@ +## Configuration file for a typical Tor user +## Last updated 28 February 2019 for Tor 0.3.5.1-alpha. +## (may or may not work for much older or much newer versions of Tor.) +## +## Lines that begin with "## " try to explain what's going on. Lines +## that begin with just "#" are disabled commands: you can enable them +## by removing the "#" symbol. +## +## See 'man tor', or https://www.torproject.org/docs/tor-manual.html, +## for more options you can use in this file. +## +## Tor will look for this file in various places based on your platform: +## https://support.torproject.org/tbb/tbb-editing-torrc/ + +## Tor opens a SOCKS proxy on port 9050 by default -- even if you don't +## configure one below. Set "SOCKSPort 0" if you plan to run Tor only +## as a relay, and not make any local application connections yourself. +SOCKSPort 0.0.0.0:9050 # Default: Bind to localhost:9050 for local connections. +#SOCKSPort 192.168.0.1:9100 # Bind to this address:port too. +HTTPTunnelPort 0.0.0.0:7050 +## Entry policies to allow/deny SOCKS requests based on IP address. +## First entry that matches wins. If no SOCKSPolicy is set, we accept +## all (and only) requests that reach a SOCKSPort. Untrusted users who +## can access your SOCKSPort may be able to learn about the connections +## you make. +#SOCKSPolicy accept 192.168.0.0/16 +#SOCKSPolicy accept6 FC00::/7 +SOCKSPolicy accept * + +## Logs go to stdout at level "notice" unless redirected by something +## else, like one of the below lines. You can have as many Log lines as +## you want. +## +## We advise using "notice" in most cases, since anything more verbose +## may provide sensitive information to an attacker who obtains the logs. +## +## Send all messages of level 'notice' or higher to @LOCALSTATEDIR@/log/tor/notices.log +#Log notice file @LOCALSTATEDIR@/log/tor/notices.log +## Send every possible message to @LOCALSTATEDIR@/log/tor/debug.log +#Log debug file @LOCALSTATEDIR@/log/tor/debug.log +## Use the system log instead of Tor's logfiles +Log notice stdout +## To send all messages to stderr: +#Log debug stderr + +## Uncomment this to start the process in the background... or use +## --runasdaemon 1 on the command line. This is ignored on Windows; +## see the FAQ entry if you want Tor to run as an NT service. +#RunAsDaemon 1 + +## The directory for keeping all the keys/etc. By default, we store +## things in $HOME/.tor on Unix, and in Application Data\tor on Windows. +#DataDirectory @LOCALSTATEDIR@/lib/tor + +## The port on which Tor will listen for local connections from Tor +## controller applications, as documented in control-spec.txt. +ControlPort 127.0.0.1:9051 +## If you enable the controlport, be sure to enable one of these +## authentication methods, to prevent attackers from accessing it. +HashedControlPassword %HashedControlPassword% +#CookieAuthentication 1 + +############### This section is just for location-hidden services ### + +## Once you have configured a hidden service, you can look at the +## contents of the file ".../hidden_service/hostname" for the address +## to tell people. +## +## HiddenServicePort x y:z says to redirect requests on port x to the +## address y:z. + +#HiddenServiceDir @LOCALSTATEDIR@/lib/tor/hidden_service/ +#HiddenServicePort 80 127.0.0.1:80 + +HiddenServiceDir /tordata/hidden_service/ +# Fake service just to initialize the hidden service directory +HiddenServicePort 8080 127.0.0.1:8080 + + + + +################ This section is just for relays ##################### +# +## See https://community.torproject.org/relay for details. + +## Required: what port to advertise for incoming Tor connections. +#ORPort 9001 +## If you want to listen on a port other than the one advertised in +## ORPort (e.g. to advertise 443 but bind to 9090), you can do it as +## follows. You'll need to do ipchains or other port forwarding +## yourself to make this work. +#ORPort 443 NoListen +#ORPort 127.0.0.1:9090 NoAdvertise +## If you want to listen on IPv6 your numeric address must be explicitly +## between square brackets as follows. You must also listen on IPv4. +#ORPort [2001:DB8::1]:9050 + +## The IP address or full DNS name for incoming connections to your +## relay. Leave commented out and Tor will guess. +#Address noname.example.com + +## If you have multiple network interfaces, you can specify one for +## outgoing traffic to use. +## OutboundBindAddressExit will be used for all exit traffic, while +## OutboundBindAddressOR will be used for all OR and Dir connections +## (DNS connections ignore OutboundBindAddress). +## If you do not wish to differentiate, use OutboundBindAddress to +## specify the same address for both in a single line. +#OutboundBindAddressExit 10.0.0.4 +#OutboundBindAddressOR 10.0.0.5 + +## A handle for your relay, so people don't have to refer to it by key. +## Nicknames must be between 1 and 19 characters inclusive, and must +## contain only the characters [a-zA-Z0-9]. +## If not set, "Unnamed" will be used. +#Nickname ididnteditheconfig + +## Define these to limit how much relayed traffic you will allow. Your +## own traffic is still unthrottled. Note that RelayBandwidthRate must +## be at least 75 kilobytes per second. +## Note that units for these config options are bytes (per second), not +## bits (per second), and that prefixes are binary prefixes, i.e. 2^10, +## 2^20, etc. +#RelayBandwidthRate 100 KBytes # Throttle traffic to 100KB/s (800Kbps) +#RelayBandwidthBurst 200 KBytes # But allow bursts up to 200KB (1600Kb) + +## Use these to restrict the maximum traffic per day, week, or month. +## Note that this threshold applies separately to sent and received bytes, +## not to their sum: setting "40 GB" may allow up to 80 GB total before +## hibernating. +## +## Set a maximum of 40 gigabytes each way per period. +#AccountingMax 40 GBytes +## Each period starts daily at midnight (AccountingMax is per day) +#AccountingStart day 00:00 +## Each period starts on the 3rd of the month at 15:00 (AccountingMax +## is per month) +#AccountingStart month 3 15:00 + +## Administrative contact information for this relay or bridge. This line +## can be used to contact you if your relay or bridge is misconfigured or +## something else goes wrong. Note that we archive and publish all +## descriptors containing these lines and that Google indexes them, so +## spammers might also collect them. You may want to obscure the fact that +## it's an email address and/or generate a new address for this purpose. +## +## If you are running multiple relays, you MUST set this option. +## +#ContactInfo Random Person +## You might also include your PGP or GPG fingerprint if you have one: +#ContactInfo 0xFFFFFFFF Random Person + +## Uncomment this to mirror directory information for others. Please do +## if you have enough bandwidth. +#DirPort 9030 # what port to advertise for directory connections +## If you want to listen on a port other than the one advertised in +## DirPort (e.g. to advertise 80 but bind to 9091), you can do it as +## follows. below too. You'll need to do ipchains or other port +## forwarding yourself to make this work. +#DirPort 80 NoListen +#DirPort 127.0.0.1:9091 NoAdvertise +## Uncomment to return an arbitrary blob of html on your DirPort. Now you +## can explain what Tor is if anybody wonders why your IP address is +## contacting them. See contrib/tor-exit-notice.html in Tor's source +## distribution for a sample. +#DirPortFrontPage @CONFDIR@/tor-exit-notice.html + +## Uncomment this if you run more than one Tor relay, and add the identity +## key fingerprint of each Tor relay you control, even if they're on +## different networks. You declare it here so Tor clients can avoid +## using more than one of your relays in a single circuit. See +## https://support.torproject.org/relay-operators/multiple-relays/ +## However, you should never include a bridge's fingerprint here, as it would +## break its concealability and potentially reveal its IP/TCP address. +## +## If you are running multiple relays, you MUST set this option. +## +## Note: do not use MyFamily on bridge relays. +#MyFamily $keyid,$keyid,... + +## Uncomment this if you want your relay to be an exit, with the default +## exit policy (or whatever exit policy you set below). +## (If ReducedExitPolicy, ExitPolicy, or IPv6Exit are set, relays are exits. +## If none of these options are set, relays are non-exits.) +#ExitRelay 1 + +## Uncomment this if you want your relay to allow IPv6 exit traffic. +## (Relays do not allow any exit traffic by default.) +#IPv6Exit 1 + +## Uncomment this if you want your relay to be an exit, with a reduced set +## of exit ports. +#ReducedExitPolicy 1 + +## Uncomment these lines if you want your relay to be an exit, with the +## specified set of exit IPs and ports. +## +## A comma-separated list of exit policies. They're considered first +## to last, and the first match wins. +## +## If you want to allow the same ports on IPv4 and IPv6, write your rules +## using accept/reject *. If you want to allow different ports on IPv4 and +## IPv6, write your IPv6 rules using accept6/reject6 *6, and your IPv4 rules +## using accept/reject *4. +## +## If you want to _replace_ the default exit policy, end this with either a +## reject *:* or an accept *:*. Otherwise, you're _augmenting_ (prepending to) +## the default exit policy. Leave commented to just use the default, which is +## described in the man page or at +## https://support.torproject.org/relay-operators +## +## Look at https://support.torproject.org/abuse/exit-relay-expectations/ +## for issues you might encounter if you use the default exit policy. +## +## If certain IPs and ports are blocked externally, e.g. by your firewall, +## you should update your exit policy to reflect this -- otherwise Tor +## users will be told that those destinations are down. +## +## For security, by default Tor rejects connections to private (local) +## networks, including to the configured primary public IPv4 and IPv6 addresses, +## and any public IPv4 and IPv6 addresses on any interface on the relay. +## See the man page entry for ExitPolicyRejectPrivate if you want to allow +## "exit enclaving". +## +#ExitPolicy accept *:6660-6667,reject *:* # allow irc ports on IPv4 and IPv6 but no more +#ExitPolicy accept *:119 # accept nntp ports on IPv4 and IPv6 as well as default exit policy +#ExitPolicy accept *4:119 # accept nntp ports on IPv4 only as well as default exit policy +#ExitPolicy accept6 *6:119 # accept nntp ports on IPv6 only as well as default exit policy +#ExitPolicy reject *:* # no exits allowed + +## Uncomment this if you want your exit relay to reevaluate its exit policy on +## existing connections when the exit policy is modified. +#ReevaluateExitPolicy 1 + +## Bridge relays (or "bridges") are Tor relays that aren't listed in the +## main directory. Since there is no complete public list of them, even an +## ISP that filters connections to all the known Tor relays probably +## won't be able to block all the bridges. Also, websites won't treat you +## differently because they won't know you're running Tor. If you can +## be a real relay, please do; but if not, be a bridge! +## +## Warning: when running your Tor as a bridge, make sure than MyFamily is +## NOT configured. +#BridgeRelay 1 +## By default, Tor will advertise your bridge to users through various +## mechanisms like https://bridges.torproject.org/. If you want to run +## a private bridge, for example because you'll give out your bridge +## address manually to your friends, uncomment this line: +#BridgeDistribution none + +## Configuration options can be imported from files or folders using the %include +## option with the value being a path. This path can have wildcards. Wildcards are +## expanded first, using lexical order. Then, for each matching file or folder, the following +## rules are followed: if the path is a file, the options from the file will be parsed as if +## they were written where the %include option is. If the path is a folder, all files on that +## folder will be parsed following lexical order. Files starting with a dot are ignored. Files +## on subfolders are ignored. +## The %include option can be used recursively. +#%include /etc/torrc.d/*.conf + diff --git a/lib/lnd.js b/lib/lnd.js new file mode 100644 index 00000000..b9d37954 --- /dev/null +++ b/lib/lnd.js @@ -0,0 +1,52 @@ +// fork of https://github.com/alexbosworth/lightning/blob/master/lnd_grpc/authenticated_lnd_grpc.js +// that allows to enable or disable proxy + +import { join } from 'path' +import apiForProto from 'lightning/lnd_grpc/api_for_proto' +import { defaultSocket, grpcSslCipherSuites, packageTypes, protoFiles, protosDir, serviceTypes } from 'lightning/grpc/index' +import grpcCredentials from 'lightning/lnd_grpc/grpc_credentials' +import { createRequire } from 'module' + +const { GRPC_SSL_CIPHER_SUITES } = process.env +const { keys } = Object + +export function authenticatedLndGrpc ({ cert, macaroon, path, socket }, withProxy) { + const req = createRequire(import.meta.url) + + const lightningModulePath = req.resolve('lightning') + const pathForProto = proto => join(lightningModulePath, protosDir, proto) + + const { credentials } = grpcCredentials({ cert, macaroon }) + const lndSocket = socket || defaultSocket + + if (!!cert && GRPC_SSL_CIPHER_SUITES !== grpcSslCipherSuites) { + process.env.GRPC_SSL_CIPHER_SUITES = grpcSslCipherSuites + } + + const params = { + 'grpc.max_receive_message_length': -1, + 'grpc.max_send_message_length': -1, + 'grpc.enable_http_proxy': withProxy ? 1 : 0 + } + + // Assemble different services from their proto files + return { + lnd: keys(serviceTypes).reduce((services, type) => { + const service = serviceTypes[type] + + const file = protoFiles[service] + + services[type] = apiForProto({ + credentials, + params, + service, + path: path ? join(path, file) : pathForProto(file), + socket: lndSocket, + type: packageTypes[service] + }) + + return services + }, + {}) + } +} diff --git a/sndev b/sndev index 39672edc..eff1dcbc 100755 --- a/sndev +++ b/sndev @@ -56,12 +56,19 @@ docker__stacker_lnd() { docker__stacker_cln() { t=$1 + if [ "$t" = "-t" ]; then shift else t="" fi + if [ "$1" = "get_onion" ]; then + onion="$(docker__exec $t stacker_cln cat /home/clightning/.tor/hidden_service/hostname | tr -d '[:space:]')" + echo "$onion:3010" + exit 0 + fi + docker__exec $t -u clightning stacker_cln lightning-cli --regtest "$@" } @@ -414,6 +421,9 @@ sndev__stacker_clncli() { sndev__help_stacker_clncli() { docker__stacker_cln help + echo + echo "EXTRA:" + echo " get_onion get the onion address" } sndev__stacker_litcli() { @@ -524,6 +534,80 @@ USAGE echo "$help" } +sndev__help_stacker_lnd() { + help=" + +USAGE + $ sndev stacker_lnd get_cert get the tls cert + $ sndev stacker_lnd get_onion get the onion address +" + + echo "$help" +} + +sndev__stacker_lnd() { + shift + if [ -z "$1" ]; then + echo "no command provided" + sndev__help_stacker_lnd + exit 1 + fi + if [ "$1" = "get_cert" ]; then + echo $(docker__exec -t stacker_lnd openssl base64 -A -in /home/lnd/.lnd/tls.cert) + elif [ "$1" = "get_onion" ]; + then + onion="$(docker__exec -t stacker_lnd cat /home/lnd/.tor/hidden_service/hostname | tr -d '[:space:]')" + echo "$onion:10009" + fi +} + +sndev__help_lnbits() { + help=" + +USAGE + $ sndev stacker_lnbits get_onion get the onion address +" +} + +sndev__lnbits() { + shift + if [ -z "$1" ]; then + echo "no command provided" + sndev__help_lnbits + exit 1 + fi + if [ "$1" = "get_onion" ]; + then + onion="$(docker__exec -t lnbits cat /app/.tor/hidden_service/hostname | tr -d '[:space:]')" + echo "$onion:5000" + fi +} + + +sndev__help_tor() { + help=" + +USAGE + $ sndev tor get_onion get the onion address +" + + echo "$help" +} + +sndev__tor() { + shift + if [ -z "$1" ]; then + echo "no command provided" + sndev__help_tor + exit 1 + fi + if [ "$1" = "get_onion" ]; + then + onion="$(docker__exec -t stacker_lnd cat /home/lnd/.tor/hidden_service/hostname | tr -d '[:space:]')" + echo "$onion" + fi +} + sndev__help() { if [ $# -eq 2 ]; then call "sndev__$1_$2" "$@" @@ -547,43 +631,48 @@ USAGE $ sndev help [COMMAND] COMMANDS - help show help + help show help env: - start start env - stop stop env - restart restart env - status status of env - logs logs from env - delete delete env + start start env + stop stop env + restart restart env + status status of env + logs logs from env + delete delete env sn: - login login as a nym - fund_user fund a nym without using an LN invoice + login login as a nym + fund_user fund a nym without using an LN invoice lnd: - fund pay a bolt11 for funding - withdraw create a bolt11 for withdrawal + fund pay a bolt11 for funding + withdraw create a bolt11 for withdrawal cln: - cln_fund pay a bolt11 for funding with CLN - cln_withdraw create a bolt11 for withdrawal with CLN + cln_fund pay a bolt11 for funding with CLN + cln_withdraw create a bolt11 for withdrawal with CLN db: - psql open psql on db - prisma run prisma commands + psql open psql on db + prisma run prisma commands dev: - pr fetch and checkout a pr - lint run linters - open open container url in browser + pr fetch and checkout a pr + lint run linters + open open container url in browser other: - compose docker compose passthrough - sn_lndcli lncli passthrough on sn_lnd - stacker_lndcli lncli passthrough on stacker_lnd - stacker_clncli lightning-cli passthrough on stacker_cln - stacker_litcli litcli passthrough on litd + compose docker compose passthrough + sn_lndcli lncli passthrough on sn_lnd + stacker_lndcli lncli passthrough on stacker_lnd + stacker_clncli lightning-cli passthrough on stacker_cln + stacker_litcli litcli passthrough on litd + tor get_onion get the onion address + stacker_lnd get_cert get the tls cert + stacker_lnd get_onion get the onion address + stacker_clncli get_onion get the onion address + lndbits get_onion get the onion address " echo "$help" } diff --git a/wallets/cln/ATTACH.md b/wallets/cln/ATTACH.md index 6d282628..4093f1b4 100644 --- a/wallets/cln/ATTACH.md +++ b/wallets/cln/ATTACH.md @@ -4,6 +4,14 @@ For testing cln as an attached receiving wallet, you'll need a rune and the cert `stacker_cln:3010` +# host and port (onion) + +Run: + +```bash +sndev stacker_clncli get_onion +``` + # create rune ```bash diff --git a/wallets/lnbits/ATTACH.md b/wallets/lnbits/ATTACH.md index baafa1bf..aced9a41 100644 --- a/wallets/lnbits/ATTACH.md +++ b/wallets/lnbits/ATTACH.md @@ -22,3 +22,12 @@ Or simply copy the keys from here: ( These keys can be found under `Node URL, API keys and API docs`. ) To use the same URL to connect to LNbits in the browser and server during local development, `localhost:` is mapped to `lnbits:5000` on the server. + + +# tor onion url + +Run the following command to get the onion url: + +```bash +sndev lnbits get_onion +``` \ No newline at end of file diff --git a/wallets/lnbits/server.js b/wallets/lnbits/server.js index 768990db..1d200aa2 100644 --- a/wallets/lnbits/server.js +++ b/wallets/lnbits/server.js @@ -1,5 +1,6 @@ import { msatsToSats } from '@/lib/format' import { getAgent } from '@/lib/proxy' +import fetch from 'cross-fetch' export * from 'wallets/lnbits' diff --git a/wallets/lnd/ATTACH.md b/wallets/lnd/ATTACH.md index 1d508c3c..e61c2e90 100644 --- a/wallets/lnd/ATTACH.md +++ b/wallets/lnd/ATTACH.md @@ -4,6 +4,14 @@ For testing lnd as an attached receiving wallet, you'll need a macaroon and the `stacker_lnd:10009` +## host and port (onion) + +To get the onion address run this command: + +```bash +sndev stacker_lnd get_onion +``` + # generate macaroon ```bash @@ -12,14 +20,8 @@ sndev stacker_lndcli -n regtest bakemacaroon invoices:write invoices:read # get cert -This is static in dev env so you can use this one: +To get the cert run this command: ```bash -LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNSekNDQWUyZ0F3SUJBZ0lRYzA2dldJQnVQOXVLZVFOSEtiRmxsREFLQmdncWhrak9QUVFEQWpBNE1SOHcKSFFZRFZRUUtFeFpzYm1RZ1lYVjBiMmRsYm1WeVlYUmxaQ0JqWlhKME1SVXdFd1lEVlFRREV3dzRZMk00TkRGawpNalkyTXpnd0hoY05NalF3TXpBM01UY3dNakU1V2hjTk1qVXdOVEF5TVRjd01qRTVXakE0TVI4d0hRWURWUVFLCkV4WnNibVFnWVhWMGIyZGxibVZ5WVhSbFpDQmpaWEowTVJVd0V3WURWUVFERXd3NFkyTTROREZrTWpZMk16Z3cKV1RBVEJnY3Foa2pPUFFJQkJnZ3Foa2pPUFFNQkJ3TkNBQVFUL253dk1IYVZDZmRWYWVJZ3Y4TUtTK1NIQVM5YwpFbGlmN1hxYTdxc1Z2UGlXN1ZuaDRNRFZFQmxNNXJnMG5rYUg2VjE3c0NDM3JzZS9PcVBMZlZZMW80SFlNSUhWCk1BNEdBMVVkRHdFQi93UUVBd0lDcERBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREFUQVBCZ05WSFJNQkFmOEUKQlRBREFRSC9NQjBHQTFVZERnUVdCQlFtYW1Wbi9LY1JxSG9OUjlkazlDMWcyTStqU1RCK0JnTlZIUkVFZHpCMQpnZ3c0WTJNNE5ERmtNalkyTXppQ0NXeHZZMkZzYUc5emRJSUxjM1JoWTJ0bGNsOXNibVNDRkdodmMzUXVaRzlqCmEyVnlMbWx1ZEdWeWJtRnNnZ1IxYm1sNGdncDFibWw0Y0dGamEyVjBnZ2RpZFdaamIyNXVod1IvQUFBQmh4QUEKQUFBQUFBQUFBQUFBQUFBQUFBQUJod1NzR3dBR01Bb0dDQ3FHU000OUJBTUNBMGdBTUVVQ0lGRDI3M1dCY01LegpVUG9PTDhid3ExNUpYdHJTR2VQS3BBZU4xVGJsWTRRNUFpRUF2S3R1aytzc3g5V1FGWkJFaVd4Q1NqVzVnZUtrCjZIQjdUZHhzVStaYmZMZz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= -``` - -Which is generated with the following command - -```bash -openssl base64 -A -in docker/lnd/stacker/tls.cert +sndev stacker_lnd get_cert ``` \ No newline at end of file diff --git a/wallets/lnd/server.js b/wallets/lnd/server.js index 5cdffb88..8e260984 100644 --- a/wallets/lnd/server.js +++ b/wallets/lnd/server.js @@ -1,5 +1,7 @@ import { datePivot } from '@/lib/time' -import { authenticatedLndGrpc, createInvoice as lndCreateInvoice } from 'ln-service' +import { authenticatedLndGrpc } from '@/lib/lnd' +import { createInvoice as lndCreateInvoice } from 'ln-service' +import { TOR_REGEXP } from '@/lib/url' export * from 'wallets/lnd' @@ -12,11 +14,13 @@ export const createInvoice = async ( { cert, macaroon, socket } ) => { try { + const isOnion = TOR_REGEXP.test(socket) + const { lnd } = await authenticatedLndGrpc({ cert, macaroon, socket - }) + }, isOnion) const invoice = await lndCreateInvoice({ lnd, diff --git a/worker/index.js b/worker/index.js index 83d166ed..3b12e962 100644 --- a/worker/index.js +++ b/worker/index.js @@ -16,7 +16,7 @@ import { timestampItem } from './ots.js' import { computeStreaks, checkStreak } from './streak.js' import { nip57 } from './nostr.js' import fetch from 'cross-fetch' -import { authenticatedLndGrpc } from 'ln-service' +import { authenticatedLndGrpc } from '@/lib/lnd' import { views, rankViews } from './views.js' import { imgproxy } from './imgproxy.js' import { deleteItem } from './ephemeralItems.js'