Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
40a45e3
WIP for update
mdp Oct 30, 2019
ac10529
Gulp breaking, not sure about server, but it starts
mdp Oct 30, 2019
cf4bf2e
working'ish. Needs gulp help
mdp Nov 4, 2019
8d66704
Update to get everything working
mdp Nov 11, 2019
ec269f5
Remove unneeded deps
mdp Nov 11, 2019
df51e1a
Update everything to 13.1
mdp Nov 11, 2019
9df0a5e
Remove frontend dist code
mdp Nov 11, 2019
847c0a9
Don't check in compile artifacts:
mdp Nov 11, 2019
b1058cd
Final cleanup
mdp Nov 11, 2019
f416fbe
WIP, making more generic
mdp Nov 13, 2019
9db7a22
Boiling it down more
mdp Nov 14, 2019
8086d66
Mostly working
mdp Nov 14, 2019
f71a288
Seperate dev env, and fix HMAC_KEY
mdp Nov 15, 2019
5bf3e5f
More simplification
mdp Nov 16, 2019
7042709
Update instructions
mdp Nov 18, 2019
4352f71
Cleanup docker and setings. Fix domain on frontend
mdp Nov 20, 2019
a91dc2c
Move to OpenPGP.org keyserver, SKS is crippled
mdp Nov 27, 2019
96aa501
SRI hashes on scripts and styles
mdp Nov 28, 2019
641affb
Rev control dist for auditing purposes
mdp Nov 28, 2019
ffdd779
Finalize v2.0.0-beta1
mdp Nov 29, 2019
f40dc86
Copy and link fixes
mdp Nov 29, 2019
b50481c
Copy changes
mdp Nov 30, 2019
abf31de
Move .env to .env.sample
mdp Nov 30, 2019
365787d
connect self
mdp Nov 30, 2019
25af09b
Add in padlock svg
mdp Nov 30, 2019
74c13b5
Fix view error on padlock svg
mdp Nov 30, 2019
3fdb230
Moving towards reproduible builds
mdp Dec 1, 2019
e65d082
Remove dist
mdp Dec 1, 2019
130da70
ESLint and verifiable builds
mdp Dec 2, 2019
661542f
Fix version and hash
mdp Dec 3, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.git
*Dockerfile*
*docker-compose*
node_modules
15 changes: 15 additions & 0 deletions .env.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#Server vars
PGP_PASSPHRASE=MY_VERY_COMPLEX_PGP_PASPHRASE
HMAC_KEY=MY_VERY_COMPLEX_HMAC_KEY
SMTP_HOST=smtp
SMTP_PORT=25
DOMAIN=tipbox.site
IDENTITY="Tipbox <tips@tipbox.site>"
NODE_ENV=development

# If you need to override the default https url (eg. Tor)
#ORIGIN=http://tipbox123456789.onion

# STMP Vars
## The domain to send mail from
MAILNAME=tipbox.site
15 changes: 15 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#Server vars
PGP_PASSPHRASE=MY_VERY_COMPLEX_PGP_PASPHRASE
HMAC_KEY=MY_VERY_COMPLEX_HMAC_KEY
SMTP_HOST=smtp
SMTP_PORT=25
DOMAIN=tipbox.site
IDENTITY="Tipbox <tips@tipbox.site>"
NODE_ENV=production

# If you need to override the default https url (eg. Tor)
#ORIGIN=http://tipbox123456789.onion

