Merge branch 'master' into integrate-new-blocks

This commit is contained in:
Kevin Froman 2021-01-24 18:11:42 +00:00
commit 003718db04
12 changed files with 194 additions and 70 deletions

View File

@ -1,3 +1,6 @@
onionr/data/**/* onionr/data/**/*
onionr/data onionr/data
MY-RUN.sh MY-RUN.sh
Dockerfile
.dockerignore
.git

View File

@ -1,12 +1,12 @@
FROM python:3.7 FROM python:3.7
EXPOSE 8080
USER root USER root
RUN mkdir /app RUN mkdir /app
WORKDIR /app WORKDIR /app
ENV PORT=8080 ENV ONIONR_DOCKER=true
EXPOSE 8080
#Install needed packages #Install needed packages
RUN apt-get update && apt-get install -y tor locales RUN apt-get update && apt-get install -y tor locales
@ -26,5 +26,6 @@ VOLUME /app/data/
#Default to running as nonprivileged user #Default to running as nonprivileged user
RUN chmod g=u -R /app RUN chmod g=u -R /app
USER 1000 USER 1000
ENV HOME=/app
CMD ["bash", "./onionr.sh"] CMD ["bash", "./run-onionr-node.sh"]

View File

@ -0,0 +1,9 @@
# Interesting papers related to Onionr development
A paper being listed here is not end-all-be-all endorsement of every detail inside.
* [Epidemic Routing for Partially-Connected Ad Hoc Networks](https://web.archive.org/web/20200208074703/http://issg.cs.duke.edu/epidemic/epidemic.pdf)
* [Freenet: A distibuted decentralized information storage and retrieval system](https://freenetproject.org/assets/papers/ddisrs.pdf)
* [Protecting Free Expression Online with Freenet](https://freenetproject.org/assets/papers/ddisrs.pdf)
* [Bitmessage: A PeertoPeer Message Authentication and Delivery System](https://archive.org/details/BitmessageWhitepaper/)
* [MuON: Epidemic based Mutual Anonymity](https://web.archive.org/web/20060901153544/http://www.csl.mtu.edu/cs6461/www/Reading/MuON_ICNP2005.pdf)

28
docs/docker.md Normal file
View File

@ -0,0 +1,28 @@
# Running Onionr in Docker
A Dockerfile is included in the root directory of Onionr.
In Docker version 20.10 (and probably others), there is a strange bug where Onionr must be run with -it or stdout will be garbled and it may hang.
## Clone and build the image
`$ git clone https://git.voidnet.tech/kev/onionr/`
`$ cd onionr`
`$ sudo docker build -t onionr .`
## Run Onionr
`$ sudo docker run -it -p 8080:8080 onionr`
Onionr will be accessible over any network interface by default, so make sure to either change the entry point bind-address argument or set a firewall rule.
That said, Onionr does protect it's interface by default with a web token, which will be shown in stdout.
**However, anyone who can access the port may be able to see what Onionr sites you have saved and potentially deanonymize your node**
## View the UI
Visit the address and port for the machine Onionr is running on, for example: http://192.168.1.5:8080/#<long-token-taken-from-stdout>
If you want a secure connection to the interface, either use a proxy such as nginx or caddy, or use [SSH tunneling](./vps-cloud-guide.md).

18
docs/vps-cloud-guide.md Normal file
View File

@ -0,0 +1,18 @@
# Cloud/Server Hosting Onionr
Cloud hosting is not the recommended way to run Onionr, as it gives the cloud provider control over your data.
That said, it is quite useful and by running a 24/7 Onionr node you contribute to the network's health.
![https://cock.li/img/cockbox.png](https://cockbox.org/?r=12)
[Cockbox](https://cockbox.org/?r=12) is the recommended provider, as they do not demand personal information and accept Monero. This is an affiliate link, which gives us a commission at no expense or complication to you.
1. [Install Onionr like normal](./basic-onionr-user-guide.pdf) or [from Docker](./docker.md)
2. Run with specific host and port to bind to: `$ ./run-onionr-node.py --bind-address 0.0.0.0 --port 8080`
3. Get the web security token: `$ ./onionr.sh get-web` (the long string at the end of the URL)
4. [Configure Firefox for SSH tunneling](https://web.archive.org/web/20210123034529/https://gist.github.com/brentjanderson/6ed800376e53746d2d28ba7b6bdcdc12). `Set network.proxy.allow_hijacking_localhost` to true in about:config
Disable random host binding in config (general.security.random_bind_ip = false) if you have Onionr auto start. You may also want to disable deniable block inserts.

View File

@ -9,10 +9,8 @@ from subprocess import DEVNULL
import ujson import ujson
from psutil import Popen from psutil import Popen
from psutil import Process from psutil import Process
import psutil
import sys import sys
import curses
script_dir = os.path.dirname(os.path.realpath(__file__)) script_dir = os.path.dirname(os.path.realpath(__file__))
sys.path.append(script_dir + '/src/') sys.path.append(script_dir + '/src/')
@ -23,36 +21,6 @@ from etc import onionrvalues
sub_script = script_dir + '/' + onionrvalues.SCRIPT_NAME sub_script = script_dir + '/' + onionrvalues.SCRIPT_NAME
def show_info(p: Process):
def pbar(window):
window.addstr(8, 10, "Onionr statistics")
window.addstr(9, 10, "-" * 17)
curses.curs_set(0)
while True:
threads = p.num_threads()
open_files = len(p.open_files())
cpu_percent = p.cpu_percent()
block_count = len(blockmetadb.get_block_list())
for proc in p.children(recursive=True):
threads += proc.num_threads()
cpu_percent += proc.cpu_percent()
try:
open_files += len(proc.open_files())
except psutil.AccessDenied:
pass
cpu_percent = cpu_percent * 100
window.addstr(11, 10, f"Threads: {threads}")
window.addstr(10, 10, f"Open files: {open_files}")
window.addstr(12, 10, f"CPU: {cpu_percent}%")
window.addstr(13, 10, f"Blocks: {block_count}")
window.refresh()
sleep(0.5)
sleep(15)
curses.wrapper(pbar)
while True:
sleep(1)
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument( parser.add_argument(
@ -111,8 +79,9 @@ parser.add_argument(
args = parser.parse_args() args = parser.parse_args()
p = Popen([sub_script, 'version'], stdout=DEVNULL) p = Popen([sub_script, 'version'])
p.wait() p.wait()
print("Configuring Onionr before starting daemon")
from filepaths import config_file, keys_file from filepaths import config_file, keys_file
from coredb import blockmetadb from coredb import blockmetadb
import onionrcrypto import onionrcrypto
@ -172,13 +141,10 @@ with open(config_file, 'w') as cf:
cf.write(ujson.dumps(config, reject_bytes=False)) cf.write(ujson.dumps(config, reject_bytes=False))
if args.open_ui: if args.open_ui:
p = Popen([sub_script, 'start'], stdout=DEVNULL) p = Popen([sub_script, 'start'])
sleep(2) sleep(2)
Popen([sub_script, 'openhome'], stdout=DEVNULL) Popen([sub_script, 'openhome'])
else: else:
p = Popen([sub_script, 'start'], stdout=DEVNULL) p = Popen([sub_script, 'start'])
p = p.children()[0]
if args.show_stats:
Thread(target=show_info, args=[p], daemon=True).start()
p.wait() p.wait()

51
run-onionr-node.sh Executable file
View File

@ -0,0 +1,51 @@
#!/bin/sh
set -x
ORIG_ONIONR_RUN_DIR=`pwd`
export ORIG_ONIONR_RUN_DIR
cd "$(dirname "$0")"
if [[ -n "$ONIONR_DOCKER" ]]; then
[[ -f "/privkey" ]] && privkey_opt="--private-key /privkey"
[[ -n "$ONIONR_ONBOARDING" ]] || ONIONR_ONBOARDING=0
[[ -n "$ONIONR_OPEN_UI" ]] || ONIONR_OPEN_UI=0
[[ -n "$ONIONR_RANDOM_LOCALHOST_IP" ]] || ONIONR_RANDOM_LOCALHOST_IP=0
[[ -n "$ONIONR_BIND_ADDRESS" ]] || ONIONR_BIND_ADDRESS=0.0.0.0
[[ -n "$ONIONR_PORT" ]] || ONIONR_PORT=8080
fi
[[ -n "$ONIONR_PRIVATE_KEY_FILE" ]] && privkey_opt="--private-key $ONIONR_PRIVATE_KEY_FILE"
[[ -n "$ONIONR_USE_BOOTSTRAP_FILE" ]] && bootstrap_opt="--use-bootstrap-file $ONIONR_USE_BOOTSTRAP_FILE"
[[ -n "$ONIONR_SHOW_STATS" ]] && show_stats_opt="--show-stats $ONIONR_SHOW_STATS"
[[ -n "$ONIONR_ONBOARDING" ]] && onboarding_opt="--onboarding $ONIONR_ONBOARDING"
[[ -n "$ONIONR_SECURITY_LEVEL" ]] && security_level_opt="--security-level $ONIONR_SECURITY_LEVEL"
[[ -n "$ONIONR_OPEN_UI" ]] && open_ui_opt="--open-ui $ONIONR_OPEN_UI"
[[ -n "$ONIONR_RANDOM_LOCALHOST_IP" ]] && random_localhost_ip_opt="--random-localhost-ip $ONIONR_RANDOM_LOCALHOST_IP"
[[ -n "$ONIONR_USE_TOR" ]] && use_tor_opt="--use-tor $ONIONR_USE_TOR"
[[ -n "$ONIONR_ANIMATED_BACKGROUND" ]] && animated_background_opt="--animated-background $ONIONR_ANIMATED_BACKGROUND"
[[ -n "$ONIONR_KEEP_LOG" ]] && keep_log_opt="--keep-log-on-exit $ONIONR_KEEP_LOG"
[[ -n "$ONIONR_USE_UPLOAD_MIXING" ]] && use_upload_mixing_opt="--use-upload-mixing $ONIONR_USE_UPLOAD_MIXING"
[[ -n "$ONIONR_DEV_MODE" ]] && dev_mode_opt="--dev-mode $ONIONR_DEV_MODE"
[[ -n "$ONIONR_DISABLE_PLUGIN_LIST" ]] && disable_plugin_list_opt=" --disable-plugin-list $ONIONR_DISABLE_PLUGIN_LIST"
[[ -n "$ONIONR_STORE_PLAINTEXT" ]] && store_plaintext_opt="--store-plaintext $ONIONR_STORE_PLAINTEXT"
[[ -n "$ONIONR_BIND_ADDRESS" ]] && bind_address_opt="--bind-address $ONIONR_BIND_ADDRESS"
[[ -n "$ONIONR_PORT" ]] && port_opt="--port $ONIONR_PORT"
python3 run-onionr-node.py \
$privkey_opt \
$bootstrap_opt \
$show_stats_opt \
$onboarding_opt \
$security_level_opt \
$open_ui_opt \
$random_localhost_ip_opt \
$use_tor_opt \
$animated_background_opt \
$keep_log_opt \
$use_upload_mixing_opt \
$dev_mode_opt \
$disable_plugin_list_opt \
$store_plaintext_opt \
$bind_address_opt \
$port_opt \
"$@"

50
scripts/remote-ui.py Executable file
View File

@ -0,0 +1,50 @@
#!/usr/bin/env python3
"""Craft and send requests to the local client API"""
import sys
import os
import time
from threading import Thread
if not os.path.exists('onionr.sh'):
os.chdir('../')
sys.path.append("src/")
import filepaths
import config
config.reload()
with open(filepaths.private_API_host_file, 'r') as host:
hostname = host.read()
port = config.get("client.client.port", 0)
if not port:
print("Could not get port for Onionr UI. Try again")
sys.exit(1)
torrc = f"""
HiddenServiceDir remote-onionr-hs
HiddenServicePort 80 {hostname}:{port}
"""
with open("remote-onionr-torrc", "w") as torrc_f:
torrc_f.write(torrc)
def show_onion():
while True:
time.sleep(1)
try:
with open("remote-onionr-hs/hostname", "r") as f:
o = f.read()
print("UI Onion (Keep secret):", o)
config.set("ui.public_remote_enabled", True)
config.set("ui.public_remote_hosts", [o])
config.save()
break
except FileNotFoundError:
pass
Thread(target=show_onion, daemon=True).start()
os.system("tor -f remote-onionr-torrc")

View File

@ -3,6 +3,7 @@
Process incoming requests to the client api server to validate Process incoming requests to the client api server to validate
that they are legitimate and not DNSR/XSRF or other local adversary that they are legitimate and not DNSR/XSRF or other local adversary
""" """
from ipaddress import ip_address
import hmac import hmac
from flask import Blueprint, request, abort, g from flask import Blueprint, request, abort, g
@ -53,21 +54,22 @@ class ClientAPISecurity:
def validate_request(): def validate_request():
"""Validate request has set password & is the correct hostname.""" """Validate request has set password & is the correct hostname."""
# For the purpose of preventing DNS rebinding attacks # For the purpose of preventing DNS rebinding attacks
localhost = True if ip_address(client_api.host).is_loopback:
if request.host != '%s:%s' % \ localhost = True
(client_api.host, client_api.bindPort): if request.host != '%s:%s' % \
localhost = False (client_api.host, client_api.bindPort):
localhost = False
if not localhost and public_remote_enabled: if not localhost and public_remote_enabled:
if request.host not in public_remote_hostnames: if request.host not in public_remote_hostnames:
logger.warn( logger.warn(
f'{request.host} not in {public_remote_hostnames}') f'{request.host} not in {public_remote_hostnames}')
abort(403) abort(403)
else: else:
if not localhost: if not localhost:
logger.warn( logger.warn(
f'Possible DNS rebinding attack by {request.host}') f'Possible DNS rebinding attack by {request.host}')
abort(403) abort(403)
# Add shared objects # Add shared objects
try: try:

View File

@ -91,15 +91,17 @@ class NetController:
if '100' not in line.decode(): if '100' not in line.decode():
logger.info(line.decode().strip(), terminal=True) logger.info(line.decode().strip(), terminal=True)
if 'bootstrapped 100' in line.decode().lower(): if 'bootstrapped 100' in line.decode().lower():
logger.info(line.decode()) logger.info(line.decode(), terminal=True)
break break
elif 'opening socks listener' in line.decode().lower(): elif 'opening socks listener' in line.decode().lower():
logger.debug(line.decode().replace('\n', '')) logger.debug(line.decode().replace('\n', ''))
else: else:
if 'err' in line.decode(): if 'err' in line.decode():
logger.error(line.decode().replace('\n', '')) logger.error(
line.decode().replace('\n', ''), terminal=True)
elif 'warn' in line.decode(): elif 'warn' in line.decode():
logger.warn(line.decode().replace('\n', '')) logger.warn(
line.decode().replace('\n', ''), terminal=True)
else: else:
logger.debug(line.decode().replace('\n', '')) logger.debug(line.decode().replace('\n', ''))
else: else:
@ -119,8 +121,8 @@ class NetController:
with open(self.dataDir + 'torPid.txt', 'w') as tor_pid_file: with open(self.dataDir + 'torPid.txt', 'w') as tor_pid_file:
tor_pid_file.write(str(tor.pid)) tor_pid_file.write(str(tor.pid))
multiprocessing.Process(target=watchdog.watchdog, #multiprocessing.Process(target=watchdog.watchdog,
args=[os.getpid(), tor.pid], daemon=True).start() # args=[os.getpid(), tor.pid], daemon=True).start()
logger.info('Finished starting Tor.', terminal=True) logger.info('Finished starting Tor.', terminal=True)

View File

@ -4,8 +4,6 @@ Create required Onionr directories
""" """
import os import os
import stat import stat
from pwd import getpwuid
from getpass import getuser
from . import identifyhome from . import identifyhome
import filepaths import filepaths
@ -27,10 +25,6 @@ import onionrexceptions
home = identifyhome.identify_home() home = identifyhome.identify_home()
def find_owner(filename):
return getpwuid(os.stat(filename).st_uid).pw_name
def create_dirs(): def create_dirs():
"""Create onionr data-related directories in """Create onionr data-related directories in
order of the hardcoded list below, order of the hardcoded list below,
@ -42,7 +36,7 @@ def create_dirs():
if not os.path.exists(path): if not os.path.exists(path):
os.makedirs(path) os.makedirs(path)
else: else:
if getuser() != find_owner(path): if os.getuid() != os.stat(path).st_uid:
raise onionrexceptions.InsecureDirectoryUsage( raise onionrexceptions.InsecureDirectoryUsage(
"Directory " + path + "Directory " + path +
" already exists and is not owned by the same user") " already exists and is not owned by the same user")
@ -55,4 +49,4 @@ def create_dirs():
try: try:
db() db()
except FileExistsError: except FileExistsError:
pass pass

View File

@ -1 +1 @@
1610736254 1611429331