Added support for encrypted 7z config archive files

This commit is contained in:
jash
2018-10-20 19:15:16 +02:00
committed by kexkey
parent 026603cfcb
commit c0d439da8d
4 changed files with 173 additions and 18 deletions

View File

@@ -1,6 +1,6 @@
FROM node:alpine
RUN apk add --update bash su-exec && rm -rf /var/cache/apk/*
RUN apk add --update bash su-exec p7zip && rm -rf /var/cache/apk/*
RUN mkdir -p /app
RUN mkdir /.config
RUN chmod a+rwx /.config

View File

@@ -7,6 +7,7 @@ const validator = require('validator');
const path = require("path");
const featureChoices = require(path.join(__dirname, "features.json"));
const coinstring = require('coinstring');
const Archive = require('./lib/archive.js');
const uaCommentRegexp = /^[a-zA-Z0-9 \.,:_\-\?\/@]+$/; // TODO: look for spec of unsafe chars
const userRegexp = /^[a-zA-Z0-9\._\-]+$/;
@@ -73,28 +74,87 @@ module.exports = class extends Generator {
this.recreate = true;
}
if( fs.existsSync(this.destinationPath('config.json')) ) {
this.props = require(this.destinationPath('config.json'));
} else {
this.props = {};
}
this.props.derivation_path = this.props.derivation_path || '0/n';
this.props.installer = this.props.installer ||  'docker';
this.props.devmode = this.props.devmode || false;
this.props.devregistry = this.props.devregistry || false;
this.props.devmode = this.props.devmode || false;
this.props.username = this.props.username || 'cyphernode';
this.featureChoices = featureChoices;
for( let c of this.featureChoices ) {
c.checked = this._isChecked( 'features', c.value );
}
}
async _initConfig() {
if( fs.existsSync(this.destinationPath('config.7z')) ) {
let r = {};
while( !r.password ) {
r = await this.prompt([{
type: 'password',
name: 'password',
message: 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);
} catch( err ) {
console.log(chalk.bold.red('config archive is corrupt.'));
process.exit(1);
}
this._assignConfigDefaults(this.props);
for( let c of this.featureChoices ) {
c.checked = this._isChecked( 'features', c.value );
}
} else {
let r = {};
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: chalk.bold.blue('Choose your configuration password'),
filter: this._trimFilter
},
{
type: 'password',
name: 'password1',
message: chalk.bold.blue('Confirm your configuration password'),
filter: this._trimFilter
}]);
}
this.configurationPassword = r.password0;
this.props = {};
this._assignConfigDefaults(this.props);
console.log(chalk.bold.green('Password is set'));
}
}
async prompting() {
process.stdout.write(reset);
await this._initConfig();
await sleep(1000);
await splash();
if( this.recreate ) {
@@ -113,7 +173,14 @@ module.exports = class extends Generator {
}
writing() {
fs.writeFileSync(this.destinationPath('config.json'), JSON.stringify(this.props, null, 2));
const configJsonString = JSON.stringify(this.props);
const archive = new Archive( this.destinationPath('config.7z'), this.configurationPassword );
if( archive.writeEntry( 'config.json', configJsonString ) ) {
console.log(chalk.bold.green( 'config archive was written' ));
} else {
console.log(chalk.bold.red( 'error! config archive was not written' ));
}
for( let m of prompters ) {
const name = m.name();
@@ -132,6 +199,16 @@ module.exports = class extends Generator {
}
/* some utils */
_assignConfigDefaults( props ) {
props.derivation_path = this.props.derivation_path || '0/n';
props.installer = this.props.installer ||  'docker';
props.devmode = this.props.devmode || false;
props.devregistry = this.props.devregistry || false;
props.devmode = this.props.devmode || false;
props.username = this.props.username || 'cyphernode';
}
_isChecked( name, value ) {
return this.props && this.props[name] && this.props[name].indexOf(value) != -1 ;
}

View File

@@ -0,0 +1,77 @@
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

@@ -20,6 +20,7 @@
"npm": ">= 4.0.0"
},
"dependencies": {
"@rauschma/stringio": "^1.4.0",
"chalk": "^2.1.0",
"coinstring": "^2.3.0",
"validator": "^10.8.0",