# STMP Vars
## The domain to send mail from
MAILNAME=tipbox.site
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/node_modules
frontend/src/js/*.compiled.js
19 changes: 19 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"env": {
"browser": true,
"commonjs": true,
"es6": true
},
"extends": [
"standard"
],
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
}
}
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
node_modules

.env

*.js.map
*.compiled.js

frontend/src/css
frontend/dist
data/caddy/*
data/keys/*
data/*.cnt

!data/caddy/.gitkeep
!data/keys/.gitkeep
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
language: node_js
node_js:
- 13
11 changes: 11 additions & 0 deletions Caddyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{$DOMAIN} {
root /root
gzip
proxy / server:3000 {
transparent
}
log {
except /
}
}

10 changes: 10 additions & 0 deletions DOCKER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Steps to use Docker/docker-compose to run your own tipbox

1. Edit .env and update with your own variables
1. Build the image and create a new PGP key
- docker-compose build server
- docker-compose run --rm server node ./server/utils/keygen.js
- docker-compose run --rm server yarn build
1. Run all the services
- `docker-compose up -d`

8 changes: 8 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
FROM node:13.1-stretch

RUN mkdir /app
WORKDIR /app
COPY . /app
RUN yarn install

CMD [ "yarn", "start" ]
103 changes: 80 additions & 23 deletions Gulpfile.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
var gulp = require('gulp'),
crypto = require('crypto'),
fs = require('fs'),
packageJSON = require(__dirname + '/package.json')
gutil = require('gulp-util'),
source = require('vinyl-source-stream'),
buffer = require('vinyl-buffer'),
watchify = require('watchify'),
browserify = require('browserify'),
minifyCSS = require('gulp-minify-css'),
uncss = require('gulp-uncss'),
cleanCSS = require('gulp-clean-css');
mold = require('mold-source-map'),
uglify = require('gulp-uglify'),
rename = require("gulp-rename"),
sourcemaps = require('gulp-sourcemaps'),
less = require('gulp-less'),
replace = require('gulp-replace'),
path = require('path'),
sriHash = require('gulp-sri-hash'),
LessPluginAutoPrefix = require('less-plugin-autoprefix');

var filesToCopy = ['frontend/src/*.html', 'frontend/src/img/**/*', 'frontend/src/videos/**/*', 'frontend/src/fonts/**/*', 'frontend/src/js/*.compiled.js', 'frontend/src/js/*.js.map', 'frontend/src/favicon.ico'];
var version = packageJSON.version;

var filesToCopy = ['frontend/src/index.html', 'frontend/src/img/**/*', 'frontend/src/videos/**/*', 'frontend/src/fonts/**/*', 'frontend/src/js/*.compiled.js', 'frontend/src/js/*.js.map', 'frontend/src/favicon.ico'];

var TipboxAppBundler = browserify({
entries: ['./frontend/src/js/tipbox.js'],
Expand All @@ -27,15 +32,16 @@ var NavigationBundler = browserify({
});

var TipboxAppBundle = function() {
return TipboxAppBundler.bundle()
return TipboxAppBundler
.ignore('sodium')
.bundle()
.on('error', gutil.log.bind(gutil, 'Browserify Error'))
.pipe(source('tipbox.compiled.js'))
.pipe(buffer())
.pipe(sourcemaps.init({
loadMaps: true
}))
// Add transformation tasks to the pipeline here.
.pipe(uglify())
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest('frontend/src/js/'))
.on('end', function() {
Expand All @@ -52,7 +58,6 @@ var NavigationBundle = function() {
loadMaps: true
}))
// Add transformation tasks to the pipeline here.
.pipe(uglify())
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest('frontend/src/js/'))
.on('end', function() {
Expand Down Expand Up @@ -81,7 +86,7 @@ gulp.task('less', function(done) {
});

gulp.task('watch', function() {
gulp.watch('frontend/src/less/*.less', ['less']);
gulp.watch('frontend/src/less/*.less', gulp.series('less'));
gulp.watch('frontend/src/**/*.js', function(diff) {
// avoid infinite loop with browserify changing a .js file
if (diff.path.match(/navigation.js/)) {
Expand All @@ -95,24 +100,76 @@ gulp.task('watch', function() {
});
});

