Removed install folder, replaced by cyphernodeconf_docker.

Removed LE-companion doc.
Updated documentation.
This commit is contained in:
kexkey
2019-06-28 11:11:28 -04:00
parent e880d47cb4
commit da6e4ba805
58 changed files with 16 additions and 5503 deletions

View File

@@ -20,6 +20,8 @@ Or you can simply run this magic command to start setup and installation:
curl -fsSL https://raw.githubusercontent.com/SatoshiPortal/cyphernode/master/dist/setup.sh -o setup_cyphernode.sh && chmod +x setup_cyphernode.sh && ./setup_cyphernode.sh
```
Note that you can replace "master" in the URL by "dev" or any existing git branch/tag you actually want to install.
### Build cyphernode yourself
You can build cyphernode images yourself. The images will have the same name than the ones in the docker hub, with the suffix -local.
@@ -32,6 +34,10 @@ cd dist
./setup.sh
```
`setup.sh` will detect locally built images (with suffix `-local`) and ask you if you want to use them when installing cyphernode.
For full paranoia mode, you can also build yourself all images used by cyphernode but in external repositories by using the `build.sh` script in each of the repo. You can see a list of images cyphernode uses here: https://cloud.docker.com/u/cyphernode/repository/list
## Upgrading
To upgrade to the most recent version, just get and run the most recent version of the setup.sh file as described in the previous section. Migration should be taken care by the script.

View File

