electrum-personal-server

Maximally lightweight electrum server for a single user
git clone https://git.parazyd.org/electrum-personal-server
Log | Files | Refs | README

commit 5a255e406a578622f074259ece66e9d047d8ac98
parent 4dc8422e6e18ef09e771aed53b7a334c9d376d2e
Author: chris-belcher <chris-belcher@users.noreply.github.com>
Date:   Wed, 23 May 2018 16:52:34 +0100

Add IP address whitelisting feature

Diffstat:
MREADME.md | 10++++++----
Mconfig.cfg_sample | 7++++++-
Mserver.py | 23++++++++++++++++++-----
3 files changed, 30 insertions(+), 10 deletions(-)

diff --git a/README.md b/README.md @@ -78,18 +78,20 @@ headers; and locks Electrum to connect only to your server, disabling the GUI button to stop accidental connections. This helps avoid a user accidentally ruining their privacy by connecting to public Electrum servers. -Electrum Personal Server also works on [testnet bitcoin](https://en.bitcoin.it/wiki/Testnet). The Electrum wallet can be started in testnet mode with the command line flag `--testnet`. +Electrum Personal Server also works on [testnet](https://en.bitcoin.it/wiki/Testnet) and [regtest](https://bitcoin.org/en/glossary/regression-test-mode). The Electrum wallet can be started in testnet mode with the command line flag `--testnet` or `--regtest`. #### Exposure to the Internet Other people should not be connecting to your server. They won't be able to synchronize their wallet, and they could potentially learn all your -wallet addresses. +wallet addresses. They should also not be packet sniffing the connection +because it is not encrypted securely. By default the server will accept connections only from `localhost` so you -should either run Electrum wallet from the same computer or use a SSH tunnel -from another computer. +should either run Electrum wallet from the same computer, or use an encrypted +SSH tunnel from another computer, or use the IP address whitelisting feature to +connect over your own LAN. #### How is this different from other Electrum servers ? diff --git a/config.cfg_sample b/config.cfg_sample @@ -48,10 +48,15 @@ gap_limit = 25 [electrum-server] # 0.0.0.0 to accept connections from any IP #127.0.0.1 to accept from only localhost -# recommend you accept localhost only, for connecting remotely use a ssh tunnel host = 127.0.0.1 port = 50002 +# space-separated whitelist of IP addresses +# accepts CIDR notation eg 192.168.0.0/16 or 2a01:4f8:1f1::/120 +# star (*) means all are accepted +# generally requires host binding (above) to be 0.0.0.0 +ip_whitelist = * + #uses the default one, which is fine because by default nobody should be # allowed to connect to your server or scan your packets #to generate another certificate see https://github.com/spesmilo/electrum-server/blob/ce1b11d7f5f7a70a3b6cc7ec1d3e552436e54ffe/HOWTO.md#step-8-create-a-self-signed-ssl-cert diff --git a/server.py b/server.py @@ -4,6 +4,7 @@ import socket, time, json, datetime, struct, binascii, ssl, os, os.path from configparser import ConfigParser, NoSectionError, NoOptionError from collections import defaultdict import traceback, sys, platform +from ipaddress import ip_network, ip_address from electrumpersonalserver.jsonrpc import JsonRpc, JsonRpcError import electrumpersonalserver.hashes as hashes @@ -295,8 +296,8 @@ def create_server_socket(hostport): log("Listening for Electrum Wallet on " + str(hostport)) return server_sock -def run_electrum_server(hostport, rpc, txmonitor, poll_interval_listening, - poll_interval_connected, certfile, keyfile): +def run_electrum_server(rpc, txmonitor, hostport, ip_whitelist, + poll_interval_listening, poll_interval_connected, certfile, keyfile): log("Starting electrum server") server_sock = create_server_socket(hostport) server_sock.settimeout(poll_interval_listening) @@ -306,12 +307,16 @@ def run_electrum_server(hostport, rpc, txmonitor, poll_interval_listening, while sock == None: try: sock, addr = server_sock.accept() + if not any([ip_address(addr[0]) in ipnet + for ipnet in ip_whitelist]): + debug(addr[0] + " not in whitelist, closing") + raise ConnectionRefusedError() sock = ssl.wrap_socket(sock, server_side=True, certfile=certfile, keyfile=keyfile, ssl_version=ssl.PROTOCOL_SSLv23) except socket.timeout: on_heartbeat_listening(txmonitor) - except ssl.SSLError: + except (ConnectionRefusedError, ssl.SSLError): sock.close() sock = None @@ -504,14 +509,22 @@ def main(): return hostport = (config.get("electrum-server", "host"), int(config.get("electrum-server", "port"))) + ip_whitelist = [] + for ip in config.get("electrum-server", "ip_whitelist").split(" "): + if ip == "*": + #matches everything + ip_whitelist.append(ip_network("0.0.0.0/0")) + ip_whitelist.append(ip_network("::0/0")) + else: + ip_whitelist.append(ip_network(ip, strict=False)) poll_interval_listening = int(config.get("bitcoin-rpc", "poll_interval_listening")) poll_interval_connected = int(config.get("bitcoin-rpc", "poll_interval_connected")) certfile = config.get("electrum-server", "certfile") keyfile = config.get("electrum-server", "keyfile") - run_electrum_server(hostport, rpc, txmonitor, poll_interval_listening, - poll_interval_connected, certfile, keyfile) + run_electrum_server(rpc, txmonitor, hostport, ip_whitelist, + poll_interval_listening, poll_interval_connected, certfile, keyfile) if __name__ == "__main__": main()