gulp.task('copy',['compile'], function() {
gulp.src(filesToCopy, {
base: './frontend/src/'
})
.pipe(gulp.dest('frontend/dist'));
gulp.task('sri', () => {
return gulp.src('frontend/dist/index.html')
// do not modify contents of any referenced css- and js-files after this task...
.pipe(sriHash())
// ... manipulating html files further, is perfectly fine
.pipe(gulp.dest('frontend/dist/'));
});

gulp.task('version', () => {
console.log("Updating version to ", version);
return gulp.src('frontend/dist/index.html')
// do not modify contents of any referenced css- and js-files after this task...
.pipe(replace('$VERSION$', version))
// ... manipulating html files further, is perfectly fine
.pipe(gulp.dest('frontend/dist/'));
});

gulp.task('minify-css', ['less'], function() {
gulp.task('minify-css', gulp.series('less', function() {
return gulp.src('frontend/src/css/tipbox.css')
.pipe(uncss({
html: ['./frontend/src/index.html'],
ignore: [/\.selected/, /\.active/, /\.encrypted/, /\.slideout-menu/, /\.slideout-open/, /\.slideout-panel/, /\.text-page/, /\.donation-page/, /\.transaction-page/]
}))
.pipe(minifyCSS())
.pipe(cleanCSS())
// .pipe(rename({ extname: '.min.css' }))
.pipe(gulp.dest('frontend/dist/css/'));
});
}));

gulp.task('default', gulp.series('watch', 'less'));
gulp.task('compile', gulp.series('browserify-app', 'browserify-nav', 'minify-css'));

// Output the sha256 hash of the final index.html along with the version

gulp.task('addendum', () => {
return new Promise(function(resolve, reject) {
var hash = null
var algorithm = 'sha256'
, shasum = crypto.createHash(algorithm)

// Updating shasum with file content
var filename = __dirname + "/frontend/dist/index.html"
, s = fs.ReadStream(filename)
s.on('data', function (data) {
shasum.update(data)
})

// making digest
s.on('end', function () {
hash = shasum.digest('hex')
console.log("SHA256 for index.html@" + version + " - " + hash)
resolve()
})
});
})

gulp.task('copyKey', () => {
return new Promise(function(resolve, reject) {
var serverKeyFile = './data/keys/public.key.json'
if (process.env["SERVER_PUBLIC_KEY"]) {
serverKeyFile = process.env["SERVER_PUBLIC_KEY"]
}
fs.copyFile(serverKeyFile, 'frontend/src/js/public.key.json', (err) => {
if (err) throw reject(err)
console.log('Using ', serverKeyFile, 'as public key.');
resolve()
});
})
})

gulp.task('copy', gulp.series('copyKey', 'compile', function() {
return gulp.src(filesToCopy, {
base: './frontend/src/'
})
.pipe(gulp.dest('frontend/dist'));
}));

gulp.task('default', ['watch', 'less']);
gulp.task('compile', ['minify-css', 'browserify-app', 'browserify-nav']);
gulp.task('build', ['copy']);
gulp.task('build', gulp.series('copy', 'version', 'sri', 'addendum'));
26 changes: 26 additions & 0 deletions NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Attempting to upgrade

`docker build . -t tipbox/server`

Run yarn install to grab the latest node modules
`docker run --rm -it -v $(pwd):/app tipbox/server yarn install`

Run the build process
`docker run --rm -it -v $(pwd):/app tipbox/server yarn run build`

Generate Keys
`docker run --rm -it -v $(pwd):/app tipbox/server bash -c 'PASSPHRASE=1234 IDENTITY="<tips@tipbox.in>" node ./server/utils/keygen.js'`

Run the server
`docker run --rm -it -p 4000:3000 -e PASSPHRASE=1234 -e HOST=tipbox.d.mdp.im -e PORT=4000 -v $(pwd):/app tipbox/server yarn start`


## What's not working

- Gulp process needs to be updated
- I've stripped out anything that didn't work (uglify, uncss), so I'm sure I've broken something
- Keybase doesn't seem to play nicely with browserify(Libsodium issue)
- https://github.com/keybase/node-nacl/blob/master/CHANGELOG.md#v110-2019-02-18
- I added 'ignore' to Gulp, but haven't tested the encryption


53 changes: 27 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,53 +10,54 @@ Try it at https://tipbox.is
- Nothing to install for the tipsters, they just need to open the unique URL generated at the creation of the Tipbox.
- Support for photo/document upload.
- Stateless, no logs in production (no information is ever saved).
- Unique information about the Tipbox is in the hash of the URL so that no one can tell who opened a particular Tipbox by monitoring the network traffic.
- PGP encryption between the frontend and the backend so that men-in-the-middle can't read the content of the requests sent to the backend.
- Support for End-To-End encryption with PGP.
- Automatically fetches the PGP key of the recipient from public key servers at the creation of the tipbox if one exists (you need to manually verify and select it to avoid spoofing).
- PGP encryption between the frontend and the backend so reduce the risk of leaking information to passive man-in-the-middle.
- Automatically fetches the PGP key of the recipient from public key server (fingerprint is included in the URL to prevent alteration).

## Disclaimer
*This is open source software, use at your own risk.*

There is always a tradeoff between ease of use and security (that's why you don't live in a bunker). By not requiring your potential sources to install an app, there is a risk that a hacker could tamper with the files served to them to include a key logger. Depending on your threat model, this may or may not matter. It’s all about finding the appropriate tool for the job.
There is always a tradeoff between ease of use and security. By not requiring your potential sources to install an app, there is a greater risk of exposure. Depending on your threat model, this may or may not matter. It’s all about finding the appropriate tool for the job.
[Read more about the security of Tipbox](https://tipbox.is#security).

## How install
## Installation and development

### Setting up the keys for testing
### Setup for production

PASSPHRASE=1234 IDENTITY="<tips@tipbox.in>" node ./server/utils/keygen.js
# Will generate private and public keys under the 'keys' directory

### Running the server with the keys

PGP_PASSPHRASE=1234 npm start
We use Docker and docker-compose to run run the entire stack (HTTPS with certs from LetsEncrypt, Express/Node Server, SMTP Server) with a minimal setup

1. Start by altering .env with your settings
- You'll need to pick a passphrase for your local PGP key and an HMAC key for validating the URLs
1. Build the image and create a new PGP key
- `docker-compose build server`
- Run the keygen script to create your keys, `docker-compose run --rm server node ./server/utils/keygen.js`. This will be saved in `/data/keys`
- `docker-compose run --rm server yarn build`
1. Run the server. Ensure you have the proper DNS records pointing domain you selected in .env to the server you're running this on. Caddy will automatically generated and confirm the domain SSL certificate from LetsEncrypt using this DNS record.
`docker-compose up -d`

### Locally for development:

After cloning this repo, simply run

npm install --dev
npm run dev
docker-compose -f docker-compose.dev.yml build
docker-compose -f docker-compose.dev.yml node ./server/utils/keygen.js
docker-compose -f docker-compose.dev.yml up

Visit http://localhost:3000 in order to view the development version of the site.

This will serve the static files from `frontend/src` and watch for any change.
When any file in `frontend/src/less/` or `frontend/src/js/*` changes, `gulp` will run the `less` and the `browserify` tasks.

# Validating the version of Tipbox being served

### In production mode

npm install;
npm run build;
NODE_ENV=tipbox.is npm start;

This will serve the static files from `frontend/dist`.

## Generating an invitation URL
During the private beta, an invitation code is required to create a tipbox.
You can generate one with the following command line:
In order to validate that the version of Tipbox has not been altered, it's possible to compare the sha256 hash of index.html with the one checked into this repository. All external script and style assets utilize [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) hashes, meaning that any changes to the underlying Javascript or CSS will result in a different cryptographic hash for the page.

NODE_ENV=production PGP_PASSPHRASE=1234 HMAC_KEY=[HMAC_KEY of the server] node invite.js [email address]
1. Find the version of the page being served. It will be listed in the footer, at the very bottom of the page.
1. Get the hash of the current page being served. For example, if you want to check the version on https://tipbox.is, run the following
`curl https://tipbox.is | openssl dgst -sha256`
1. Compare this hash to the hash listed in VERSION.md available in this repository.
1. Alternatively, you can also compute the hash yourself with
`curl https://raw.githubusercontent.com/xdamman/tipbox/vVERSION/frontend/dist/index.html | openssl dgst -sha256` where VERSION is the version of the page your checking against.

## Frontend tests with Nightwatch

Expand Down
16 changes: 16 additions & 0 deletions VERSION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# v2.0.0-beta5

- Moved entire stack to Docker in order to make it easier to build and run the project
- Updated all the NPM modules, moved to Yarn to help provide a lockfile for future-proofing future builds.
- Move from SKS to keys.openpgp.org. SKS servers suffered an attack which left many user with a large number of fake keys. It's also fairly unreliable or slow.
- Reproducible and verifiable builds
- Subresource integrety on all style and script assets
- SHA256 of frontend/dist/index.html listed in VERSION.md for comparison and auditing
- Users can build with Docker and get the same hash if they have the server public key
- Removed any external scripts, and ability to load anything inline or external to the current domain via CSP
- ESLint everything
- Minor bugfixes and website updates

### Hashes:
- tipbox.is: b21747edac9acc2fb7daaf414cf0c3dc1b5645fac73fdfa0852d95032942ac96

Loading