From 51cb4a2e9a747fb643b414661aa42962bb3d0f6b Mon Sep 17 00:00:00 2001 From: kexkey Date: Thu, 5 Dec 2019 21:17:27 -0500 Subject: [PATCH] TOR keys and hostname not created during setup --- cyphernodeconf_docker/lib/app.js | 9 +- cyphernodeconf_docker/lib/torgen.js | 47 ++++++ cyphernodeconf_docker/prompters/040_tor.js | 42 ++--- .../schema/config-v0.2.3.json | 2 +- .../templates/lightning/c-lightning/config | 11 +- cyphernodeconf_docker/templates/tor/torrc | 2 +- cyphernodeconf_docker/torgen/torgen.go | 152 +++++++++--------- dist/setup.sh | 21 +-- proxy_docker/app/script/requesthandler.sh | 7 +- 9 files changed, 162 insertions(+), 131 deletions(-) create mode 100644 cyphernodeconf_docker/lib/torgen.js diff --git a/cyphernodeconf_docker/lib/app.js b/cyphernodeconf_docker/lib/app.js index fd9fe4c..54920da 100644 --- a/cyphernodeconf_docker/lib/app.js +++ b/cyphernodeconf_docker/lib/app.js @@ -14,6 +14,7 @@ const ejsRenderFileAsync = promisify( ejs.renderFile ).bind( ejs ); const html2ansi = require('./html2ansi.js'); const name = require('./name.js'); const Archive = require('./archive.js'); +const TorGen = require('./torgen.js'); const ApiKey = require('./apikey.js'); const Cert = require('./cert.js'); const htpasswd = require( './htpasswd.js'); @@ -271,6 +272,12 @@ module.exports = class App { async processProps() { + // TOR... + if( this.isChecked( 'features', 'tor' ) ) { + const torgen = new TorGen(this.destinationPath( path.join( destinationDirName, 'tor/hidden_service' ) )); + this.sessionData.tor_hostname = await torgen.generateTorFiles(); + } + // creates keys if they don't exist or we say so. if( this.config.data.gatekeeper_recreatekeys || this.config.data.gatekeeper_keys.configEntries.length===0 ) { @@ -479,7 +486,7 @@ module.exports = class App { networks: ['cyphernodenet', 'cyphernodeappsnet'], docker: "cyphernode/tor:" + this.config.docker_versions['cyphernode/tor'], extra: { - hostname: 'tor_hostname_placeholder', + hostname: this.sessionData.tor_hostname, } }, otsclient: { diff --git a/cyphernodeconf_docker/lib/torgen.js b/cyphernodeconf_docker/lib/torgen.js new file mode 100644 index 0000000..512f87d --- /dev/null +++ b/cyphernodeconf_docker/lib/torgen.js @@ -0,0 +1,47 @@ +const spawn = require('child_process').spawn; +const stringio = require('@rauschma/stringio'); +const fs = require('fs'); +const chalk = require('chalk'); + +module.exports = class TorGen { + + constructor( path ) { + this.path = path || './' + } + + async generateTorFiles() { +// console.log(chalk.green( 'TOR datapath=' + this.path )); + + if( !fs.existsSync(this.path) ) { + console.log(chalk.green( 'Creating TOR Hidden Service directory...' )); + fs.mkdirSync(this.path, { recursive: true }); + } + + if( !fs.existsSync(this.path + '/hostname') ) { + + console.log(chalk.bold.green( 'Generating TOR Hidden Service secret key, public key and hostname...' )); + + const torgenbin = spawn('./torgen/torgen', [this.path]); + try { + await stringio.onExit( torgenbin ); + } catch( err ) { + console.log(chalk.bold.red('Error: ' + err) ); + return ""; + } + +// console.log(chalk.bold.green( 'Generated TOR Hidden Service secret key, public key and hostname.' )); + + } else { + console.log(chalk.red('TOR config files already exist, skipping generation') ); + } + + try { + var data = fs.readFileSync(this.path + '/hostname', 'utf8'); + return data.slice(0, -1); + } catch (err) { + console.log(chalk.bold.red('Error: ' + err) ); + return ""; + } + + } +} diff --git a/cyphernodeconf_docker/prompters/040_tor.js b/cyphernodeconf_docker/prompters/040_tor.js index 198f440..41c5fdf 100644 --- a/cyphernodeconf_docker/prompters/040_tor.js +++ b/cyphernodeconf_docker/prompters/040_tor.js @@ -27,22 +27,22 @@ module.exports = { name: 'torifyables', message: prefix()+'What features do you want to TORify?'+utils.getHelp('torifyables'), choices: utils.torifyableChoices() + }, + { + when: featureCondition, + type: 'checkbox', + name: 'clearnet', + default: utils.getDefault( 'clearnet' ), + message: prefix()+'What features do you want to allow using clearnet?'+utils.getHelp('clearnet'), + choices: [{ + name: "Bitcoin Node", + value: "clearnet_bitcoinnode" + },{ + name: "LN Node", + value: "clearnet_lnnode" + }] }]; }, - { - when: featureCondition, - type: 'checkbox', - name: 'clearnet', - default: utils.getDefault( 'clearnet' ), - message: prefix()+'What features do you want to allow using clearnet?'+utils.getHelp('clearnet'), - choices: [{ - name: "Bitcoin Node", - value: "clearnet_bitcoinnode" - },{ - name: "LN Node", - value: "clearnet_lnnode" - }] - }, templates: function( props ) { return [ 'torrc' ]; } @@ -67,11 +67,11 @@ module.exports = { // - Address Watches Callbacks (webhooks) // - TXID Watches Callbacks (webhooks) -Certain services can also use clearnet. What do you want to allow to use clearnet? -- Bitcoin Node -- LN Node - -Do you want to announce your LN node onion address and/or IP address? - -What is your public IP address? +// Certain services can also use clearnet. What do you want to allow to use clearnet? +// - Bitcoin Node +// - LN Node +// +// Do you want to announce your LN node onion address and/or IP address? +// +// What is your public IP address? diff --git a/cyphernodeconf_docker/schema/config-v0.2.3.json b/cyphernodeconf_docker/schema/config-v0.2.3.json index 16622a4..c4f9c1b 100644 --- a/cyphernodeconf_docker/schema/config-v0.2.3.json +++ b/cyphernodeconf_docker/schema/config-v0.2.3.json @@ -99,7 +99,7 @@ }, "then": { "required": [ - "tor_datapath" + "tor_datapath", "torifyables", "clearnet" ] diff --git a/cyphernodeconf_docker/templates/lightning/c-lightning/config b/cyphernodeconf_docker/templates/lightning/c-lightning/config index e4df753..f6bce82 100644 --- a/cyphernodeconf_docker/templates/lightning/c-lightning/config +++ b/cyphernodeconf_docker/templates/lightning/c-lightning/config @@ -16,10 +16,10 @@ rgb=<%= lightning_nodecolor %> addr=0.0.0.0:9735 -<% if ( features.indexOf('tor_lnnode') !== -1 ) { %> +<% if ( torifyables.indexOf('tor_lnnode') !== -1 ) { %> #tor proxy=tor:9050 -<% if ( features.indexOf('clearnet_lnnode') == -1 ) { %> +<% if ( clearnet.indexOf('clearnet_lnnode') == -1 ) { %> always-use-proxy=true <% } %> #disable-dns @@ -29,10 +29,11 @@ announce-addr=<%= locals.lightning_external_ip %>:9735 <% } %> <% } %> <% if (lightning_announce) { %> - <% if ( features.indexOf('tor_lnnode') !== -1 ) { %> + <% if ( torifyables.indexOf('tor_lnnode') !== -1 ) { %> announce-addr=<%= locals.lightning_external_ip %>:1234 - <% if ( features.indexOf('clearnet_lnnode') == -1 ) { %> - announce-addr=onionaddress:9735 + <% if ( clearnet.indexOf('clearnet_lnnode') == -1 ) { %> + announce-addr=<%= locals.tor_hostname %>:9735 + <% } %> <% } %> <% } %> diff --git a/cyphernodeconf_docker/templates/tor/torrc b/cyphernodeconf_docker/templates/tor/torrc index 18a09eb..f218bf5 100644 --- a/cyphernodeconf_docker/templates/tor/torrc +++ b/cyphernodeconf_docker/templates/tor/torrc @@ -1,4 +1,4 @@ -<% if ( features.indexOf('tor_hiddenservice') !== -1 ) { %> +<% if ( torifyables.indexOf('tor_hiddenservice') !== -1 ) { %> HiddenServiceDir /tor/hidden_service/ HiddenServiceVersion 3 HiddenServicePort 80 traefik:80 diff --git a/cyphernodeconf_docker/torgen/torgen.go b/cyphernodeconf_docker/torgen/torgen.go index 8eab44a..6a3bd6b 100644 --- a/cyphernodeconf_docker/torgen/torgen.go +++ b/cyphernodeconf_docker/torgen/torgen.go @@ -25,101 +25,101 @@ package main import ( - "bytes" - "encoding/base32" - "fmt" - "os" - "path" - "strings" + "bytes" + "encoding/base32" + "fmt" + "os" + "path" + "strings" - "crypto/ed25519" - "crypto/sha512" + "crypto/ed25519" + "crypto/sha512" - "golang.org/x/crypto/sha3" + "golang.org/x/crypto/sha3" ) func main() { - path := path.Clean(os.Args[1]) - fmt.Println("path=" + path) + path := path.Clean(os.Args[1]) + fmt.Println("path=" + path) - /** - About the key files format: https://gitweb.torproject.org/tor.git/tree/src/lib/crypt_ops/crypto_format.c?h=tor-0.4.1.6#n34 + /** + About the key files format: https://gitweb.torproject.org/tor.git/tree/src/lib/crypt_ops/crypto_format.c?h=tor-0.4.1.6#n34 - Write the datalen bytes from data to the file named - fname in the tagged-data format. This format contains a - 32-byte header, followed by the data itself. The header is the - NUL-padded string "== typestring: tag ==". The length - of typestring and tag must therefore be no more than - 24. + Write the datalen bytes from data to the file named + fname in the tagged-data format. This format contains a + 32-byte header, followed by the data itself. The header is the + NUL-padded string "== typestring: tag ==". The length + of typestring and tag must therefore be no more than + 24. - About the secret key format: https://gitweb.torproject.org/tor.git/tree/src/lib/crypt_ops/crypto_ed25519.h?h=tor-0.4.1.6#n29 + About the secret key format: https://gitweb.torproject.org/tor.git/tree/src/lib/crypt_ops/crypto_ed25519.h?h=tor-0.4.1.6#n29 - Note that we store secret keys in an expanded format that doesn't match - the format from standard ed25519. Ed25519 stores a 32-byte value k and - expands it into a 64-byte H(k), using the first 32 bytes for a multiplier - of the base point, and second 32 bytes as an input to a hash function - for deriving r. But because we implement key blinding, we need to store - keys in the 64-byte expanded form. - **/ + Note that we store secret keys in an expanded format that doesn't match + the format from standard ed25519. Ed25519 stores a 32-byte value k and + expands it into a 64-byte H(k), using the first 32 bytes for a multiplier + of the base point, and second 32 bytes as an input to a hash function + for deriving r. But because we implement key blinding, we need to store + keys in the 64-byte expanded form. + **/ - // Key pair generation - fmt.Println("Generating ed25519 keys...") - publicKey, privateKey, _ := ed25519.GenerateKey(nil) + // Key pair generation + fmt.Println("Generating ed25519 keys...") + publicKey, privateKey, _ := ed25519.GenerateKey(nil) - // Convert seed to expanded private key... - // Ref.: https://gitweb.torproject.org/tor.git/tree/src/ext/ed25519/donna/ed25519_tor.c?h=tor-0.4.1.6#n61 - // Ref.: https://gitweb.torproject.org/tor.git/tree/src/ext/curve25519_donna/README?h=tor-0.4.1.6#n28 - fmt.Println("Converting keys for TOR...") - h := sha512.Sum512(privateKey[:32]) - h[0] &= 248 - h[31] &= 127 - h[31] |= 64 + // Convert seed to expanded private key... + // Ref.: https://gitweb.torproject.org/tor.git/tree/src/ext/ed25519/donna/ed25519_tor.c?h=tor-0.4.1.6#n61 + // Ref.: https://gitweb.torproject.org/tor.git/tree/src/ext/curve25519_donna/README?h=tor-0.4.1.6#n28 + fmt.Println("Converting keys for TOR...") + h := sha512.Sum512(privateKey[:32]) + h[0] &= 248 + h[31] &= 127 + h[31] |= 64 - // Create the Tor Hidden Service private key file - fmt.Println("Creating secret file...") - var fileBytes bytes.Buffer - fileBytes.Write([]byte("== ed25519v1-secret: type0 ==")) - fileBytes.Write(bytes.Repeat([]byte{0x00}, 3)) - fileBytes.Write(h[:]) + // Create the Tor Hidden Service private key file + fmt.Println("Creating secret file...") + var fileBytes bytes.Buffer + fileBytes.Write([]byte("== ed25519v1-secret: type0 ==")) + fileBytes.Write(bytes.Repeat([]byte{0x00}, 3)) + fileBytes.Write(h[:]) - prvFile, _ := os.Create(path + "/hs_ed25519_secret_key") - fileBytes.WriteTo(prvFile) - prvFile.Close() + prvFile, _ := os.Create(path + "/hs_ed25519_secret_key") + fileBytes.WriteTo(prvFile) + prvFile.Close() - // Create the Tor Hidden Service public key file - fmt.Println("Creating public file...") - fileBytes.Reset() - fileBytes.Write([]byte("== ed25519v1-public: type0 ==")) - fileBytes.Write(bytes.Repeat([]byte{0x00}, 3)) - fileBytes.Write([]byte(publicKey)) + // Create the Tor Hidden Service public key file + fmt.Println("Creating public file...") + fileBytes.Reset() + fileBytes.Write([]byte("== ed25519v1-public: type0 ==")) + fileBytes.Write(bytes.Repeat([]byte{0x00}, 3)) + fileBytes.Write([]byte(publicKey)) - pubFile, _ := os.Create(path + "/hs_ed25519_public_key") - fileBytes.WriteTo(pubFile) - pubFile.Close() + pubFile, _ := os.Create(path + "/hs_ed25519_public_key") + fileBytes.WriteTo(pubFile) + pubFile.Close() - // From https://github.com/rdkr/oniongen-go - // checksum = H(".onion checksum" || pubkey || version) - fmt.Println("Creating onion address...") - var checksumBytes bytes.Buffer - checksumBytes.Write([]byte(".onion checksum")) - checksumBytes.Write([]byte(publicKey)) - checksumBytes.Write([]byte{0x03}) - checksum := sha3.Sum256(checksumBytes.Bytes()) + // From https://github.com/rdkr/oniongen-go + // checksum = H(".onion checksum" || pubkey || version) + fmt.Println("Creating onion address...") + var checksumBytes bytes.Buffer + checksumBytes.Write([]byte(".onion checksum")) + checksumBytes.Write([]byte(publicKey)) + checksumBytes.Write([]byte{0x03}) + checksum := sha3.Sum256(checksumBytes.Bytes()) - // onion_address = base32(pubkey || checksum || version) - var onionAddressBytes bytes.Buffer - onionAddressBytes.Write([]byte(publicKey)) - onionAddressBytes.Write([]byte(checksum[:2])) - onionAddressBytes.Write([]byte{0x03}) - onionAddress := base32.StdEncoding.EncodeToString(onionAddressBytes.Bytes()) + // onion_address = base32(pubkey || checksum || version) + var onionAddressBytes bytes.Buffer + onionAddressBytes.Write([]byte(publicKey)) + onionAddressBytes.Write([]byte(checksum[:2])) + onionAddressBytes.Write([]byte{0x03}) + onionAddress := base32.StdEncoding.EncodeToString(onionAddressBytes.Bytes()) - // Create the Tor Hidden Service hostname file - fmt.Println("Creating onion address file...") - nameFile, _ := os.Create(path + "/hostname") - nameFile.WriteString(strings.ToLower(onionAddress) + ".onion\n") - nameFile.Close() + // Create the Tor Hidden Service hostname file + fmt.Println("Creating onion address file...") + nameFile, _ := os.Create(path + "/hostname") + nameFile.WriteString(strings.ToLower(onionAddress) + ".onion\n") + nameFile.Close() - fmt.Println("Done!") + fmt.Println("Done!") } diff --git a/dist/setup.sh b/dist/setup.sh index 680c91e..2be2723 100755 --- a/dist/setup.sh +++ b/dist/setup.sh @@ -407,6 +407,7 @@ install_docker() { fi copy_file $cyphernodeconf_filepath/tor/torrc $TOR_DATAPATH/torrc 1 $SUDO_REQUIRED + copy_file $cyphernodeconf_filepath/tor/hidden_service/* $TOR_DATAPATH/hidden_service/ 1 $SUDO_REQUIRED fi @@ -721,22 +722,6 @@ install() { fi } -manage_tor_keys() { - until [ -f hostname ] && [ -f hs_ed25519_secret_key ] && [ -f hs_ed25519_public_key ] -do - sleep 0.1 - max=$(($max-1)) - if [[ $max == 0 ]] - then - # Kill Tor on timeout and exit with error code 1. - kill -9 $pid >/dev/null 2>&1 - rm -f torrc hostname hs_ed25519_secret_key hs_ed25519_public_key - popd >/dev/null 2>&1 - exit 1 - fi -done -} - CONFIGURE=0 INSTALL=0 RECREATE=0 @@ -829,10 +814,6 @@ if [[ $CONFIGURE == 1 ]]; then configure $RECREATE fi -# If TOR is installed, we want to create the Hidden Serivce hostname now to make it -# available to LN and whatever needs it at this point... -manage_tor_keys - if [[ -f "$cyphernodeconf_filepath/installer/config.sh" ]]; then . "$cyphernodeconf_filepath/installer/config.sh" fi diff --git a/proxy_docker/app/script/requesthandler.sh b/proxy_docker/app/script/requesthandler.sh index c62a761..613545c 100644 --- a/proxy_docker/app/script/requesthandler.sh +++ b/proxy_docker/app/script/requesthandler.sh @@ -79,12 +79,7 @@ main() { installation_info) # GET http://192.168.111.152:8080/info if [ -f "$DB_PATH/info.json" ]; then - if [ -f "tor/hidden_service/hostname" ]; then - # Replace tor_hostname_placeholder with actual tor hostname from tor file - response=$(sed "s/tor_hostname_placeholder/`tr -d '\n\r' < tor/hidden_service/hostname`/g" "$DB_PATH/info.json") - else - response=$(cat "$DB_PATH/info.json") - fi + response=$(cat "$DB_PATH/info.json") else response='{ "error": "missing installation data" }' fi