Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ jobs:
- name: Test with filesystem backend
run: |
cp test_keys/* ../
chmod 600 ../server_private_key.key
cd ..
python -m unittest -v
env:
Expand All @@ -54,6 +55,7 @@ jobs:
- name: Test with Redis backend
run: |
cp test_keys/* ../
chmod 600 ../server_private_key.key
cd ..
python -m unittest -v
env:
Expand Down
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,19 @@ Green.

`PYTHONPATH=.. python -m blind_pin_server.generateserverkey`

## Securing the server key

Anyone who can read `server_private_key.key` can impersonate this server, so it is created
mode `0600` and the server refuses to start if it is readable by anyone else.

In the container it is read by `www-data`:

`sudo chown www-data:www-data server_private_key.key`

`sudo chmod 600 server_private_key.key`

Mount it read-only (`:ro`, as below) so the container can't alter it.

## Build the docker image

`docker build -f Dockerfile . -t dockerized_pinserver`
Expand All @@ -28,4 +41,4 @@ Green.

## Run the docker image (requires the previous steps)

`docker run -v $PWD/server_private_key.key:/server_private_key.key -v $PWD/pinsdir:/pins -p 8096:8096 dockerized_pinserver`
`docker run -v $PWD/server_private_key.key:/server_private_key.key:ro -v $PWD/pinsdir:/pins -p 8096:8096 dockerized_pinserver`
12 changes: 11 additions & 1 deletion server.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import time
from hmac import compare_digest
import os
import stat
from .lib import decrypt, encrypt, E_ECDH
from wallycore import ec_private_key_verify, ec_sig_from_bytes, sha256, \
hmac_sha256, EC_FLAG_ECDSA, ec_private_key_bip341_tweak, ec_public_key_from_private_key
Expand All @@ -20,7 +21,11 @@ def generate_server_key_pair(cls):

private_key, public_key = cls.generate_ec_key_pair()

with open(cls.STATIC_SERVER_PRIVATE_KEY_FILE, 'wb') as f:
# O_EXCL: fail if it exists, S_IRUSR|S_IWUSR: chmod 600
fd = os.open(cls.STATIC_SERVER_PRIVATE_KEY_FILE,
os.O_WRONLY | os.O_CREAT | os.O_EXCL,
stat.S_IRUSR | stat.S_IWUSR)
with os.fdopen(fd, 'wb') as f:
f.write(private_key)

with open(cls.STATIC_SERVER_PUBLIC_KEY_FILE, 'wb') as f:
Expand All @@ -32,6 +37,11 @@ def generate_server_key_pair(cls):
@classmethod
def load_private_key(cls):
if not cls.STATIC_SERVER_PRIVATE_KEY:
mode = stat.S_IMODE(os.stat(cls.STATIC_SERVER_PRIVATE_KEY_FILE).st_mode)
if mode & (stat.S_IRWXG | stat.S_IRWXO):
raise Exception(f'{cls.STATIC_SERVER_PRIVATE_KEY_FILE} is mode {mode:04o}, '
'must not be readable by other users')

with open(cls.STATIC_SERVER_PRIVATE_KEY_FILE, 'rb') as f:
cls.STATIC_SERVER_PRIVATE_KEY = f.read()
ec_private_key_verify(cls.STATIC_SERVER_PRIVATE_KEY)
Expand Down
Loading