@@ -25,14 +25,11 @@ We are providing an installer to help you setup Cyphernode.
#### See [Instructions for installation](INSTALL.md) for automatic install instructions
All the Docker images used by Cyphernode have been prebuilt for x86 and ARM (RPi) architectures and are hosted on the Docker hub public registry, Cyphernode repository (https://hub.docker.com/u/cyphernode/).
All the Docker images used by Cyphernode have been prebuilt for x86, ARM (RPi) and aarch64 (pine64/NODL) architectures and are hosted on the Docker hub public registry, Cyphernode repository (https://hub.docker.com/u/cyphernode/).
### Build from sources
However, it is possible for you to build from sources. In that case, please refer to the files INSTALL-MANUALLY.md and INSTALL-MANUAL-STEPS.md.
#### See [Instructions for manual installation](INSTALL-MANUALLY.md) for manual build and install instructions
#### See [Step-by-step detailed instructions](INSTALL-MANUAL-STEPS.md) for real-world copy-paste standard install instructions
However, it is possible for you to build from sources. In that case, please refer to the `build.sh` scripts in each of the repositories used by cyphernode (https://cloud.docker.com/u/cyphernode/repository/list). See [Instructions for installation](INSTALL.md).
# For Your Information
@@ -42,6 +39,8 @@ Current components in Cyphernode:
- Proxy: request handler. Well dispatch authenticated and authorized requests to the right component. Use a SQLite3 database for its tasks.
- Proxy Cron: scheduler. Can call the proxy on regular interval for asynchronous tasks like payment notifications on watches, callbacks when OTS files are ready, etc.
- Pycoin: Bitcoin keys and addresses tool. Used by Cyphernode to derive addresses from an xPub and a derivation path.
- notifier: Handling callbacks used by watchers as well as OTS stamping.
- broker: pub/sub mechanism is taken care by the broker to which all subscribers and publishers should register.
- Bitcoin: Bitcoin Core node. Cyphernode uses a watching wallet for watchers (no funds) and a spending wallet for spending. Mandatory component, but optionally part of Cyphernode installation, as we can use an already running Bitcoin Core node.
- Lightning: optional. C-Lightning node. The LN node will use the Bitcoin node for its tasks.
- OTSclient: optional. Used to stamp hashes on the Bitcoin blockchain.
@@ -61,18 +60,18 @@ If you decide to have a prune Bitcoin Core node, the fee calculation on incoming
## Lightning Network
Currently, the LN functionalities of Cyphernode are very limited. Maybe even hard to use. You can:
Currently, basic LN functionalities is offered by Cyphernode. You can:
- Get information on your LN node: ln_getinfo
- Get a Bitcoin address where to send your funds to be used by your LN node: ln_newaddr
- Create an invoice, so people can send you payment; the burden of creating a channel/route to you is on the payer: ln_create_invoice
- Pay an invoice. You have to have the invoice and your LN node must already be connected to the network: ln_pay
Basic and crucial functionalities that's missing (you have to manually use lightning-cli on your LN node):
- Decode a BOLT11 string.
- Delete a created invoice to make sure cancelled payments are not accepted.
- Get a previously created invoice.
- Connect + fund: connects to a peer and fund a channel, all in one call. A callback can be provided to let you know when the channel is ready to use.
- Get connection string: to let your user know how to connect to your LN node.
- Be notified when a LN payment is received
- Connect your node to the LN network
- Open/close channels
## Manually test your installation through the Gatekeeper

View File

@@ -1,66 +0,0 @@
# Cyphernode + Let's Encrypt Companion
Cyphernode has been built with a low-traffic semi-trusted usage in mind. Expose it to the outside world at your own risk and peril.
## Install Cyphernode
```
cd ; git clone https://github.com/SatoshiPortal/cyphernode.git
cd cyphernode/
git checkout features/lnfeats
```
### Adjust Cyphernode docker-compose
```
vi install/generator-cyphernode/generators/app/templates/installer/docker/docker-compose.yaml
```
(add environment in gatekeeper)
```
- "VIRTUAL_PROTO=https"
- "VIRTUAL_HOST=cyphernode.yourdomain.com"
- "VIRTUAL_PORT=443"
- "LETSENCRYPT_HOST=cyphernode.yourdomain.com"
- "LETSENCRYPT_EMAIL=you@yourdomain.com"
```
```
./build.sh
./dist/setup.sh
```
(choose a different port for the gatekeeper, 443 will be used by the letsencrypt companion)
## Install docker-compose
```
sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
```
## Install and start letsencypt-companion
```
cd ; git clone https://github.com/buchdag/letsencrypt-nginx-proxy-companion-compose.git
cd letsencrypt-nginx-proxy-companion-compose/2-containers/compose-v3/labels/
vi docker-compose.yaml
```
(change network to cyphernodenet)
```
docker-compose up -d
```
## Start Cyphernode
```
cd ~/cyphernode/dist/
./start.sh
```
## Web access Cyphernode
https://cyphernode.yourdomain.com/status

View File

@@ -1,6 +0,0 @@
SatoshiPortal
data
script
generator-cyphernode/node_modules
generator-cyphernode/package-lock
generator-cyphernode/__tests__

1
install/.gitignore vendored
View File

@@ -1 +0,0 @@
data

View File

@@ -1,19 +0,0 @@
FROM node:11.1-alpine
RUN apk add --update bash su-exec p7zip openssl nano apache2-utils git && rm -rf /var/cache/apk/*
RUN mkdir -p /app
RUN mkdir /.config
RUN chmod a+rwx /.config
# Workaround for https://github.com/npm/uid-number/issues/3
RUN NPM_CONFIG_UNSAFE_PERM=true npm install -g yo
COPY generator-cyphernode /app
WORKDIR /app/generator-cyphernode
RUN npm link
WORKDIR /data
ENV EDITOR=/usr/bin/nano
ENTRYPOINT ["/sbin/su-exec"]
RUN find / -perm +6000 -type f -exec chmod a-s {} \; || true

View File

View File

@@ -1,2 +0,0 @@
node_modules
coverage

View File

@@ -1,10 +0,0 @@
[
{
"name": "Lightning node",
"value": "lightning"
},
{
"name": "Opentimestamps client",
"value": "otsclient"
}
]

View File

@@ -1,45 +0,0 @@
{
"features": "What optional <font underline='true'>features</font> do you want me to activate?",
"net": "Which Bitcoin <font underline='true'>network</font> do you want Cyphernode to run on?",
"run_as_different_user": "I recommend running Cyphernode as a <font underline='true'>different user</font> when possible. Using your current user would give Cyphernode your current access rights, which could be a security issue especially if you are a sudoer. Please note that this feature is not supported on OSX at runtime, but you will be fine activating it in case you want to use the configuration file on another machine.",
"username": "Run Cyphernode as <font underline='true'>what user</font>? I recommend user <font color='# 0000ff'>cyphernode</font>. If the user does not exist, I will create it for you.",
"use_xpub": "Cyphernode can derive Bitcoin addresses from an xPub and the derivation path you want. If you want, you can provide your xPub and derivation path right now and call 'derive' with only the index instead of having to pass your xPub and derivation path on each call.",
"xpub": "Cyphernode can derive addresses from your <font underline='true'>default xPub key</font>. With that functionality, you don't have to provide your xPub every time you call the derivation endpoints.",
"derivation_path": "Cyphernode can derive addresses from your <font underline='true'>default derivation path</font>. With that functionality, you don't have to provide your derivation path every time you call the derivation endpoints.",
"proxy_datapath": "The Cyphernode proxy container, which routes all the requests to the right services uses a sqlite3 database to keep track of some things. This DB will be mounted from a <font underline='true'>local path</font>, easy to back up from outside Docker. <font color='#ff0000'>If running on OSX, check mountable directories in Docker's File Sharing configs.</font>",
"proxy_datapath_custom": " ",
"gatekeeper_clientkeyspassword": "The Gatekeeper checks all the incoming requests for the right permissions before delegating them to the proxy. Following the JWT standard, it uses HMAC signature verification to allow or deny access. Signatures are created and verified using secret keys. I am going to generate the secret keys and keep them in an encrypted file. You will be able to download this encrypted file later. Please provide the <font underline='true'>encryption passphrase</font>.",
"gatekeeper_clientkeyspassword_c": " ",
"gatekeeper_recreatekeys": "The Gatekeeper keys already exist, do you want to <font underline='true'>regenerate</font> them? This will overwrite existing ones.",
"gatekeeper_recreatecert": "The Gatekeeper TLS (SSL) certificates already exist, do you want to <font underline='true'>regenerate</font> them? This will overwrite existing ones.",
"gatekeeper_datapath": "The Gatekeeper's files (TLS certs, HMAC keys, Groups/API) will be stored in a container's mounted directory. Please provide the <font underline='true'>local mounted path</font> to that directory. <font color='#ff0000'>If running on OSX, check mountable directories in Docker's File Sharing configs.</font>",
"gatekeeper_datapath_custom": "Provide the <font underline='true'>full path name</font> where the Gatekeeper's files will be saved.",
"gatekeeper_edit_apiproperties": "If you know what you are doing, it is possible to manually edit the API endpoints/groups authorization. (Not recommended)",
"gatekeeper_apiproperties": "You are about to edit the api.properties file. The format of the file is pretty simple: for each action, you will find what access group can access it. <font color='# 0000ff'>Admin</font> group can do what <font color='# 0000ff'>Spender</font> group can, and <font color='# 0000ff'>Spender</font> group can do what <font color='# 0000ff'>Watcher</font> group can. <font color='# 0000ff'>Internal</font> group is for the endpoints accessible only within the Docker network, like the backoffice tasks used by the Cron container. The access groups for each API id/key are found in the <font color='# 0000ff'>keys.properties</font> file.",
"gatekeeper_cns": "I use <font underline='true'>domain names</font> and/or <font underline='true'>IP addresses</font> to create valid TLS certificates. For example, if <font color='# 0000ff'>https://cyphernodehost/getbestblockhash</font> and <font color='# 0000ff'>https://192.168.7.44/getbestblockhash</font> will be used, enter <font color='# 0000ff'>cyphernodehost, 192.168.7.44</font> as a possible domains. <font color='# 0000ff'>127.0.0.1, localhost, gatekeeper</font> will be automatically added to your list. Make sure the provided domain names are in your DNS or client's hosts file and is reachable.",
"traefik_datapath": "The Traefik's files will be stored in a container's mounted directory. Please provide the <font underline='true'>local mounted path</font> to that directory. <font color='#ff0000'>If running on OSX, check mountable directories in Docker's File Sharing configs.</font>",
"traefik_datapath_custom": "Provide the <font underline='true'>full path name</font> where the Traefik's files will be saved.",
"bitcoin_mode": "Cyphernode will spawn a new <font underline='true'>Bitcoin Core</font> full node for its own use. If you already have Bitcoin Core node data, you can use the directory containing that data directly or copy the contents of it to a new directory to be used by cyphernode. Be aware that the files might change ownership, if you run cyphernode as a different user. In case you want to move the blockchain data to another node you might need to change the owner to fit the configuration of that node.",
"bitcoin_node_ip": "Cyphernode uses <font color='#00ff00'>Bitcoin Core</font> RPC interface for its tasks. Please provide the <font underline='true'>IP address</font> of your current Bitcoin Core node.",
"bitcoin_rpcuser": "Bitcoin Core's <font underline='true'>RPC username</font> used by Cyphernode when calling the node.",
"bitcoin_rpcpassword": "Bitcoin Core's <font underline='true'>RPC password</font> used by Cyphernode when calling the node.",
"bitcoin_prune": "If you don't have at least 350GB of disk space, you should run Bitcoin Core in <font underline='true'>prune mode</font>. <font color='#00ff00'>NOTE</font>: when running Bitcoin Core in prune mode, the incoming transactions' fees cannot be computed by Cyphernode and won't be part of the addresses watching's callbacks payload.",
"bitcoin_prune_size": "Minimum <font underline='true'>size</font> is <font color='#00ff00'>550</font>. This option specifies the maximum number in MB Bitcoin Core will allocate for raw block & undo data.",
"bitcoin_uacomment": "<font underline='true'>User Agent</font> string used by Bitcoin Core. (Optional)",
"bitcoin_datapath": "<font underline='true'>Path name</font> to where Bitcoin Core's data files (blockchain data, wallets, configs, etc.) are stored. This directory will be mounted into the Bitcoin node's container. If you already have a sync'ed node, you can copy data there to be used by the node, instead of resyncing everything. <font color='#00ff00'>NOTE</font>: only copy chainstate/ and blocks/ contents. <font color='#ff0000'>If running on OSX, check mountable directories in Docker's File Sharing configs.</font>",
"bitcoin_datapath_custom": " ",
"bitcoin_expose": "By default, Bitcoin node ports (RPC and protocol) won't be <font underline='true'>published</font> outside of Docker. Do you want to expose them so that your node can be accessed from outside of the Docker network?",
"lightning_implementation": "Multiple <font underline='true'>LN implementations</font> exist. Please choose the one you want to use with Cyphernode.",
"lightning_external_ip": "If you want you LN node to be accessible from the Internet, provide the <font underline='true'>IP address</font> that other LN nodes will use to connect to it. This is usually your router's public IP. <font color='#00ff00'>NOTE</font>: In case you are running Cyphernode at home. This option won't make your router forward needed LN ports; you still need to configure and manage that part yourself in your router configuration.",
"lightning_nodename": "LN nodes have names. Choose the <font underline='true'>name you want</font> for yours.",
"lightning_nodecolor": "LN nodes have colors. Choose the <font underline='true'>color you want</font> for yours in RGB format (RRGGBB). For example, pure red would be <font color='#ff0000'>ff0000</font>.",
"lightning_datapath": "<font underline='true'>Path name</font> to where LN's data files are stored. This directory will be mounted into the LN node's container. <font color='#ff0000'>If running on OSX, check mountable directories in Docker's File Sharing configs.</font>",
"lightning_datapath_custom": " ",
"lightning_expose": "By default, LN node port will be <font underline='true'>published</font> outside of Docker. Do you want to hide it so that your node can't be accessed from outside of the Docker network?",
"otsclient_datapath": "<font underline='true'>Full path</font> where the OTS files will be stored. This path will be mounted into the otsclient container which will create the OTS files when <font color='#00ff00'>stamping</font> and update them when <font color='#00ff00'>upgrading</font> stamps. It will also be mounted to the proxy container so that it can serve the <font color='#00ff00'>ots_getfile</font> and send the OTS files to clients. <font color='#ff0000'>If running on OSX, check mountable directories in Docker's File Sharing configs.</font>",
"otsclient_datapath_custom": " ",
"installer_mode": "Only one <font underline='true'>installation mode</font> is supported, right now: <font color='#0000ff'>local docker (self-hosted)</font>. Choose wisely ;-)",
"installer_cleanup": "Do you want to <font underline='true'>remove</font> this configurator Docker image after installation? This would free about 150MB of disk space.",
"docker_mode": "Cyphernode Docker services can be run using <font underline='true'>Docker Swarm</font> (https://docs.docker.com/engine/swarm/) or <font underline='true'>docker-compose</font> (https://docs.docker.com/compose/overview/). Both will work, some users prefer one to another depending on deployment types, scalability, current framework, etc.",
"__default__": ""
}

View File

@@ -1,577 +0,0 @@
const Generator = require('yeoman-generator');
const chalk = require('chalk');
const wrap = require('wrap-ansi');
const html2ansi = require('./lib/html2ansi.js');
const fs = require('fs');
const validator = require('validator');
const path = require("path");
const coinstring = require('coinstring');
const name = require('./lib/name.js');
const Archive = require('./lib/archive.js');
const ApiKey = require('./lib/apikey.js');
const Cert = require('./lib/cert.js');
const htpasswd = require( './lib/htpasswd.js')
const featureChoices = require('./features.json');
const uaCommentRegexp = /^[a-zA-Z0-9 \.,:_\-\?\/@]+$/; // TODO: look for spec of unsafe chars
const userRegexp = /^[a-zA-Z0-9\._\-]+$/;
const reset = '\u001B8\u001B[u';
const clear = '\u001Bc';
const configFileVersion='0.1.0';
const defaultAPIProperties = `
# Stats can:
action_getblockchaininfo=stats
# Watcher can:
action_watch=watcher
action_unwatch=watcher
action_watchxpub=watcher
action_unwatchxpubbyxpub=watcher
action_unwatchxpubbylabel=watcher
action_getactivewatchesbyxpub=watcher
action_getactivewatchesbylabel=watcher
action_getactivexpubwatches=watcher
action_watchtxid=watcher
action_getactivewatches=watcher
action_getbestblockhash=watcher
action_getbestblockinfo=watcher
action_getblockinfo=watcher
action_gettransaction=watcher
action_ln_getinfo=watcher
action_ln_create_invoice=watcher
action_ln_getconnectionstring=watcher
action_ln_decodebolt11=watcher
# Spender can do what the watcher can do, plus:
action_getbalance=spender
action_getbalancebyxpub=spender
action_getbalancebyxpublabel=spender
action_getnewaddress=spender
action_spend=spender
action_addtobatch=spender
action_batchspend=spender
action_deriveindex=spender
action_derivepubpath=spender
action_ln_pay=spender
action_ln_newaddr=spender
action_ots_stamp=spender
action_ots_getfile=spender
action_ln_getinvoice=spender
action_ln_decodebolt11=spender
action_ln_connectfund=spender
# Admin can do what the spender can do, plus:
# Should be called from inside the Docker network only:
action_conf=internal
action_newblock=internal
action_executecallbacks=internal
action_ots_backoffice=internal
`;
const prefix = function() {
return chalk.green('Cyphernode')+': ';
};
let prompters = [];
fs.readdirSync(path.join(__dirname, "prompters")).forEach(function(file) {
prompters.push(require(path.join(__dirname, "prompters",file)));
});
const sleep = function(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const easeOutCubic = function(t, b, c, d) {
return c*((t=t/d-1)*t*t+1)+b;
}
const splash = async function() {
let frames = [];
fs.readdirSync(path.join(__dirname,'splash')).forEach(function(file) {
frames.push(fs.readFileSync(path.join(__dirname,'splash',file)));
});
const frame0 = frames[0];
const frame0lines = frame0.toString().split('\n');
const frame0lineCount = frame0lines.length;
const steps = 10;
process.stdout.write(clear);
await sleep(150);
for( let i=0; i<=steps; i++ ) {
const pos = easeOutCubic( i, 0, frame0lineCount, steps ) | 0;
process.stdout.write(reset);
for( let l=frame0lineCount-pos; l<frame0lineCount; l++ ) {
process.stdout.write( frame0lines[l]+'\n' );
}
await sleep(33);
}
await sleep(400);
for( let frame of frames ) {
process.stdout.write(reset);
process.stdout.write(frame.toString());
await sleep(33);
}
await sleep(400);
process.stdout.write('\n');
}
module.exports = class extends Generator {
constructor(args, opts) {
super(args, opts);
if( args.indexOf('recreate') !== -1 ) {
this.recreate = true;
}
this.featureChoices = featureChoices;
if( fs.existsSync(path.join('/data', 'exitStatus.sh')) ) {
fs.unlinkSync(path.join('/data', 'exitStatus.sh'));
}
}
async _initConfig() {
this.defaultDataDirBase = process.env.DEFAULT_DATADIR_BASE;
this.setupDir = process.env.SETUP_DIR;
const versionOverride = process.env.VERSION_OVERRIDE==='true';
if( fs.existsSync(this.destinationPath('config.7z')) ) {
let r = {};
if( process.env.CFG_PASSWORD ) {
this.configurationPassword = process.env.CFG_PASSWORD;
} else {
process.stdout.write(reset);
while( !r.password ) {
r = await this.prompt([{
type: 'password',
name: 'password',
message: prefix()+chalk.bold.blue('Enter your configuration password?'),
filter: this._trimFilter
}]);
}
this.configurationPassword = r.password;
}
const archive = new Archive( this.destinationPath('config.7z'), this.configurationPassword );
r = await archive.readEntry('config.json');
if( r.error ) {
console.log(chalk.bold.red('Password is wrong. Have a nice day.'));
process.exit(1);
}
if( !r.value ) {
console.log(chalk.bold.red('config archive is corrupt.'));
process.exit(1);
}
try {
this.props = JSON.parse(r.value);
this.props.__version = this.props.__version || configFileVersion;
} catch( err ) {
console.log(chalk.bold.red('config archive is corrupt.'));
process.exit(1);
}
} else {
let r = {};
process.stdout.write(clear+reset);
while( !r.password0 || !r.password1 || r.password0 !== r.password1 ) {
if( r.password0 && r.password1 && r.password0 !== r.password1 ) {
console.log(chalk.bold.red('Passwords do not match')+'\n');
}
r = await this.prompt([{
type: 'password',
name: 'password0',
message: prefix()+chalk.bold.blue('Choose your configuration password'),
filter: this._trimFilter
},
{
type: 'password',
name: 'password1',
message: prefix()+chalk.bold.blue('Confirm your configuration password'),
filter: this._trimFilter
}]);
}
this.configurationPassword = r.password0;
this.props = {
__version: configFileVersion
};
}
if( this.props.__version !== configFileVersion ) {
// migrate here
}
this.props.initial_admin_password = await htpasswd(this.configurationPassword);
if( versionOverride ) {
delete this.props.gatekeeper_version;
delete this.props.proxy_version;
delete this.props.notifier_version;
delete this.props.proxycron_version;
delete this.props.pycoin_version;
delete this.props.otsclient_version;
delete this.props.bitcoin_version;
delete this.props.lightning_version;
delete this.props.sparkwallet_version;
delete this.props.grafana_version;
}
this._resolveConfigConflicts();
this._assignConfigDefaults();
for( let c of this.featureChoices ) {
c.checked = this._isChecked( 'features', c.value );
}
}
async prompting() {
await this._initConfig();
await sleep(1000);
await splash();
if( this.recreate ) {
// no prompts
return;
}
// save gatekeeper key password to check if it changed
this.gatekeeper_clientkeyspassword = this.props.gatekeeper_clientkeyspassword;
let r = await this.prompt([{
type: 'confirm',
name: 'enablehelp',
message: prefix()+'Enable help?',
default: this._getDefault( 'enablehelp' ),
}]);
this.props.enablehelp = r.enablehelp;
if( this.props.enablehelp ) {
this.help = require('./help.json');
}
let prompts = [];
for( let m of prompters ) {
prompts = prompts.concat(m.prompts(this));
}
return this.prompt(prompts).then(props => {
this.props = Object.assign(this.props, props);
});
}
async configuring() {
if( this.props.gatekeeper_recreatekeys ||
this.props.gatekeeper_keys.configEntries.length===0 ) {
const apikey = new ApiKey();
let configEntries = [];
let clientInformation = [];
apikey.setId('000');
apikey.setGroups(['stats']);
await apikey.randomiseKey();
configEntries.push(apikey.getConfigEntry());
clientInformation.push(apikey.getClientInformation());
apikey.setId('001');
apikey.setGroups(['stats','watcher']);
await apikey.randomiseKey();
configEntries.push(apikey.getConfigEntry());
clientInformation.push(apikey.getClientInformation());
apikey.setId('002');
apikey.setGroups(['stats','watcher','spender']);
await apikey.randomiseKey();
configEntries.push(apikey.getConfigEntry());
clientInformation.push(apikey.getClientInformation());
apikey.setId('003');
apikey.setGroups(['stats','watcher','spender','admin']);
await apikey.randomiseKey();
configEntries.push(apikey.getConfigEntry());
clientInformation.push(apikey.getClientInformation());
this.props.gatekeeper_keys = {
configEntries: configEntries,
clientInformation: clientInformation
}
}
if( this.props.gatekeeper_recreatecert ||
!this.props.gatekeeper_sslcert ||
!this.props.gatekeeper_sslkey ) {
delete this.props.gatekeeper_recreatecert;
const cert = new Cert();
console.log(chalk.bold.green( '☕ Generating gatekeeper cert. This may take a while ☕' ));
try {
const cns = (this.props.gatekeeper_cns||'').split(',').map(e=>e.trim().toLowerCase()).filter(e=>!!e);
const result = await cert.create(cns);
if( result.code === 0 ) {
this.props.gatekeeper_sslkey = result.key.toString();
this.props.gatekeeper_sslcert = result.cert.toString();
// Total array of cns, used to create Cyphernode's URLs
this.props.cns = []
result.cns.forEach(e => {
this.props.cns.push(e)
})
} else {
console.log(chalk.bold.red( 'error! Gatekeeper cert was not created' ));
}
} catch( err ) {
console.log(chalk.bold.red( 'error! Gatekeeper cert was not created' ));
}
}
delete this.props.gatekeeper_recreatekeys;
}
async writing() {
this._resolveConfigConflicts()
const configJsonString = JSON.stringify(this.props, null, 4);
const archive = new Archive( this.destinationPath('config.7z'), this.configurationPassword );
if( !await archive.writeEntry( 'config.json', configJsonString ) ) {
console.log(chalk.bold.red( 'error! Config archive was not written' ));
}
const pathProps = [
'gatekeeper_datapath',
'traefik_datapath',
'proxy_datapath',
'bitcoin_datapath',
'lightning_datapath',
'otsclient_datapath'
];
for( let pathProp of pathProps ) {
if( this.props[pathProp] === '_custom' ) {
this.props[pathProp] = this.props[pathProp+'_custom'] || '';
}
}
for( let m of prompters ) {
const name = m.name();
for( let t of m.templates(this.props) ) {
const p = path.join(name,t);
this.fs.copyTpl(
this.templatePath(p),
this.destinationPath(p),
this.props
);
}
}
if( this.props.gatekeeper_keys && this.props.gatekeeper_keys.clientInformation ) {
if( this.gatekeeper_clientkeyspassword !== this.props.gatekeeper_clientkeyspassword &&
fs.existsSync(this.destinationPath('client.7z')) ) {
fs.unlinkSync( this.destinationPath('client.7z') );
}
const archive = new Archive( this.destinationPath('client.7z'), this.props.gatekeeper_clientkeyspassword );
if( !await archive.writeEntry( 'keys.txt', this.props.gatekeeper_keys.clientInformation.join('\n') ) ) {
console.log(chalk.bold.red( 'error! Client gatekeeper key archive was not written' ));
}
if( !await archive.writeEntry( 'cacert.pem', this.props.gatekeeper_sslcert ) ) {
console.log(chalk.bold.red( 'error! Client gatekeeper key archive was not written' ));
}
}
fs.writeFileSync(path.join('/data', 'exitStatus.sh'), 'EXIT_STATUS=0');
}
install() {
}
/* some utils */
_resolveConfigConflicts() {
if( this.props.features && this.props.features.length && this.props.features.indexOf('lightning') !== -1 ) {
this.props.bitcoin_prune = false;
delete this.props.bitcoin_prune_size;
}
}
_assignConfigDefaults() {
this.props = Object.assign( {
features: [],
enablehelp: true,
net: 'testnet',
xpub: '',
derivation_path: '0/n',
installer_mode: 'docker',
devmode: false,
devregistry: false,
run_as_different_user: true,
username: 'cyphernode',
docker_mode: 'compose',
bitcoin_rpcuser: 'bitcoin',
bitcoin_rpcpassword: 'CHANGEME',
bitcoin_uacomment: '',
bitcoin_prune: false,
bitcoin_prune_size: 550,
bitcoin_datapath: '',
bitcoin_node_ip: '',
bitcoin_mode: 'internal',
bitcoin_expose: false,
lightning_expose: true,
gatekeeper_port: 2009,
gatekeeper_apiproperties: defaultAPIProperties,
gatekeeper_ipwhitelist: '',
gatekeeper_keys: { configEntries: [], clientInformation: [] },
gatekeeper_sslcert: '',
gatekeeper_sslkey: '',
gatekeeper_cns: process.env['DEFAULT_CERT_HOSTNAME'] || '',
gatekeeper_datapath: '',
proxy_datapath: '',
lightning_implementation: 'c-lightning',
lightning_external_ip: '',
lightning_datapath: '',
lightning_nodename: name.generate(),
lightning_nodecolor: '',
otsclient_datapath: '',
traefik_datapath: '',
installer_cleanup: false,
default_username: process.env.DEFAULT_USER || '',
gatekeeper_version: process.env.GATEKEEPER_VERSION || 'latest',
proxy_version: process.env.PROXY_VERSION || 'latest',
notifier_version: process.env.NOTIFIER_VERSION || 'latest',
proxycron_version: process.env.PROXYCRON_VERSION || 'latest',
pycoin_version: process.env.PYCOIN_VERSION || 'latest',
otsclient_version: process.env.OTSCLIENT_VERSION || 'latest',
bitcoin_version: process.env.BITCOIN_VERSION || 'latest',
lightning_version: process.env.LIGHTNING_VERSION || 'latest',
sparkwallet_version: process.env.SPARKWALLET_VERSION || 'standalone'
}, this.props );
}
_isChecked( name, value ) {
return this.props && this.props[name] && this.props[name].indexOf(value) != -1 ;
}
_getDefault( name ) {
return this.props && this.props[name];
}
_optional(input,validator) {
if( input === undefined ||
input === null ||
input === '' ) {
return true;
}
return validator(input);
}
_ipOrFQDNValidator( host ) {
host = (host+"").trim();
if( !(validator.isIP(host) ||
validator.isFQDN(host)) ) {
throw new Error( 'No IP address or fully qualified domain name' )
}
return true;
}
_xkeyValidator( xpub ) {
// TOOD: check for version
if( !coinstring.isValid( xpub ) ) {
throw new Error('Not an extended key.');
}
return true;
}
_pathValidator( p ) {
return true;
}
_derivationPathValidator( path ) {
return true;
}
_colorValidator(color) {
if( !validator.isHexadecimal(color) ) {
throw new Error('Not a hex color.');
}
return true;
}
_lightningNodeNameValidator(name) {
if( !name || name.length > 32 ) {
throw new Error('Please enter anything shorter than 32 characters');
}
return true;
}
_notEmptyValidator( path ) {
if( !path ) {
throw new Error('Please enter something');
}
return true;
}
_usernameValidator( user ) {
if( !userRegexp.test( user ) ) {
throw new Error('Choose a valid username');
}
return true;
}
_UACommentValidator( comment ) {
if( !uaCommentRegexp.test( comment ) ) {
throw new Error('Unsafe characters in UA comment. Please use only a-z, A-Z, 0-9, SPACE and .,:_?@');
}
return true;
}
_trimFilter( input ) {
return (input+"").trim();
}
_featureChoices() {
return this.featureChoices;
}
_getHelp( topic ) {
if( !this.props.enablehelp || !this.help ) {
return '';
}
const helpText = this.help[topic] || this.help['__default__'];
if( !helpText ||helpText === '' ) {
return '';
}
return "\n\n"+wrap( html2ansi(helpText),82 )+"\n\n";
}
};

View File

@@ -1,76 +0,0 @@
const spawn = require('child_process').spawn;
module.exports = class ApiKey {
constructor( id, groups, key, script ) {
this.setId(id || '001');
this.setGroups(groups || ['admin'] );
this.setScript(script || 'eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}' );
this.setKey(key);
}
setGroups( groups ) {
this.groups = groups;
}
setId( id ) {
this.id = id;
}
setScript( script ) {
this.script = script;
}
setKey( key ) {
this.key = key;
}
async randomiseKey() {
try {
//const dd = spawn('/bin/dd if=/dev/urandom bs=32 count=1 | /usr/bin/xxd -pc 32');
const dd = spawn("dd if=/dev/urandom bs=32 count=1 | xxd -pc32", [], {stdio: ['ignore', 'pipe', 'ignore' ], shell: true} );
const result = await new Promise( function(resolve, reject ) {
let result = '';
dd.stdout.on('data', function( a,b,c) {
let chunk = a.toString().trim();
result += chunk;
});
dd.stdout.on('end', function() {
result = result.replace(/[^a-zA-Z0-9]/,'');
resolve(result);
});
dd.stdout.on('error', function(err) {
console.log(err);
reject(err);
})
});
this.key = result;
} catch( err ) {
console.log( err );
return;
}
}
getKey() {
return this.key;
}
getConfigEntry() {
if( !this.key ) {
return;
}
return `kapi_id="${this.id}";kapi_key="${this.key}";kapi_groups="${this.groups.join(',')}";${this.script}`;
}
getClientInformation() {
return `${this.id}=${this.key}`;
}
}
//dd if=/dev/urandom bs=32 count=1 2> /dev/null | xxd -pc 32

View File

@@ -1,77 +0,0 @@
const fs = require('fs');
const spawn = require('child_process').spawn;
const stringio = require('@rauschma/stringio');
const defaultArgs = ['-t7z', '-ms=on', '-mhe=on'];
module.exports = class Archive {
constructor( file, password ) {
this.file = file || 'archive.7z'
this.password = password;
}
async readEntry( entryName ) {
if( !entryName ) {
return;
}
let args = defaultArgs.slice();
args.unshift('x');
args.push( '-so' );
if( this.password ) {
args.push('-p'+this.password );
}
args.push( this.file )
args.push( entryName )
const archiver = spawn('7z', args, { stdio: ['ignore', 'pipe', 'ignore'] } );
const result = await stringio.readableToString(archiver.stdout);
try {
await stringio.onExit( archiver );
} catch( err ) {
return { error: err };
}
return { error: null, value: result };
}
async writeEntry( entryName, content ) {
if( !entryName ) {
return;
}
let args = defaultArgs.slice();
args.unshift('a');
if( this.password ) {
args.push('-p'+this.password );
}
args.push( '-si'+entryName );
args.push( this.file )
const archiver = spawn('7z', args, { stdio: ['pipe', 'ignore', 'ignore' ] } );
await stringio.streamWrite(archiver.stdin, content);
await stringio.streamEnd(archiver.stdin);
try {
await stringio.onExit( archiver );
} catch( err ) {
return false;
}
return true;
}
async deleteEntry( entryName ) {
if( !entryName ) {
return;
}
let args = defaultArgs.slice();
args.unshift('d');
if( this.password ) {
args.push('-p'+this.password );
}
args.push( this.file )
args.push( entryName )
const archiver = spawn('7z', args, { stdio: ['ignore', 'pipe','ignore'] } );
try {
await stringio.onExit( archiver );
} catch( err ) {
return false;
}
return true;
}
}

View File

@@ -1,117 +0,0 @@
const fs = require('fs');
const spawn = require('child_process').spawn;
const defaultArgs = ['req', '-x509', '-newkey', 'rsa:4096', '-nodes'];
const path = require('path');
const tmp = require('tmp');
const validator = require('validator');
const confTmpl = `
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_ca
prompt = no
[req_distinguished_name]
CN = %PRIMARY_CN%
[v3_ca]
subjectAltName = @alt_names
[alt_names]
%ALT_DOMAINS%
%ALT_IPS%
`;
const domainTmpl = 'DNS.%#% = %DOMAIN%';
const ipTmpl = 'IP.%#% = %IP%'
module.exports = class Cert {
constructor( options ) {
options = options || {};
this.args = options.args || { days: 3650 };
}
buildConfig( cns ) {
let ips = [];
let domains = [];
for( let cn of cns ) {
if( validator.isIP(cn) ) {
ips.push( cn );
} else {
domains.push( cn );
}
}
let conf = confTmpl;
if( !domains.length ) {
domains.push('localhost');
}
conf = conf.replace( '%PRIMARY_CN%', domains[0] )
let domainCount = 0;
domains = domains.map( d => domainTmpl.replace( '%#%', ++domainCount ).replace('%DOMAIN%', d) );
conf = conf.replace( '%ALT_DOMAINS%', domains.join('\n') || '' )
let ipCount = 0;
ips = ips.map( ip => ipTmpl.replace( '%#%', ++ipCount ).replace('%IP%', ip) );
conf = conf.replace( '%ALT_IPS%', ips.join('\n') || '' )
return conf;
}
async create( cns ) {
cns = cns || [];
cns = cns.concat(['127.0.0.1','localhost','gatekeeper']);
let args = defaultArgs.slice();
const certFileTmp = tmp.fileSync();
const keyFileTmp = tmp.fileSync();
const confFileTmp = tmp.fileSync();
args.push( '-out' );
args.push( certFileTmp.name );
args.push( '-keyout' );
args.push( keyFileTmp.name );
args.push( '-config' );
args.push( confFileTmp.name );
for( let k in this.args ) {
args.push( '-'+k);
args.push( this.args[k] );
}
const conf = this.buildConfig( cns );
fs.writeFileSync( confFileTmp.name, conf );
const openssl = spawn('openssl', args, { stdio: ['ignore', 'ignore', 'ignore'] } );
let code = await new Promise( function(resolve, reject) {
openssl.on('exit', (code) => {
resolve(code);
});
});
const cert = fs.readFileSync( certFileTmp.name );
const key = fs.readFileSync( keyFileTmp.name );
certFileTmp.removeCallback();
keyFileTmp.removeCallback();
confFileTmp.removeCallback();
return {
code: code,
key: key,
cert: cert,
cns: cns
}
}
getFullPath() {
return path.join( this.folder, this.filename );
}
}

View File

@@ -1,46 +0,0 @@
const parse5 = require('parse5');
const chalk = require('chalk');
const options = {
scriptingEnabled: false
}
const convert = function(data){
// recursively flatten
let v = data.childNodes && data.childNodes.length?
data.childNodes.map(d=> convert(d)).join(''):
data.value?data.value:'';
switch(data.tagName){
case 'br':
v += '\n'
break
case 'font':
if( data.attrs && data.attrs.length ) {
for( let attr of data.attrs ) {
if( attr.name === 'color' && /^#[a-f0-9]{6}$/.test(attr.value) ) {
v = chalk.hex(attr.value)(v);
}
if( attr.name === 'bold' && attr.value === 'true' ) {
v = chalk.bold(v);
}
if( attr.name === 'italic' && attr.value === 'true' ) {
v = chalk.italic(v);
}
if( attr.name === 'underline' && attr.value === 'true' ) {
v = chalk.underline(v);
}
if( attr.name === 'strikethrough' && attr.value === 'true' ) {
v = chalk.strikethrough(v);
}
}
}
break;
}
return v;
}
module.exports = function(html){
return convert(parse5.parseFragment(html, options));
}

View File

@@ -1,23 +0,0 @@
const exec = require('child_process').exec;
module.exports = async ( password ) => {
if( !password ) {
return null;
}
password = password.replace(/'/g, `'\\''`);
return await new Promise( (resolve) => {
exec('htpasswd -bnB admin \''+password+'\' | cut -sd \':\' -f2', (error, stdout, stderr) => {
if (error) {
return resolve(null);
}
// remove newline at the end
resolve(stdout.substr(0,stdout.length-1));
});
});
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,88 +0,0 @@
const chalk = require('chalk');
const name = 'cyphernode';
const capitalise = function( txt ) {
return txt.charAt(0).toUpperCase() + txt.substr(1);
};
const prefix = function() {
return chalk.green(capitalise(name)+': ');
};
module.exports = {
name: function() {
return name;
},
prompts: function( utils ) {
return [{
// https://github.com/SBoudrias/Inquirer.js#question
// input, confirm, list, rawlist, expand, checkbox, password, editor
type: 'checkbox',
name: 'features',
message: prefix()+'What features do you want to add to your cyphernode?'+utils._getHelp('features'),
choices: utils._featureChoices()
},
{
type: 'list',
name: 'net',
default: utils._getDefault( 'net' ),
message: prefix()+'What net do you want to run on?'+utils._getHelp('net'),
choices: [{
name: "Testnet",
value: "testnet"
},{
name: "Mainnet",
value: "mainnet"
}]
},
{
type: 'confirm',
name: 'run_as_different_user',
default: utils._getDefault( 'run_as_different_user' ),
message: prefix()+'Run as different user?'+utils._getHelp('run_as_different_user')
},
{
when: function( props ) {
return props.run_as_different_user;
},
type: 'input',
name: 'username',
default: utils._getDefault( 'username' ),
message: prefix()+'What username will cyphernode run under?'+utils._getHelp('username'),
filter: utils._trimFilter,
validate: utils._usernameValidator
},
{
type: 'confirm',
name: 'use_xpub',
default: utils._getDefault( 'use_xpub' )||false,
message: prefix()+'Use a default xpub key to watch or generate adresses?'+utils._getHelp('use_xpub'),
},
{
when: function( props ) {
return props.use_xpub;
},
type: 'input',
name: 'xpub',
default: utils._getDefault( 'xpub' ),
message: prefix()+'What is your default xpub key?'+utils._getHelp('xpub'),
filter: utils._trimFilter,
validate: utils._xkeyValidator
},
{
when: function( props ) {
return props.use_xpub;
},
type: 'input',
name: 'derivation_path',
default: utils._getDefault( 'derivation_path' ),
message: prefix()+'What is your default derivation path?'+utils._getHelp('derivation_path'),
filter: utils._trimFilter,
validate: utils._derivationPathValidator
}];
},
templates: function( props ) {
return [];
}
};

View File

@@ -1,112 +0,0 @@
const chalk = require('chalk');
const name = 'gatekeeper';
const capitalise = function( txt ) {
return txt.charAt(0).toUpperCase() + txt.substr(1);
};
const prefix = function() {
return chalk.green(capitalise(name)+': ');
};
const hasAuthKeys = function( props ) {
return props &&
props.gatekeeper_keys &&
props.gatekeeper_keys.configEntries &&
props.gatekeeper_keys.configEntries.length > 0;
}
const hasCert = function( props ) {
return props &&
props.gatekeeper_sslkey &&
props.gatekeeper_sslcert
}
let password = '';
module.exports = {
name: function() {
return name;
},
prompts: function( utils ) {
// TODO: delete clientKeys archive when password chnages
return [{
type: 'password',
name: 'gatekeeper_clientkeyspassword',
default: utils._getDefault( 'gatekeeper_clientkeyspassword' ),
message: prefix()+'Enter a password to protect your client keys with'+utils._getHelp('gatekeeper_clientkeyspassword'),
filter: utils._trimFilter,
validate: utils._notEmptyValidator
},
{
when: function( props ) {
// hacky hack
password = props.gatekeeper_clientkeyspassword;
return true;
},
type: 'password',
name: 'gatekeeper_clientkeyspassword_c',
default: utils._getDefault( 'gatekeeper_clientkeyspassword_c' ),
message: prefix()+'Confirm your client keys password.'+utils._getHelp('gatekeeper_clientkeyspassword_c'),
filter: utils._trimFilter,
validate: function( input ) {
if(input !== password) {
throw new Error( 'Client keys passwords do not match' );
}
return true;
}
},
{
type: 'input',
name: 'gatekeeper_port',
default: utils._getDefault( 'gatekeeper_port' ),
message: prefix()+'The port gatekeeper will listen on for requests'+utils._getHelp('gatekeeper_port'),
filter: utils._trimFilter,
validate: function( port ) {
return utils._notEmptyValidator( port ) && !isNaN( parseInt(port) )
}
},
{
when: function() { return hasAuthKeys( utils.props ); },
type: 'confirm',
name: 'gatekeeper_recreatekeys',
default: false,
message: prefix()+'Recreate gatekeeper keys?'+utils._getHelp('gatekeeper_recreatekeys')
},
{
when: function() { return hasCert( utils.props ); },
type: 'confirm',
name: 'gatekeeper_recreatecert',
default: false,
message: prefix()+'Recreate gatekeeper certificate?'+utils._getHelp('gatekeeper_recreatecert')
},
{
when: function(props) { return !hasCert( utils.props ) || props.gatekeeper_recreatecert },
type: 'input',
name: 'gatekeeper_cns',
default: utils._getDefault( 'gatekeeper_cns' ),
message: prefix()+'Gatekeeper cert CNS (ips, domains, wildcard domains seperated by comma)?'+utils._getHelp('gatekeeper_cns')
},
{
type: 'confirm',
name: 'gatekeeper_edit_apiproperties',
default: false,
message: prefix()+'Edit API properties?'+utils._getHelp('gatekeeper_edit_apiproperties')
},
{
when: function( props ) {
const r = props.gatekeeper_edit_apiproperties;
delete props.gatekeeper_edit_apiproperties;
return r;
},
type: 'editor',
name: 'gatekeeper_apiproperties',
message: utils._getHelp('gatekeeper_apiproperties')||' ',
default: utils._getDefault( 'gatekeeper_apiproperties' )
}];
},
templates: function( props ) {
return [ 'keys.properties', 'api.properties', 'cert.pem', 'key.pem' ];
}
};

View File

@@ -1,15 +0,0 @@
const chalk = require('chalk');
const name = 'traefik';
module.exports = {
name: function() {
return name;
},
prompts: function( utils ) {
return [];
},
templates: function( props ) {
return [ 'acme.json', 'traefik.toml', 'htpasswd' ];
}
};

View File

@@ -1,90 +0,0 @@
const path = require('path');
const chalk = require('chalk');
const name = 'lightning';
const capitalise = function( txt ) {
return txt.charAt(0).toUpperCase() + txt.substr(1);
};
const prefix = function() {
return chalk.green(capitalise(name)+': ');
};
const featureCondition = function(props) {
return props.features && props.features.indexOf( name ) != -1;
};
const templates = {
'lnd': [ path.join('lnd','lnd.conf') ],
'c-lightning': [ path.join('c-lightning','config'), path.join('c-lightning','bitcoin.conf') ]
};
module.exports = {
name: function() {
return name;
},
prompts: function( utils ) {
return [
/*
{
when: featureCondition,
type: 'list',
name: 'lightning_implementation',
default: utils._getDefault( 'lightning_implementation' ),
message: prefix()+'What lightning implementation do you want to use?'+utils._getHelp('lightning_implementation'),
choices: [
{
name: 'C-lightning',
value: 'c-lightning'
},
{
name: 'LND',
value: 'lnd'
}
]
},
*/
{
when: featureCondition,
type: 'input',
name: 'lightning_external_ip',
default: utils._getDefault( 'lightning_external_ip' ),
filter: utils._trimFilter,
validate: utils._ipOrFQDNValidator,
message: prefix()+'What external ip does your lightning node have?'+utils._getHelp('lightning_external_ip'),
},
{
when: featureCondition,
type: 'input',
name: 'lightning_nodename',
default: utils._getDefault( 'lightning_nodename' ),
filter: utils._trimFilter,
validate: (input)=>{
if( !input.trim() ) {
return true;
}
return utils._lightningNodeNameValidator(input);
},
message: prefix()+'What name has your lightning node?'+utils._getHelp('lightning_nodename'),
},
{
when: featureCondition,
type: 'input',
name: 'lightning_nodecolor',
default: utils._getDefault( 'lightning_nodecolor' ),
filter: utils._trimFilter,
validate: (input)=>{
if( !input.trim() ) {
return true;
}
return utils._colorValidator(input);
},
message: prefix()+'What color has your lightning node?'+utils._getHelp('lightning_nodecolor'),
}];
},
templates: function( props ) {
return templates[props.lightning_implementation]
}
};

View File

@@ -1,109 +0,0 @@
const chalk = require('chalk');
const name = 'bitcoin';
const capitalise = function( txt ) {
return txt.charAt(0).toUpperCase() + txt.substr(1);
};
const prefix = function() {
return chalk.green(capitalise(name)+': ');
};
const bitcoinExternal = function(props) {
return props.bitcoin_mode === 'external'
};
const bitcoinInternal = function(props) {
return props.bitcoin_mode === 'internal'
};
const bitcoinInternalAndPrune = function(props) {
return bitcoinInternal(props) && props.bitcoin_prune;
};
module.exports = {
name: function() {
return name;
},
prompts: function( utils ) {
return [
{
type: 'list',
name: 'bitcoin_mode',
default: utils._getDefault( 'bitcoin_mode' ),
message: prefix()+'Cyphernode will manage your bitcoin full node.'+utils._getHelp('bitcoin_mode'),
choices: [
{
name: 'Ok. That is awesome',
value: 'internal'
}
]
},
{
when: bitcoinExternal,
type: 'input',
name: 'bitcoin_node_ip',
default: utils._getDefault( 'bitcoin_node_ip' ),
filter: utils._trimFilter,
validate: utils._ipOrFQDNValidator,
message: prefix()+'What is your full node ip address?'+utils._getHelp('bitcoin_node_ip'),
},
{
type: 'input',
name: 'bitcoin_rpcuser',
default: utils._getDefault( 'bitcoin_rpcuser' ),
message: prefix()+'Name of bitcoin rpc user?'+utils._getHelp('bitcoin_rpcuser'),
filter: utils._trimFilter,
},
{
type: 'password',
name: 'bitcoin_rpcpassword',
default: utils._getDefault( 'bitcoin_rpcpassword' ),
message: prefix()+'Password of bitcoin rpc user?'+utils._getHelp('bitcoin_rpcpassword'),
filter: utils._trimFilter,
},
{
when: function(props) {
return bitcoinInternal( props ) && props.features.indexOf('lightning') === -1;
},
type: 'confirm',
name: 'bitcoin_prune',
default: utils._getDefault( 'bitcoin_prune' ),
message: prefix()+'Run bitcoin node in prune mode?'+utils._getHelp('bitcoin_prune'),
},
{
when: function(props) {
return bitcoinInternalAndPrune( props ) && props.features.indexOf('lightning') === -1;
},
type: 'input',
name: 'bitcoin_prune_size',
default: utils._getDefault( 'bitcoin_prune_size' ),
message: prefix()+'What is the maximum size of your blockchain data in megabytes?'+utils._getHelp('bitcoin_prune_size'),
validate: function( input ) {
if( ! /^\d+$/.test(input) ) {
throw new Error( "Not a number");
}
if( input < 550 ) {
throw new Error( "At least 550 is required");
}
return true;
}
}, // TODO: ask for size of prune
{
when: bitcoinInternal,
type: 'input',
name: 'bitcoin_uacomment',
default: utils._getDefault( 'bitcoin_uacomment' ),
message: prefix()+'Any UA comment?'+utils._getHelp('bitcoin_uacomment'),
filter: utils._trimFilter,
validate: (input)=> {return utils._optional(input,utils._UACommentValidator) }
}];
},
env: function( props ) {
return 'VAR0=VALUE0\nVAR1=VALUE1'
},
templates: function( props ) {
return ['bitcoin.conf']
}
};

View File

@@ -1,303 +0,0 @@
const path = require('path');
const chalk = require('chalk');
const name = 'installer';
const capitalise = function( txt ) {
return txt.charAt(0).toUpperCase() + txt.substr(1);
};
const prefix = function() {
return chalk.green(capitalise(name)+': ');
};
const installerDocker = function(props) {
return props.installer_mode === 'docker'
};
module.exports = {
name: function() {
return name;
},
prompts: function( utils ) {
return [{
type: 'list',
name: 'installer_mode',
default: utils._getDefault( 'installer_mode' ),
message: prefix()+chalk.red('Where do you want to install cyphernode?')+utils._getHelp('installer_mode'),
choices: [{
name: "Docker",
value: "docker"
}]
},
{
when: installerDocker,
type: 'list',
name: 'traefik_datapath',
default: utils._getDefault( 'traefik_datapath' ),
choices: [
{
name: utils.setupDir+"/cyphernode/traefik",
value: utils.setupDir+"/cyphernode/traefik"
},
{
name: utils.defaultDataDirBase+"/cyphernode/traefik",
value: utils.defaultDataDirBase+"/cyphernode/traefik"
},
{
name: utils.defaultDataDirBase+"/.cyphernode/traefik",
value: utils.defaultDataDirBase+"/.cyphernode/traefik"
},
{
name: utils.defaultDataDirBase+"/traefik",
value: utils.defaultDataDirBase+"/traefik"
},
{
name: "Custom path",
value: "_custom"
}
],
message: prefix()+'Where do you want to store your traefik data?'+utils._getHelp('traefik_datapath'),
},
{
when: (props)=>{ return installerDocker(props) && (props.traefik_datapath === '_custom') },
type: 'input',
name: 'traefik_datapath_custom',
default: utils._getDefault( 'traefik_datapath_custom' ),
filter: utils._trimFilter,
validate: utils._pathValidator,
message: prefix()+'Custom path for traefik data?'+utils._getHelp('traefik_datapath_custom'),
},
{
when: installerDocker,
type: 'list',
name: 'gatekeeper_datapath',
default: utils._getDefault( 'gatekeeper_datapath' ),
choices: [
{
name: utils.setupDir+"/cyphernode/gatekeeper",
value: utils.setupDir+"/cyphernode/gatekeeper"
},
{
name: utils.defaultDataDirBase+"/cyphernode/gatekeeper",
value: utils.defaultDataDirBase+"/cyphernode/gatekeeper"
},
{
name: utils.defaultDataDirBase+"/.cyphernode/gatekeeper",
value: utils.defaultDataDirBase+"/.cyphernode/gatekeeper"
},
{
name: utils.defaultDataDirBase+"/gatekeeper",
value: utils.defaultDataDirBase+"/gatekeeper"
},
{
name: "Custom path",
value: "_custom"
}
],
message: prefix()+'Where do you want to store your gatekeeper data?'+utils._getHelp('gatekeeper_datapath'),
},
{
when: (props)=>{ return installerDocker(props) && (props.gatekeeper_datapath === '_custom') },
type: 'input',
name: 'gatekeeper_datapath_custom',
default: utils._getDefault( 'gatekeeper_datapath_custom' ),
filter: utils._trimFilter,
validate: utils._pathValidator,
message: prefix()+'Custom path for gatekeeper data?'+utils._getHelp('gatekeeper_datapath_custom'),
},
{
when: installerDocker,
type: 'list',
name: 'proxy_datapath',
default: utils._getDefault( 'proxy_datapath' ),
choices: [
{
name: utils.setupDir+"/cyphernode/proxy",
value: utils.setupDir+"/cyphernode/proxy"
},
{
name: utils.defaultDataDirBase+"/cyphernode/proxy",
value: utils.defaultDataDirBase+"/cyphernode/proxy"
},
{
name: utils.defaultDataDirBase+"/.cyphernode/proxy",
value: utils.defaultDataDirBase+"/.cyphernode/proxy"
},
{
name: utils.defaultDataDirBase+"/proxy",
value: utils.defaultDataDirBase+"/proxy"
},
{
name: "Custom path",
value: "_custom"
}
],
message: prefix()+'Where do you want to store your proxy data?'+utils._getHelp('proxy_datapath'),
},
{
when: (props)=>{ return installerDocker(props) && (props.proxy_datapath === '_custom') },
type: 'input',
name: 'proxy_datapath_custom',
default: utils._getDefault( 'proxy_datapath_custom' ),
filter: utils._trimFilter,
validate: utils._pathValidator,
message: prefix()+'Custom path for your proxy data?'+utils._getHelp('proxy_datapath_custom'),
},
{
when: function(props) { return installerDocker(props) && props.bitcoin_mode === 'internal' },
type: 'list',
name: 'bitcoin_datapath',
default: utils._getDefault( 'bitcoin_datapath' ),
choices: [
{
name: utils.setupDir+"/cyphernode/bitcoin",
value: utils.setupDir+"/cyphernode/bitcoin"
},
{
name: utils.defaultDataDirBase+"/cyphernode/bitcoin",
value: utils.defaultDataDirBase+"/cyphernode/bitcoin"
},
{
name: utils.defaultDataDirBase+"/.cyphernode/bitcoin",
value: utils.defaultDataDirBase+"/.cyphernode/bitcoin"
},
{
name: utils.defaultDataDirBase+"/bitcoin",
value: utils.defaultDataDirBase+"/bitcoin"
},
{
name: "Custom path",
value: "_custom"
}
],
message: prefix()+'Where do you want to store your bitcoin full node data?'+utils._getHelp('bitcoin_datapath'),
},
{
when: function(props) { return installerDocker(props) && props.bitcoin_mode === 'internal' && props.bitcoin_datapath === '_custom' },
type: 'input',
name: 'bitcoin_datapath_custom',
default: utils._getDefault( 'bitcoin_datapath_custom' ),
filter: utils._trimFilter,
validate: utils._pathValidator,
message: prefix()+'Custom path for your bitcoin full node data?'+utils._getHelp('bitcoin_datapath_custom'),
},
{
when: function(props) { return installerDocker(props) && props.features.indexOf('lightning') !== -1 },
type: 'list',
name: 'lightning_datapath',
default: utils._getDefault( 'lightning_datapath' ),
choices: [
{
name: utils.setupDir+"/cyphernode/lightning",
value: utils.setupDir+"/cyphernode/lightning"
},
{
name: utils.defaultDataDirBase+"/cyphernode/lightning",
value: utils.defaultDataDirBase+"/cyphernode/lightning"
},
{
name: utils.defaultDataDirBase+"/.cyphernode/lightning",
value: utils.defaultDataDirBase+"/.cyphernode/lightning"
},
{
name: utils.defaultDataDirBase+"/lightning",
value: utils.defaultDataDirBase+"/lightning"
},
{
name: "Custom path",
value: "_custom"
}
],
message: prefix()+'Where do you want to store your lightning node data?'+utils._getHelp('lightning_datapath'),
},
{
when: function(props) { return installerDocker(props) && props.features.indexOf('lightning') !== -1 && props.lightning_datapath === '_custom'},
type: 'input',
name: 'lightning_datapath_custom',
default: utils._getDefault( 'lightning_datapath_custom' ),
filter: utils._trimFilter,
validate: utils._pathValidator,
message: prefix()+'Custom path for your lightning node data?'+utils._getHelp('lightning_datapath_custom'),
},
{
when: function(props) { return installerDocker(props) && props.features.indexOf('otsclient') !== -1 },
type: 'list',
name: 'otsclient_datapath',
default: utils._getDefault( 'otsclient_datapath' ),
choices: [
{
name: utils.setupDir+"/cyphernode/otsclient",
value: utils.setupDir+"/cyphernode/otsclient"
},
{
name: utils.defaultDataDirBase+"/cyphernode/otsclient",
value: utils.defaultDataDirBase+"/cyphernode/otsclient"
},
{
name: utils.defaultDataDirBase+"/.cyphernode/otsclient",
value: utils.defaultDataDirBase+"/.cyphernode/otsclient"
},
{
name: utils.defaultDataDirBase+"/otsclient",
value: utils.defaultDataDirBase+"/otsclient"
},
{
name: "Custom path",
value: "_custom"
}
],
message: prefix()+'Where do you want to store your OTS data?'+utils._getHelp('otsclient_datapath'),
},
{
when: function(props) { return installerDocker(props) && props.features.indexOf('otsclient') !== -1 && props.otsclient_datapath === '_custom' },
type: 'input',
name: 'otsclient_datapath_custom',
default: utils._getDefault( 'otsclient_datapath_custom' ),
filter: utils._trimFilter,
validate: utils._pathValidator,
message: prefix()+'Where is your otsclient data?'+utils._getHelp('otsclient_datapath_custom'),
},
{
when: function(props) { return installerDocker(props) && props.bitcoin_mode === 'internal' },
type: 'confirm',
name: 'bitcoin_expose',
default: utils._getDefault( 'bitcoin_expose' ),
message: prefix()+'Expose bitcoin full node outside of the docker network?'+utils._getHelp('bitcoin_expose'),
},
{
when: function(props) { return installerDocker(props) && props.features.indexOf('lightning') !== -1 },
type: 'confirm',
name: 'lightning_expose',
default: utils._getDefault( 'lightning_expose' ),
message: prefix()+'Expose lightning node outside of the docker network?'+utils._getHelp('lightning_expose'),
},
{
when: installerDocker,
type: 'list',
name: 'docker_mode',
default: utils._getDefault( 'docker_mode' ),
message: prefix()+'What docker mode: docker swarm or docker-compose?'+utils._getHelp('docker_mode'),
choices: [{
name: "docker swarm",
value: "swarm"
},
{
name: "docker-compose",
value: "compose"
}]
},
{
type: 'confirm',
name: 'installer_cleanup',
default: utils._getDefault( 'installer_cleanup' ),
message: prefix()+'Cleanup installer after installation?'+utils._getHelp('installer_cleanup'),
}];
},
templates: function( props ) {
if( props.installer_mode === 'docker' ) {
return ['config.sh','start.sh', 'stop.sh', 'testfeatures.sh', path.join('docker', 'docker-compose.yaml')];
}
return ['config.sh','start.sh', 'stop.sh', 'testfeatures.sh'];
}
};

View File

@@ -1,25 +0,0 @@
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         

View File

@@ -1,25 +0,0 @@
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         

View File

@@ -1,25 +0,0 @@
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         

View File

@@ -1,25 +0,0 @@
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         

View File

@@ -1,25 +0,0 @@
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         

View File

@@ -1,25 +0,0 @@
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         

View File

@@ -1,25 +0,0 @@
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         

View File

@@ -1,25 +0,0 @@
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         

View File

@@ -1,25 +0,0 @@
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         

View File

@@ -1,25 +0,0 @@
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         

View File

@@ -1,25 +0,0 @@
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         

View File

@@ -1,25 +0,0 @@
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         

View File

@@ -1,25 +0,0 @@
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         

View File

@@ -1,25 +0,0 @@
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         

View File

@@ -1,25 +0,0 @@
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         
                                         

View File

@@ -1,46 +0,0 @@
<% if (net === 'testnet') { %>
# testnet
testnet=1
<% } %>
<% if (bitcoin_prune && bitcoin_mode === 'internal') { %>
prune=<%= bitcoin_prune_size || 550 %>
<% } else { %>
txindex=1
<% } %>
zmqpubrawblock=tcp://0.0.0.0:18501
zmqpubrawtx=tcp://0.0.0.0:18502
#tor
#proxy=127.0.0.1:9050
#listen=1
maxmempool=64
dbcache=64
rpcuser=<%= bitcoin_rpcuser %>
rpcpassword=<%= bitcoin_rpcpassword %>
# ATTENTION: VERY DANGEROUS OUTSIDE THE DOCKER NETWORK
rpcallowip=0.0.0.0/0
server=1
<% if (net === 'testnet') { %>
test.wallet=watching01.dat
test.wallet=xpubwatching01.dat
test.wallet=spending01.dat
test.wallet=ln01.dat
<% } else { %>
main.wallet=watching01.dat
main.wallet=xpubwatching01.dat
main.wallet=spending01.dat
main.wallet=ln01.dat
<% } %>
walletnotify=/usr/bin/curl proxy:8888/conf/%s
blocknotify=/usr/bin/curl proxy:8888/newblock/%s
<% if ( bitcoin_uacomment != null && bitcoin_uacomment != '' ) { %>
uacomment=<%= bitcoin_uacomment %>
<% } %>

View File

@@ -1,6 +0,0 @@
# Watcher can do stuff
# Spender can do what the watcher can do plus more stuff
# Admin can do what the spender can do plus even more stuff
<%- gatekeeper_apiproperties %>

View File

@@ -1 +0,0 @@
<%- gatekeeper_sslcert %>

View File

@@ -1 +0,0 @@
<%- gatekeeper_sslkey %>

View File

@@ -1 +0,0 @@
<%- gatekeeper_keys.configEntries.join('\n') %>

View File

@@ -1,21 +0,0 @@
INSTALLER_MODE=<%= installer_mode %>
BITCOIN_INTERNAL=<%= (bitcoin_mode==="internal"?'true':'false') %>
FEATURE_LIGHTNING=<%= (features.indexOf('lightning') != -1)?'true':'false' %>
FEATURE_OTSCLIENT=<%= (features.indexOf('otsclient') != -1)?'true':'false' %>
LIGHTNING_IMPLEMENTATION=<%= lightning_implementation %>
PROXY_DATAPATH=<%= proxy_datapath %>
GATEKEEPER_DATAPATH=<%= gatekeeper_datapath %>
TRAEFIK_DATAPATH=<%= traefik_datapath %>
DOCKER_MODE=<%= docker_mode %>
RUN_AS_USER=<%= run_as_different_user?username:'' %>
CLEANUP=<%= installer_cleanup?'true':'false' %>
SHARED_HTPASSWD_PATH=<%= traefik_datapath %>/htpasswd
<% if ( features.indexOf('lightning') !== -1 && lightning_implementation === 'c-lightning' ) { %>
LIGHTNING_DATAPATH=<%= lightning_datapath %>
<% } %>
<% if ( features.indexOf('otsclient') !== -1 ) { %>
OTSCLIENT_DATAPATH=<%= otsclient_datapath %>
<% } %>
<% if ( bitcoin_mode==="internal" ) { %>
BITCOIN_DATAPATH=<%= bitcoin_datapath %>
<% } %>

View File

@@ -1,200 +0,0 @@
version: "3"
services:
gatekeeper:
# HTTP authentication API gate
environment:
- "TRACING=1"
image: cyphernode/gatekeeper:<%= gatekeeper_version %>
ports:
- "<%= gatekeeper_port %>:443"
volumes:
- "<%= gatekeeper_datapath %>/certs:/etc/ssl/certs"
- "<%= gatekeeper_datapath %>/private:/etc/ssl/private"
- "<%= gatekeeper_datapath %>/keys.properties:/etc/nginx/conf.d/keys.properties"
- "<%= gatekeeper_datapath %>/api.properties:/etc/nginx/conf.d/api.properties"
- "<%= gatekeeper_datapath %>/htpasswd:/etc/nginx/conf.d/status/htpasswd"
- "<%= gatekeeper_datapath %>/installation.json:/etc/nginx/conf.d/s/stats/installation.json"
- "<%= gatekeeper_datapath %>/client.7z:/etc/nginx/conf.d/s/stats/client.7z"
- "<%= gatekeeper_datapath %>/config.7z:/etc/nginx/conf.d/s/stats/config.7z"
command: $USER
# deploy:
# placement:
# constraints: [node.hostname==dev]
networks:
- cyphernodenet
- cyphernodeappsnet
restart: always
traefik:
image: traefik:v1.7.9-alpine
restart: always
ports:
- 80:80
- 443:443
# deploy:
# placement:
# constraints: [node.hostname==dev]
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
- "<%= traefik_datapath%>/traefik.toml:/traefik.toml"
- "<%= traefik_datapath%>/acme.json:/acme.json"
- "<%= traefik_datapath%>/htpasswd:/htpasswd/htpasswd"
networks:
- cyphernodeappsnet
proxy:
command: $USER ./startproxy.sh
# Bitcoin Mini Proxy
environment:
- "TRACING=1"
- "WATCHER_BTC_NODE_RPC_URL=<%= (bitcoin_mode === 'internal')?'bitcoin':bitcoin_node_ip %>:<%= (net === 'mainnet')?'8332':'18332' %>/wallet"
- "WATCHER_BTC_NODE_DEFAULT_WALLET=watching01.dat"
- "WATCHER_BTC_NODE_XPUB_WALLET=xpubwatching01.dat"
- "WATCHER_BTC_NODE_RPC_USER=<%= bitcoin_rpcuser %>:<%= bitcoin_rpcpassword %>"
- "WATCHER_BTC_NODE_RPC_CFG=/tmp/watcher_btcnode_curlcfg.properties"
- "SPENDER_BTC_NODE_RPC_URL=<%= (bitcoin_mode === 'internal')?'bitcoin':bitcoin_node_ip %>:<%= (net === 'mainnet')?'8332':'18332' %>/wallet"
- "SPENDER_BTC_NODE_DEFAULT_WALLET=spending01.dat"
- "SPENDER_BTC_NODE_RPC_USER=<%= bitcoin_rpcuser %>:<%= bitcoin_rpcpassword %>"
- "SPENDER_BTC_NODE_RPC_CFG=/tmp/spender_btcnode_curlcfg.properties"
- "PROXY_LISTENING_PORT=8888"
- "DB_PATH=/proxy/db"
- "DB_FILE=/proxy/db/proxydb"
- "PYCOIN_CONTAINER=pycoin:7777"
<% if ( use_xpub && xpub ) { %>
- "DERIVATION_PUB32=<%= xpub %>"
- "DERIVATION_PATH=<%= derivation_path %>"
<% } %>
- "WATCHER_BTC_NODE_PRUNED=<%= bitcoin_prune?'true':'false' %>"
- "OTSCLIENT_CONTAINER=otsclient:6666"
- "OTS_FILES=/proxy/otsfiles"
- "XPUB_DERIVATION_GAP=100"
image: cyphernode/proxy:<%= proxy_version %>
<% if ( devmode ) { %>
ports:
- "8888:8888"
<% } %>
volumes:
- "<%= proxy_datapath %>:/proxy/db"
<% if ( features.indexOf('lightning') !== -1 && lightning_implementation === 'c-lightning' ) { %>
- "<%= lightning_datapath %>:/.lightning"
<% } %>
<% if ( features.indexOf('otsclient') !== -1 ) { %>
- "<%= otsclient_datapath %>:/proxy/otsfiles"
<% } %>
# deploy:
# placement:
# constraints: [node.hostname==dev]
networks:
- cyphernodenet
restart: always
proxycron:
environment:
- "TX_CONF_URL=proxy:8888/executecallbacks"
- "OTS_URL=proxy:8888/ots_backoffice"
image: cyphernode/proxycron:<%= proxycron_version %>
# deploy:
# placement:
# constraints: [node.hostname==dev]
networks:
- cyphernodenet
restart: always
pycoin:
# Pycoin
command: $USER ./startpycoin.sh
image: cyphernode/pycoin:<%= pycoin_version %>
environment:
- "TRACING=1"
- "PYCOIN_LISTENING_PORT=7777"
<% if ( devmode ) { %>
ports:
- "7777:7777"
<% } %>
# deploy:
# placement:
# constraints: [node.hostname==dev]
networks:
- cyphernodenet
restart: always
<% if ( features.indexOf('lightning') !== -1 && lightning_implementation === 'c-lightning' ) { %>
lightning:
command: $USER lightningd
image: cyphernode/clightning:<%= lightning_version %>
<% if( lightning_expose ) { %>
ports:
- "9735:9735"
<% } %>
volumes:
- "<%= lightning_datapath %>:/.lightning"
- "<%= lightning_datapath %>/bitcoin.conf:/.bitcoin/bitcoin.conf"
# deploy:
# placement:
# constraints: [node.hostname==dev]
networks:
- cyphernodenet
restart: always
<% } %>
<% if ( features.indexOf('otsclient') !== -1 ) { %>
otsclient:
environment:
- "TRACING=1"
- "OTSCLIENT_LISTENING_PORT=6666"
image: cyphernode/otsclient:<%= otsclient_version %>
# deploy:
# placement:
# constraints: [node.hostname==dev]
volumes:
- "<%= otsclient_datapath %>:/otsfiles"
command: $USER /script/startotsclient.sh
networks:
- cyphernodenet
restart: always
<% } %>
<% if( bitcoin_mode === 'internal' ) { %>
bitcoin:
command: $USER bitcoind
image: cyphernode/bitcoin:<%= bitcoin_version %>
<% if( bitcoin_expose ) { %>
ports:
- "<%= (net === 'mainnet')?'8332:8332':'18332:18332' %>"
<% } %>
# deploy:
# placement:
# constraints: [node.hostname==dev]
volumes:
- "<%= bitcoin_datapath %>:/.bitcoin"
networks:
- cyphernodenet
restart: always
<% } %>
broker:
image: eclipse-mosquitto:1.6
# deploy:
# placement:
# constraints: [node.hostname==dev]
networks:
- cyphernodenet
restart: always
notifier:
image: cyphernode/notifier:<%= notifier_version %>
command: $USER ./startnotifier.sh
# deploy:
# placement:
# constraints: [node.hostname==dev]
networks:
- cyphernodenet
- cyphernodeappsnet
restart: always
networks:
cyphernodenet:
external: true
cyphernodeappsnet:
external: true

View File

@@ -1,130 +0,0 @@
#!/bin/sh
. ./installer/config.sh
# be aware that randomly downloaded cyphernode apps will have access to
# your configuration and filesystem.
# !!!!!!!!! DO NOT INCLUDE APPS WITHOUT REVIEW !!!!!!!!!!
# TODO: Test if we can mitigate this security issue by
# running app dockers inside a docker container
start_apps() {
local SCRIPT_NAME="start.sh"
local APP_SCRIPT_PATH
local APP_START_SCRIPT_PATH
local APP_ID
for i in $current_path/apps/*
do
APP_SCRIPT_PATH=$(echo $i)
if [ -d "$APP_SCRIPT_PATH" ] && [ ! -f "$APP_SCRIPT_PATH/ignoreThisApp" ]; then
APP_START_SCRIPT_PATH="$APP_SCRIPT_PATH/$SCRIPT_NAME"
APP_ID=$(basename $APP_SCRIPT_PATH)
if [ -f "$APP_START_SCRIPT_PATH" ]; then
. $APP_START_SCRIPT_PATH
elif [ -f "$APP_SCRIPT_PATH/docker-compose.yaml" ]; then
export SHARED_HTPASSWD_PATH
export GATEKEEPER_DATAPATH
export LIGHTNING_DATAPATH
export BITCOIN_DATAPATH
export APP_SCRIPT_PATH
export APP_ID
export DOCKER_MODE
if [ "$DOCKER_MODE" = "swarm" ]; then
docker stack deploy -c $APP_SCRIPT_PATH/docker-compose.yaml $APP_ID
elif [ "$DOCKER_MODE" = "compose" ]; then
docker-compose -f $APP_SCRIPT_PATH/docker-compose.yaml up -d --remove-orphans
fi
fi
fi
done
}
test_apps() {
local SCRIPT_NAME="test.sh"
local APP_SCRIPT_PATH
local APP_START_SCRIPT_PATH
local APP_ID
local returncode=0
for i in $current_path/apps/*
do
APP_SCRIPT_PATH=$(echo $i)
if [ -d "$APP_SCRIPT_PATH" ]; then
APP_TEST_SCRIPT_PATH="$APP_SCRIPT_PATH/$SCRIPT_NAME"
if [ -f "$APP_TEST_SCRIPT_PATH" ] && [ ! -f "$APP_SCRIPT_PATH/ignoreThisApp" ]; then
APP_ID=$(basename "$APP_SCRIPT_PATH")
printf "\r\n\e[1;36mTesting $APP_ID... \e[1;0m"
. $APP_TEST_SCRIPT_PATH
local rc=$?
if [ ""$rc -eq "0" ]; then
printf "\e[1;36m$APP_ID rocks!\e[1;0m"
fi
returncode=$(($rc | ${returncode}))
echo ""
fi
fi
done
return $returncode
}
<% if (run_as_different_user) { %>
OS=$(uname -s)
if [ "$OS" = "Darwin" ]; then
printf "\r\n\033[0;91m'Run as another user' feature is not supported on OSX. User <%= default_username %> will be used to run Cyphernode.\033[0m\r\n\r\n"
export USER=$(id -u <%= default_username %>):$(id -g <%= default_username %>)
else
export USER=$(id -u <%= username %>):$(id -g <%= username %>)
fi
<% } else { %>
export USER=$(id -u <%= default_username %>):$(id -g <%= default_username %>)
<% } %>
export ARCH=$(uname -m)
current_path="$(cd "$(dirname "$0")" >/dev/null && pwd)"
<% if (docker_mode == 'swarm') { %>
docker stack deploy -c $current_path/docker-compose.yaml cyphernode
<% } else if(docker_mode == 'compose') { %>
docker-compose -f $current_path/docker-compose.yaml up -d --remove-orphans
<% } %>
start_apps
arch=$(uname -m)
case "${arch}" in arm*)
printf "\r\n\033[1;31mSince we're on a slow RPi, let's give Docker 60 more seconds before performing our tests...\033[0m\r\n\r\n"
sleep 60
;;
esac
# Will test if Cyphernode is fully up and running...
docker run --rm -it -v $current_path/testfeatures.sh:/testfeatures.sh \
-v <%= gatekeeper_datapath %>:/gatekeeper \
-v $current_path:/dist \
--network cyphernodenet eclipse-mosquitto:1.6.2 /testfeatures.sh
if [ -f $current_path/exitStatus.sh ]; then
. $current_path/exitStatus.sh
rm -f $current_path/exitStatus.sh
fi
test_apps
EXIT_STATUS=$(($? | ${EXIT_STATUS}))
printf "\r\n\e[1;32mTests finished.\e[0m\n"
if [ "$EXIT_STATUS" -ne "0" ]; then
printf "\r\n\033[1;31mThere was an error during cyphernode installation. Please see Docker's logs for more information. Run ./stop.sh to stop cyphernode.\r\n\r\n\033[0m"
exit 1
fi
printf "\r\n\033[0;92mDepending on your current location and DNS settings, point your favorite browser to one of the following URLs to access Cyphernode's status page:\r\n"
printf "\r\n"
printf "\033[0;95m<% cns.forEach(cn => { %><%= ('https://' + cn + '/welcome\\r\\n') %><% }) %>\033[0m\r\n"
printf "\033[0;92mUse 'admin' as the username with the configuration password you selected at the beginning of the configuration process.\r\n\r\n\033[0m"

View File

@@ -1,58 +0,0 @@
#!/bin/sh
current_path="$(cd "$(dirname "$0")" >/dev/null && pwd)"
# be aware that randomly downloaded cyphernode apps will have access to
# your configuration and filesystem.
# !!!!!!!!! DO NOT INCLUDE APPS WITHOUT REVIEW !!!!!!!!!!
# TODO: Test if we can mitigate this security issue by
# running app dockers inside a docker container
stop_apps() {
local SCRIPT_NAME="stop.sh"
local APP_SCRIPT_PATH
local APP_START_SCRIPT_PATH
local APP_ID
for i in $current_path/apps/*
do
APP_SCRIPT_PATH=$(echo $i)
if [ -d "$APP_SCRIPT_PATH" ] && [ ! -f "$APP_SCRIPT_PATH/ignoreThisApp" ]; then
APP_STOP_SCRIPT_PATH="$APP_SCRIPT_PATH/$SCRIPT_NAME"
APP_ID=$(basename $APP_SCRIPT_PATH)
if [ -f "$APP_STOP_SCRIPT_PATH" ]; then
. $APP_STOP_SCRIPT_PATH
elif [ -f "$APP_SCRIPT_PATH/docker-compose.yaml" ]; then
export SHARED_HTPASSWD_PATH
export GATEKEEPER_DATAPATH
export LIGHTNING_DATAPATH
export BITCOIN_DATAPATH
export APP_SCRIPT_PATH
export APP_ID
export DOCKER_MODE
if [ "$DOCKER_MODE" = "swarm" ]; then
docker stack rm $APP_ID
elif [ "$DOCKER_MODE" = "compose" ]; then
docker-compose -f $APP_SCRIPT_PATH/docker-compose.yaml down
fi
fi
fi
done
}
. ./installer/config.sh
stop_apps
<% if (docker_mode == 'swarm') { %>
export USER=$(id -u):$(id -g)
export ARCH=$(uname -m)
docker stack rm cyphernode
<% } else if(docker_mode == 'compose') { %>
export USER=$(id -u):$(id -g)
export ARCH=$(uname -m)
docker-compose -f $current_path/docker-compose.yaml down
<% } %>

View File

@@ -1,374 +0,0 @@
#!/bin/sh
apk add --update --no-cache openssl curl jq > /dev/null
. /gatekeeper/keys.properties
checkgatekeeper() {
echo -e "\r\n\e[1;36mTesting Gatekeeper...\e[0;32m" > /dev/console
local rc
local id="001"
local k
eval k='$ukey_'$id
local h64=$(echo "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64)
# Let's test expiration: 1 second in payload, request 2 seconds later
local p64=$(echo "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+1))}" | base64)
local s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1)
local token="$h64.$p64.$s"
echo -e " Sleeping 2 seconds... " > /dev/console
sleep 2
echo " Testing expired request... " > /dev/console
rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper/v0/getblockinfo)
[ "${rc}" -ne "403" ] && return 10
# Let's test authentication (signature)
p64=$(echo "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64)
s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1)
token="$h64.$p64.a$s"
echo " Testing bad signature... " > /dev/console
rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper/v0/getblockinfo)
[ "${rc}" -ne "403" ] && return 30
# Let's test authorization (action access for groups)
token="$h64.$p64.$s"
echo " Testing watcher trying to do a spender action... " > /dev/console
rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper/v0/getbalance)
[ "${rc}" -ne "403" ] && return 40
id="002"
eval k='$ukey_'$id
p64=$(echo "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64)
s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1)
token="$h64.$p64.$s"
echo " Testing spender trying to do an internal action call... " > /dev/console
rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper/v0/conf)
[ "${rc}" -ne "403" ] && return 50
id="003"
eval k='$ukey_'$id
p64=$(echo "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64)
s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1)
token="$h64.$p64.$s"
echo " Testing admin trying to do an internal action call... " > /dev/console
rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper/v0/conf)
[ "${rc}" -ne "403" ] && return 60
echo -e "\e[1;36mGatekeeper rocks!" > /dev/console
return 0
}
checkpycoin() {
echo -en "\r\n\e[1;36mTesting Pycoin... " > /dev/console
local rc
rc=$(curl -H "Content-Type: application/json" -d "{\"pub32\":\"upub5GtUcgGed1aGH4HKQ3vMYrsmLXwmHhS1AeX33ZvDgZiyvkGhNTvGd2TA5Lr4v239Fzjj4ZY48t6wTtXUy2yRgapf37QHgt6KWEZ6bgsCLpb\",\"path\":\"0/25-30\"}" -s -o /dev/null -w "%{http_code}" http://proxy:8888/derivepubpath)
[ "${rc}" -ne "200" ] && return 100
echo -e "\e[1;36mPycoin rocks!" > /dev/console
return 0
}
checkbroker() {
echo -en "\r\n\e[1;36mTesting Broker... " > /dev/console
local rc
rc=$(mosquitto_pub -h broker -t "testtopic" -m "testbroker")
[ "$?" -ne "0" ] && return 110
echo -e "\e[1;36mBroker rocks!" > /dev/console
return 0
}
checknotifier() {
echo -en "\r\n\e[1;36mTesting Notifier... " > /dev/console
local response
local returncode
response=$(mosquitto_rr -h broker -W 5 -t notifier -e "response/$$" -m "{\"response-topic\":\"response/$$\",\"cmd\":\"web\",\"url\":\"http://proxy:8888/getbestblockhash\"}")
returncode=$?
[ "${returncode}" -ne "0" ] && return 115
http_code=$(echo "${response}" | jq ".http_code" | tr -d '"')
[ "${http_code}" -ge "400" ] && return 118
echo -e "\e[1;36mNotifier rocks!" > /dev/console
return 0
}
checkots() {
echo -en "\r\n\e[1;36mTesting OTSclient... " > /dev/console
local rc
rc=$(curl -s -H "Content-Type: application/json" -d '{"hash":"123","callbackUrl":"http://callback"}' http://proxy:8888/ots_stamp)
echo "${rc}" | grep "Invalid hash 123 for sha256" > /dev/null
[ "$?" -ne "0" ] && return 200
echo -e "\e[1;36mOTSclient rocks!" > /dev/console
return 0
}
checkbitcoinnode() {
echo -en "\r\n\e[1;36mTesting Bitcoin... " > /dev/console
local rc
rc=$(curl -s -o /dev/null -w "%{http_code}" http://proxy:8888/getbestblockhash)
[ "${rc}" -ne "200" ] && return 300
echo -e "\e[1;36mBitcoin node rocks!" > /dev/console
return 0
}
checklnnode() {
echo -en "\r\n\e[1;36mTesting Lightning... " > /dev/console
local rc
rc=$(curl -s -o /dev/null -w "%{http_code}" http://proxy:8888/ln_getinfo)
[ "${rc}" -ne "200" ] && return 400
echo -e "\e[1;36mLN node rocks!" > /dev/console
return 0
}
checkservice() {
local interval=15
local totaltime=180
local outcome
local returncode=0
local endtime=$(($(date +%s) + ${totaltime}))
local result
echo -e "\r\n\e[1;36mTesting if Cyphernode is up and running... \e[0;36mI will keep trying during up to $((${totaltime} / 60)) minutes to give time to Docker to deploy everything...\e[0;32m" > /dev/console
while :
do
outcome=0
for container in gatekeeper proxy proxycron broker notifier pycoin <%= (features.indexOf('otsclient') != -1)?'otsclient ':'' %>bitcoin <%= (features.indexOf('lightning') != -1)?'lightning ':'' %>; do
echo -e " \e[0;32mVerifying \e[0;33m${container}\e[0;32m..." > /dev/console
(ping -c 10 ${container} 2> /dev/null | grep "0% packet loss" > /dev/null) &
eval ${container}=$!
done
for container in gatekeeper proxy proxycron broker notifier pycoin <%= (features.indexOf('otsclient') != -1)?'otsclient ':'' %>bitcoin <%= (features.indexOf('lightning') != -1)?'lightning ':'' %>; do
eval wait '$'${container} ; returncode=$? ; outcome=$((${outcome} + ${returncode}))
eval c_${container}=${returncode}
done
# If '0% packet loss' everywhere or 5 minutes passed, we get out of this loop
([ "${outcome}" -eq "0" ] || [ $(date +%s) -gt ${endtime} ]) && break
echo -e "\e[1;31mCyphernode still not ready, will retry every ${interval} seconds for $((${totaltime} / 60)) minutes ($((${endtime} - $(date +%s))) seconds left)." > /dev/console
sleep ${interval}
done
# "containers": [
# { "name": "gatekeeper", "active":true },
# { "name": "proxy", "active":true },
# { "name": "proxycron", "active":true },
# { "name": "pycoin", "active":true },
# { "name": "otsclient", "active":true },
# { "name": "bitcoin", "active":true },
# { "name": "lightning", "active":true },
# ]
for container in gatekeeper proxy proxycron broker notifier pycoin <%= (features.indexOf('otsclient') != -1)?'otsclient ':'' %>bitcoin <%= (features.indexOf('lightning') != -1)?'lightning ':'' %>; do
[ -n "${result}" ] && result="${result},"
result="${result}{\"name\":\"${container}\",\"active\":"
eval "returncode=\$c_${container}"
if [ "${returncode}" -eq "0" ]; then
result="${result}true}"
else
result="${result}false}"
fi
done
result="\"containers\":[${result}]"
echo $result
return ${outcome}
}
timeout_feature() {
local interval=15
local totaltime=120
local testwhat=${1}
local returncode
local endtime=$(($(date +%s) + ${totaltime}))
while :
do
eval ${testwhat}
returncode=$?
# If no error or 2 minutes passed, we get out of this loop
([ "${returncode}" -eq "0" ] || [ $(date +%s) -gt ${endtime} ]) && break
echo -e "\e[1;31mMaybe it's too early, I'll retry every ${interval} seconds for $((${totaltime} / 60)) minutes ($((${endtime} - $(date +%s))) seconds left)." > /dev/console
sleep ${interval}
done
return ${returncode}
}
feature_status() {
local returncode=${1}
local errormsg=${2}
[ "${returncode}" -eq "0" ] && echo "true"
[ "${returncode}" -ne "0" ] && echo "false" && echo -e "\e[1;31m${errormsg}" > /dev/console
}
# /proxy/installation.json will contain something like that:
#{
# "containers": [
# { "name": "gatekeeper", "active":true },
# { "name": "proxy", "active":true },
# { "name": "proxycron", "active":true },
# { "name": "pycoin", "active":true },
# { "name": "otsclient", "active":true },
# { "name": "bitcoin", "active":true },
# { "name": "lightning", "active":true },
# ],
# "features": [
# { "name": "gatekeeper", "working":true },
# { "name": "pycoin", "working":true },
# { "name": "otsclient", "working":true },
# { "name": "bitcoin", "working":true },
# { "name": "lightning", "working":true },
# ]
#}
# Let's first see if everything is up.
echo "EXIT_STATUS=1" > /dist/exitStatus.sh
brokenproxy="false"
containers=$(checkservice)
returncode=$?
finalreturncode=${returncode}
if [ "${returncode}" -ne "0" ]; then
echo -e "\e[1;31mCyphernode could not fully start properly within delay." > /dev/console
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"proxy\") | .active")
if [ "${status}" = "false" ]; then
echo -e "\e[1;31mThe Proxy, the main Cyphernode's component, is not responding. We will only test the gatekeeper if its container is up, but you'll see errors for the other components. Please check the logs." > /dev/console
brokenproxy="true"
fi
else
echo -e "\e[1;36mCyphernode seems to be correctly deployed. Let's run more thourough tests..." > /dev/console
fi
# Let's now check each feature fonctionality...
# "features": [
# { "name": "gatekeeper", "working":true },
# { "name": "pycoin", "working":true },
# { "name": "otsclient", "working":true },
# { "name": "bitcoin", "working":true },
# { "name": "lightning", "working":true },
# ]
result="${containers},\"features\":[{\"coreFeature\":true, \"name\":\"cyphernode proxy\",\"working\":true}, {\"coreFeature\":true, \"name\":\"gatekeeper\",\"working\":"
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"gatekeeper\") | .active")
if [ "${status}" = "true" ]; then
timeout_feature checkgatekeeper
returncode=$?
else
returncode=1
fi
finalreturncode=$((${returncode} | ${finalreturncode}))
result="${result}$(feature_status ${returncode} 'Gatekeeper error!')}"
result="${result},{\"coreFeature\":true, \"name\":\"bitcoin\",\"working\":"
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"bitcoin\") | .active")
if [[ "${brokenproxy}" != "true" && "${status}" = "true" ]]; then
timeout_feature checkbitcoinnode
returncode=$?
else
returncode=1
fi
finalreturncode=$((${returncode} | ${finalreturncode}))
result="${result}$(feature_status ${returncode} 'Bitcoin error!')}"
result="${result},{\"coreFeature\":true, \"name\":\"broker\",\"working\":"
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"broker\") | .active")
if [[ "${brokenproxy}" != "true" && "${status}" = "true" ]]; then
timeout_feature checkbroker
returncode=$?
else
returncode=1
fi
finalreturncode=$((${returncode} | ${finalreturncode}))
result="${result}$(feature_status ${returncode} 'Broker error!')}"
result="${result},{\"coreFeature\":true, \"name\":\"notifier\",\"working\":"
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"notifier\") | .active")
if [[ "${brokenproxy}" != "true" && "${status}" = "true" ]]; then
timeout_feature checknotifier
returncode=$?
else
returncode=1
fi
finalreturncode=$((${returncode} | ${finalreturncode}))
result="${result}$(feature_status ${returncode} 'Notifier error!')}"
result="${result},{\"coreFeature\":true, \"name\":\"pycoin\",\"working\":"
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"pycoin\") | .active")
if [[ "${brokenproxy}" != "true" && "${status}" = "true" ]]; then
timeout_feature checkpycoin
returncode=$?
else
returncode=1
fi
finalreturncode=$((${returncode} | ${finalreturncode}))
result="${result}$(feature_status ${returncode} 'Pycoin error!')}"
<% if (features.indexOf('otsclient') != -1) { %>
result="${result},{\"coreFeature\":false, \"name\":\"otsclient\",\"working\":"
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"otsclient\") | .active")
if [[ "${brokenproxy}" != "true" && "${status}" = "true" ]]; then
timeout_feature checkots
returncode=$?
else
returncode=1
fi
finalreturncode=$((${returncode} | ${finalreturncode}))
result="${result}$(feature_status ${returncode} 'OTSclient error!')}"
<% } %>
<% if (features.indexOf('lightning') != -1) { %>
result="${result},{\"coreFeature\":false, \"name\":\"lightning\",\"working\":"
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"lightning\") | .active")
if [[ "${brokenproxy}" != "true" && "${status}" = "true" ]]; then
timeout_feature checklnnode
returncode=$?
else
returncode=1
fi
finalreturncode=$((${returncode} | ${finalreturncode}))
result="${result}$(feature_status ${returncode} 'Lightning error!')}"
<% } %>
result="{${result}]}"
echo "${result}" > /gatekeeper/installation.json
echo "EXIT_STATUS=${finalreturncode}" > /dist/exitStatus.sh

View File

@@ -1,5 +0,0 @@
# How to create the hmac for the cookie file:
```
# echo -n "access-key" | openssl dgst -hmac "cyphernode:sparkwallet" -sha256 -binary | base64 | sed 's/[\+\W]//g'
```

View File

@@ -1,8 +0,0 @@
<% if (net === 'testnet') { %>
# testnet
testnet=1
<% } %>
rpcconnect=<%= (bitcoin_mode === 'internal')?'bitcoin':bitcoin_node_ip %>
rpcuser=<%= bitcoin_rpcuser %>
rpcpassword=<%= bitcoin_rpcpassword %>

View File

@@ -1,18 +0,0 @@
<% if (net === 'testnet') { %>
# testnet
network=testnet
<% } else if (net === 'mainnet') { %>
network=bitcoin
<% } %>
<% if( lightning_nodename ) { %>
alias=<%= lightning_nodename %>
<% } %>
<% if( lightning_nodecolor ) { %>
rgb=<%= lightning_nodecolor %>
<% } %>
bitcoin-rpcconnect=<%= (bitcoin_mode === 'internal')?'bitcoin':bitcoin_node_ip %>
bitcoin-rpcuser=<%= bitcoin_rpcuser %>
bitcoin-rpcpassword=<%= bitcoin_rpcpassword %>
addr=0.0.0.0:9735
announce-addr=<%= lightning_external_ip %>:9735

View File

@@ -1 +0,0 @@
cyphernode:sparkwallet:FoeDdQw5yl7pPfqdlGy3OEk/txGqyJjSbVtffhzs7kc=

View File

@@ -1,27 +0,0 @@
[Application Options]
debuglevel=info
maxpendingchannels=10
externalip=88.198.55.131
color=#a111ff
alias=SatoshiPortal01
rpclisten=0.0.0.0:10009
tlsextraip=lnd
tlsextradomain=lnd
[Bitcoin]
bitcoin.active=1
bitcoin.node=bitcoind
bitcoin.mainnet=1
[Bitcoind]
bitcoind.rpcuser=<%= bitcoin_rpcuser %>
bitcoind.rpcpass=<%= bitcoin_rpcpassword %>
bitcoind.zmqpubrawblock=tcp://bitcoin:18501
bitcoind.zmqpubrawtx=tcp://bitcoin:18502
#bitcoind.zmqpath=tcp://bitcoin:18501
bitcoind.rpchost=bitcoin
[autopilot]
autopilot.active=1
autopilot.maxchannels=5
autopilot.allocation=0.6

View File

@@ -1 +0,0 @@
admin:<%- initial_admin_password %>

View File

@@ -1,31 +0,0 @@
debug = false
logLevel = "ERROR"
defaultEntryPoints = ["https","http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.http.redirect]
entryPoint = "https"
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
[retry]
[docker]
endpoint = "unix:///var/run/docker.sock"
domain = "cyphernode.localhost"
watch = true
exposedByDefault = false
[acme]
email = "letsencrypt@yourdomain.com"
storage = "acme.json"
entryPoint = "https"
onHostRule = true
[acme.httpChallenge]
entryPoint = "http"
[[acme.domains]]
main = "cyphernode.yourdomain.com"

View File

@@ -1,34 +0,0 @@
{
"name": "generator-cyphernode",
"version": "0.0.0",
"description": "",
"homepage": "",
"author": {
"name": "jash",
"email": "jash@schulterklopfer-productions.de",
"url": ""
},
"files": [
"generators"
],
"main": "generators/index.js",
"keywords": [
"cyphernode",
"yeoman-generator"
],
"engines": {
"npm": ">= 4.0.0"
},
"dependencies": {
"@rauschma/stringio": "^1.4.0",
"chalk": "^2.1.0",
"coinstring": "^2.3.0",
"parse5": "^5.1.0",
"validator": "^10.8.0",
"wrap-ansi": "^4.0.0",
"yeoman-environment": "2.3.3",
"yeoman-generator": "2.0.5"
},
"repository": "git@github.com:schulterklopfer/cyphernode.git",
"license": "MIT"
}