mirror of
https://github.com/AskDavis/cyphernode.git
synced 2025-12-31 20:25:57 -08:00
Merge pull request #19 from SatoshiPortal/features/authapi
Features/authapi
This commit is contained in:
22
api_auth_docker/Dockerfile
Normal file
22
api_auth_docker/Dockerfile
Normal file
@@ -0,0 +1,22 @@
|
||||
FROM nginx:alpine
|
||||
|
||||
RUN apk add --update --no-cache \
|
||||
git \
|
||||
openssl \
|
||||
fcgiwrap \
|
||||
spawn-fcgi \
|
||||
curl \
|
||||
jq
|
||||
|
||||
COPY auth.sh /etc/nginx/conf.d
|
||||
COPY default-ssl.conf /etc/nginx/conf.d/default.conf
|
||||
COPY entrypoint.sh entrypoint.sh
|
||||
COPY keys.properties /etc/nginx/conf.d
|
||||
COPY api.properties /etc/nginx/conf.d
|
||||
COPY trace.sh /etc/nginx/conf.d
|
||||
COPY tests.sh /etc/nginx/conf.d
|
||||
COPY ip-whitelist.conf /etc/nginx/conf.d
|
||||
|
||||
RUN chmod +x /etc/nginx/conf.d/auth.sh entrypoint.sh
|
||||
|
||||
ENTRYPOINT ["./entrypoint.sh"]
|
||||
147
api_auth_docker/README.md
Normal file
147
api_auth_docker/README.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# HTTP/S API supporting HMAC API keys
|
||||
|
||||
So all the other containers are in the Docker Swarm and we want to expose a real HTTP/S interface to clients outside of the Swarm, that makes sense. Clients have to get an API key first.
|
||||
|
||||
## Build
|
||||
|
||||
### Create your API key and put it in keys.properties
|
||||
|
||||
Let's produce a 256-bits key that we'll convert in an hex string to store and use with openssl hmac feature.
|
||||
|
||||
Alpine (Busybox):
|
||||
|
||||
```shell
|
||||
dd if=/dev/urandom bs=32 count=1 2> /dev/null | xxd -pc 32
|
||||
```
|
||||
|
||||
Linux:
|
||||
|
||||
```shell
|
||||
dd if=/dev/urandom bs=32 count=1 2> /dev/null | xxd -ps -c 32
|
||||
```
|
||||
|
||||
Put the id, key and groups in keys.properties and give the id and key to the client. The key is a secret. keys.properties looks like this:
|
||||
|
||||
```property
|
||||
#kappiid="id";kapi_key="key";kapi_groups="group1,group2";leave the rest intact
|
||||
kapi_id="001";kapi_key="2df1eeea370eacdc5cf7e96c2d82140d1568079a5d4d87006ec8718a98883b36";kapi_groups="watcher";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="002";kapi_key="50c5e483b80964595508f214229b014aa6c013594d57d38bcb841093a39f1d83";kapi_groups="watcher";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="003";kapi_key="b9b8d527a1a27af2ad1697db3521f883760c342fc386dbc42c4efbb1a4d5e0af";kapi_groups="watcher,spender";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="004";kapi_key="bb0458b705e774c0c9622efaccfe573aa30c82f62386d9435f04e9727cdc26fd";kapi_groups="watcher,spender";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="005";kapi_key="6c009201b123e8c24c6b74590de28c0c96f3287e88cac9460a2173a53d73fb87";kapi_groups="watcher,spender,admin";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="006";kapi_key="19e121b698014fac638f772c4ff5775a738856bf6cbdef0dc88971059c69da4b";kapi_groups="watcher,spender,admin";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
```
|
||||
|
||||
You can have multiple keys, but be aware that this container has **not** been built to support thousands of API keys! **Cyphernode should be used locally**, not publicly as a service.
|
||||
|
||||
## IP Addresses Whitelist (**do not use for now**)
|
||||
**Docker Swarm obfuscates real client IP, this feature is not ready for now**
|
||||
|
||||
You can have an IP whitelist policy, denying everything except the explicit IP addresses you need. Edit ip-whitelist.conf file:
|
||||
|
||||
```conf
|
||||
# Leave commented if you don't want to use IP whitelist
|
||||
|
||||
# List of white listed IP addresses...
|
||||
#allow 45.56.67.78;
|
||||
#deny all;
|
||||
```
|
||||
|
||||
## SSL
|
||||
|
||||
If you already have your certificates and keystores infra, you already know what to do and your can skip this section. Put your files in the bound volume (~/cyphernode-ssl/ see volume path in docker-compose.yml).
|
||||
|
||||
If not, you can create your keys and self-signed certificates.
|
||||
|
||||
```shell
|
||||
mkdir -p ~/cyphernode-ssl/certs ~/cyphernode-ssl/private
|
||||
openssl req -subj '/CN=localhost' -x509 -newkey rsa:4096 -nodes -keyout ~/cyphernode-ssl/private/key.pem -out ~/cyphernode-ssl/certs/cert.pem -days 365
|
||||
```
|
||||
|
||||
If you don't want to use HTTPS, just copy default.conf instead of default-ssl.conf in Dockerfile.
|
||||
|
||||
**Nota bene**: If you self-sign the certificate, you have to trust the certificate on the client side by adding it to the Trusted Root Certification Authorities or whatever your client needs.
|
||||
|
||||
### Build and run docker image
|
||||
|
||||
```shell
|
||||
docker build -t authapi .
|
||||
```
|
||||
|
||||
If you are using it independantly from the Docker stack (docker-compose.yml), you can run it like that:
|
||||
|
||||
```shell
|
||||
docker run -d --rm --name authapi -p 80:80 -p 443:443 --network cyphernodenet -v "~/cyphernode-ssl/certs:/etc/ssl/certs" -v "~/cyphernode-ssl/private:/etc/ssl/private" authapi
|
||||
```
|
||||
|
||||
## FYI: Bearer token
|
||||
|
||||
Following JWT (JSON Web Tokens) standard, we build a bearer token that will be in the request header and signed with the secret key. We need this in the request header:
|
||||
|
||||
```shell
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
...where token is:
|
||||
|
||||
```shell
|
||||
token = hhh.ppp.sss
|
||||
```
|
||||
|
||||
...where hhh is the header in base64, ppp is the payload in base64 and sss is the signature. Here are the expected formats and contents:
|
||||
|
||||
```shell
|
||||
header = {"alg":"HS256","typ":"JWT"}
|
||||
header64 = base64(header) = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9Cg==
|
||||
```
|
||||
|
||||
```shell
|
||||
payload = {"id":"001","exp":1538528077}
|
||||
payload64 = base64(payload) = eyJpZCI6IjAwMSIsImV4cCI6MTUzODUyODA3N30K
|
||||
```
|
||||
|
||||
The "id" property is the client id and the "exp" property should be current epoch time + 10 seconds, like:
|
||||
|
||||
```shell
|
||||
$((`date +"%s"`+10))
|
||||
```
|
||||
|
||||
...so that the request will be expired in 10 seconds. That should take care of most Replay attacks if any. You should run nginx with TLS so that the replay attack can't be possible.
|
||||
|
||||
```shell
|
||||
signature = hmacsha256(header64.payload64, key)
|
||||
```
|
||||
|
||||
```shell
|
||||
token = header64 + "." + payload64 + "." + signature
|
||||
```
|
||||
|
||||
### cURL example of an API invocation
|
||||
|
||||
Instruction should be in the form:
|
||||
|
||||
```shell
|
||||
curl -v -H "Authorization: Bearer hhh.ppp.sss" localhost
|
||||
```
|
||||
|
||||
10 seconds request expiration:
|
||||
|
||||
```shell
|
||||
id="001";h64=$(echo "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="2df1eeea370eacdc5cf7e96c2d82140d1568079a5d4d87006ec8718a98883b36";s=$(echo "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Authorization: Bearer $token" -k https://localhost/getbestblockhash
|
||||
```
|
||||
|
||||
60 seconds request expiration:
|
||||
|
||||
```shell
|
||||
id="001";h64=$(echo "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+60))}" | base64);k="2df1eeea370eacdc5cf7e96c2d82140d1568079a5d4d87006ec8718a98883b36";s=$(echo "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -v -H "Authorization: Bearer $token" -k https://localhost/getbestblockhash
|
||||
```
|
||||
|
||||
## Technicalities
|
||||
|
||||
```shell
|
||||
h64=$(echo "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64)
|
||||
p64=$(echo "{\"id\":\"001\",\"exp\":$((`date +"%s"`+10))}" | base64)
|
||||
k="2df1eeea370eacdc5cf7e96c2d82140d1568079a5d4d87006ec8718a98883b36"
|
||||
s=$(echo "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1)
|
||||
token="$h64.$p64.$s"
|
||||
```
|
||||
29
api_auth_docker/api.properties
Normal file
29
api_auth_docker/api.properties
Normal file
@@ -0,0 +1,29 @@
|
||||
|
||||
# Watcher can:
|
||||
action_watch=watcher
|
||||
action_unwatch=watcher
|
||||
action_getactivewatches=watcher
|
||||
action_getbestblockhash=watcher
|
||||
action_getbestblockinfo=watcher
|
||||
action_getblockinfo=watcher
|
||||
action_gettransaction=watcher
|
||||
action_ln_getinfo=watcher
|
||||
action_ln_create_invoice=watcher
|
||||
|
||||
# Spender can do what the watcher can do plus:
|
||||
action_getbalance=spender
|
||||
action_getnewaddress=spender
|
||||
action_spend=spender
|
||||
action_addtobatch=spender
|
||||
action_batchspend=spender
|
||||
action_deriveindex=spender
|
||||
action_derivepubpath=spender
|
||||
action_ln_pay=spender
|
||||
action_ln_newaddr=spender
|
||||
|
||||
# Admin can do what the spender can do plus:
|
||||
|
||||
|
||||
# Should be called from inside the Swarm:
|
||||
action_conf=internal
|
||||
action_executecallbacks=internal
|
||||
131
api_auth_docker/auth.sh
Normal file
131
api_auth_docker/auth.sh
Normal file
@@ -0,0 +1,131 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# This is not designed to serve thousands of API key!
|
||||
#
|
||||
# header = {"alg":"HS256","typ":"JWT"}
|
||||
# header64 = base64(header) = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9Cg==
|
||||
#
|
||||
# payload = {"id":"001","exp":1538528077}
|
||||
# payload64 = base64(payload) = eyJpZCI6IjAwMSIsImV4cCI6MTUzODUyODA3N30K
|
||||
#
|
||||
# signature = hmacsha256(header64.payload64, key)
|
||||
#
|
||||
# token = header64.payload64.signature = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9Cg==.eyJpZCI6IjAwMSIsImV4cCI6MTUzODUyODA3N30K.signature
|
||||
#
|
||||
|
||||
. ./trace.sh
|
||||
|
||||
verify_sign()
|
||||
{
|
||||
local returncode
|
||||
|
||||
local header64=$(echo ${1} | cut -sd '.' -f1)
|
||||
local payload64=$(echo ${1} | cut -sd '.' -f2)
|
||||
local signature=$(echo ${1} | cut -sd '.' -f3)
|
||||
|
||||
trace "[verify_sign] header64=${header64}"
|
||||
trace "[verify_sign] payload64=${payload64}"
|
||||
trace "[verify_sign] signature=${signature}"
|
||||
|
||||
local payload=$(echo ${payload64} | base64 -d)
|
||||
local exp=$(echo ${payload} | jq ".exp")
|
||||
local current=$(date +"%s")
|
||||
|
||||
trace "[verify_sign] payload=${payload}"
|
||||
trace "[verify_sign] exp=${exp}"
|
||||
trace "[verify_sign] current=${current}"
|
||||
|
||||
if [ ${exp} -gt ${current} ]; then
|
||||
trace "[verify_sign] Not expired, let's validate signature"
|
||||
local id=$(echo ${payload} | jq ".id" | tr -d '"')
|
||||
trace "[verify_sign] id=${id}"
|
||||
|
||||
# Check for code injection
|
||||
# id will usually be an int, but could be alphanum... nothing else
|
||||
if ! [[ $id =~ '^[A-Za-z0-9]$']]; then
|
||||
trace "[verify_sign] Potential code injection, exiting"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# It is so much faster to include the keys here instead of grep'ing the file for key.
|
||||
. ./keys.properties
|
||||
|
||||
local key
|
||||
eval key='$ukey_'$id
|
||||
trace "[verify_sign] key=${key}"
|
||||
local comp_sign=$(echo "${header64}.${payload64}" | openssl dgst -hmac "${key}" -sha256 -r | cut -sd ' ' -f1)
|
||||
|
||||
trace "[verify_sign] comp_sign=${comp_sign}"
|
||||
|
||||
if [ "${comp_sign}" = "${signature}" ]; then
|
||||
trace "[verify_sign] Valid signature!"
|
||||
|
||||
verify_group ${id}
|
||||
returncode=$?
|
||||
|
||||
if [ "${returncode}" -eq 0 ]; then
|
||||
echo -en "Status: 200 OK\r\n\r\n"
|
||||
return
|
||||
fi
|
||||
trace "[verify_sign] Invalid group!"
|
||||
return 1
|
||||
fi
|
||||
trace "[verify_sign] Invalid signature!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
trace "[verify_sign] Expired!"
|
||||
return 1
|
||||
}
|
||||
|
||||
verify_group()
|
||||
{
|
||||
trace "[verify_group] Verifying group..."
|
||||
|
||||
local id=${1}
|
||||
local action=${REQUEST_URI:1}
|
||||
trace "[verify_group] action=${action}"
|
||||
|
||||
# Check for code injection
|
||||
# action could be alphanum... nothing else
|
||||
if ! [[ $action =~ '^[A-Za-z]$']]; then
|
||||
trace "[verify_group] Potential code injection, exiting"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# It is so much faster to include the keys here instead of grep'ing the file for key.
|
||||
. ./api.properties
|
||||
|
||||
local needed_group
|
||||
local ugroups
|
||||
|
||||
eval needed_group='$action_'${action}
|
||||
trace "[verify_group] needed_group=${needed_group}"
|
||||
|
||||
eval ugroups='$ugroups_'$id
|
||||
trace "[verify_group] user groups=${ugroups}"
|
||||
|
||||
case "${ugroups}" in
|
||||
*${needed_group}*) trace "[verify_group] Access granted"; return 0 ;;
|
||||
esac
|
||||
|
||||
trace "[verify_group] Access NOT granted"
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
# $HTTP_AUTHORIZATION = Bearer <token>
|
||||
# If this is not found in header, we leave
|
||||
trace "[auth.sh] HTTP_AUTHORIZATION=${HTTP_AUTHORIZATION}"
|
||||
if [ "${HTTP_AUTHORIZATION:0:6}" = "Bearer" ]; then
|
||||
token="${HTTP_AUTHORIZATION:6}"
|
||||
|
||||
if [ -n "$token" ]; then
|
||||
trace "[auth.sh] Valid format for authorization header"
|
||||
verify_sign "${token}"
|
||||
[ "$?" -eq "0" ] && return
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -en "Status: 403 Forbidden\r\n\r\n"
|
||||
31
api_auth_docker/default-ssl.conf
Normal file
31
api_auth_docker/default-ssl.conf
Normal file
@@ -0,0 +1,31 @@
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name localhost;
|
||||
|
||||
include /etc/nginx/conf.d/ip-whitelist.conf;
|
||||
|
||||
ssl_certificate /etc/ssl/certs/cert.pem;
|
||||
ssl_certificate_key /etc/ssl/private/key.pem;
|
||||
|
||||
location / {
|
||||
auth_request /auth;
|
||||
proxy_pass http://cyphernode:8888;
|
||||
}
|
||||
|
||||
location /auth {
|
||||
internal;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME /etc/nginx/conf.d/auth.sh;
|
||||
fastcgi_pass unix:/var/run/fcgiwrap.socket;
|
||||
}
|
||||
|
||||
#error_page 404 /404.html;
|
||||
|
||||
# redirect server error pages to the static page /50x.html
|
||||
#
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
|
||||
}
|
||||
28
api_auth_docker/default.conf
Normal file
28
api_auth_docker/default.conf
Normal file
@@ -0,0 +1,28 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
include /etc/nginx/conf.d/ip-whitelist.conf;
|
||||
|
||||
location / {
|
||||
auth_request /auth;
|
||||
proxy_pass http://cyphernode:8888;
|
||||
}
|
||||
|
||||
location /auth {
|
||||
internal;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME /etc/nginx/conf.d/auth.sh;
|
||||
fastcgi_pass unix:/var/run/fcgiwrap.socket;
|
||||
}
|
||||
|
||||
#error_page 404 /404.html;
|
||||
|
||||
# redirect server error pages to the static page /50x.html
|
||||
#
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
|
||||
}
|
||||
5
api_auth_docker/entrypoint.sh
Normal file
5
api_auth_docker/entrypoint.sh
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
spawn-fcgi -s /var/run/fcgiwrap.socket -u nginx -g nginx -U nginx -- /usr/bin/fcgiwrap
|
||||
|
||||
nginx -g "daemon off;"
|
||||
1
api_auth_docker/env.properties
Normal file
1
api_auth_docker/env.properties
Normal file
@@ -0,0 +1 @@
|
||||
TRACING=1
|
||||
8
api_auth_docker/ip-whitelist.conf
Normal file
8
api_auth_docker/ip-whitelist.conf
Normal file
@@ -0,0 +1,8 @@
|
||||
# Leave commented if you don't want to use IP whitelist
|
||||
|
||||
#real_ip_header X-Forwarded-For;
|
||||
#set_real_ip_from 0.0.0.0/0;
|
||||
|
||||
# List of white listed IP addresses...
|
||||
#allow 45.56.67.78;
|
||||
#deny all;
|
||||
7
api_auth_docker/keys.properties
Normal file
7
api_auth_docker/keys.properties
Normal file
@@ -0,0 +1,7 @@
|
||||
#kappiid="id";kapi_key="key";kapi_groups="group1,group2";leave the rest intact
|
||||
kapi_id="001";kapi_key="2df1eeea370eacdc5cf7e96c2d82140d1568079a5d4d87006ec8718a98883b36";kapi_groups="watcher";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="002";kapi_key="50c5e483b80964595508f214229b014aa6c013594d57d38bcb841093a39f1d83";kapi_groups="watcher";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="003";kapi_key="b9b8d527a1a27af2ad1697db3521f883760c342fc386dbc42c4efbb1a4d5e0af";kapi_groups="watcher,spender";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="004";kapi_key="bb0458b705e774c0c9622efaccfe573aa30c82f62386d9435f04e9727cdc26fd";kapi_groups="watcher,spender";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="005";kapi_key="6c009201b123e8c24c6b74590de28c0c96f3287e88cac9460a2173a53d73fb87";kapi_groups="watcher,spender,admin";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="006";kapi_key="19e121b698014fac638f772c4ff5775a738856bf6cbdef0dc88971059c69da4b";kapi_groups="watcher,spender,admin";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
282
api_auth_docker/tests.sh
Normal file
282
api_auth_docker/tests.sh
Normal file
@@ -0,0 +1,282 @@
|
||||
#!/bin/sh
|
||||
|
||||
# We just want to test the authentication/authorization, not the actual called function
|
||||
# Replace
|
||||
# proxy_pass http://cyphernode:8888;
|
||||
# by
|
||||
# proxy_pass http://tests:8888;
|
||||
# in /etc/nginx/conf.d/default.conf to run the tests
|
||||
|
||||
test_expiration()
|
||||
{
|
||||
# Let's test expiration: 1 second in payload, request 2 seconds later
|
||||
|
||||
local id=${1}
|
||||
# echo "id=${id}"
|
||||
local k
|
||||
eval k='$ukey_'$id
|
||||
|
||||
local p64=$(echo "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+1))}" | base64)
|
||||
local s=$(echo "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1)
|
||||
local token="$h64.$p64.$s"
|
||||
|
||||
echo " Sleeping 2 seconds... "
|
||||
sleep 2
|
||||
|
||||
local rc
|
||||
echo -n " Testing expired request... "
|
||||
rc=$(time -f "%E" curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" -k https://localhost/getblockinfo)
|
||||
[ "${rc}" -ne "403" ] && return 10
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
test_authentication()
|
||||
{
|
||||
# Let's test authentication/signature
|
||||
|
||||
local id=${1}
|
||||
# echo "id=${id}"
|
||||
local k
|
||||
eval k='$ukey_'$id
|
||||
|
||||
local p64=$(echo "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64)
|
||||
local s=$(echo "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1)
|
||||
local token="$h64.$p64.$s"
|
||||
|
||||
local rc
|
||||
|
||||
echo -n " Testing good signature... "
|
||||
rc=$(time -f "%E" curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" -k https://localhost/getblockinfo)
|
||||
[ "${rc}" -eq "403" ] && return 20
|
||||
|
||||
token="$h64.$p64.a$s"
|
||||
echo -n " Testing bad signature... "
|
||||
rc=$(time -f "%E" curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" -k https://localhost/getblockinfo)
|
||||
[ "${rc}" -ne "403" ] && return 30
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
test_authorization_watcher()
|
||||
{
|
||||
# Let's test autorization
|
||||
|
||||
local id=${1}
|
||||
# echo "id=${id}"
|
||||
local k
|
||||
eval k='$ukey_'$id
|
||||
|
||||
local p64=$(echo "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64)
|
||||
local s=$(echo "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1)
|
||||
local token="$h64.$p64.$s"
|
||||
|
||||
local rc
|
||||
|
||||
# Watcher can:
|
||||
# watch
|
||||
echo -n " Testing watch... "
|
||||
rc=$(time -f "%E" curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" -k https://localhost/watch)
|
||||
[ "${rc}" -eq "403" ] && return 40
|
||||
|
||||
# unwatch
|
||||
echo -n " Testing unwatch... "
|
||||
rc=$(time -f "%E" curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" -k https://localhost/unwatch)
|
||||
[ "${rc}" -eq "403" ] && return 50
|
||||
|
||||
# getactivewatches
|
||||
echo -n " Testing getactivewatches... "
|
||||
rc=$(time -f "%E" curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" -k https://localhost/getactivewatches)
|
||||
[ "${rc}" -eq "403" ] && return 60
|
||||
|
||||
# getbestblockhash
|
||||
echo -n " Testing getbestblockhash... "
|
||||
rc=$(time -f "%E" curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" -k https://localhost/getbestblockhash)
|
||||
[ "${rc}" -eq "403" ] && return 70
|
||||
|
||||
# getbestblockinfo
|
||||
echo -n " Testing getbestblockinfo... "
|
||||
rc=$(time -f "%E" curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" -k https://localhost/getbestblockinfo)
|
||||
[ "${rc}" -eq "403" ] && return 80
|
||||
|
||||
# getblockinfo
|
||||
echo -n " Testing getblockinfo... "
|
||||
rc=$(time -f "%E" curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" -k https://localhost/getblockinfo)
|
||||
[ "${rc}" -eq "403" ] && return 90
|
||||
|
||||
# gettransaction
|
||||
echo -n " Testing gettransaction... "
|
||||
rc=$(time -f "%E" curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" -k https://localhost/gettransaction)
|
||||
[ "${rc}" -eq "403" ] && return 100
|
||||
|
||||
# ln_getinfo
|
||||
echo -n " Testing ln_getinfo... "
|
||||
rc=$(time -f "%E" curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" -k https://localhost/ln_getinfo)
|
||||
[ "${rc}" -eq "403" ] && return 110
|
||||
|
||||
# ln_create_invoice
|
||||
echo -n " Testing ln_create_invoice... "
|
||||
rc=$(time -f "%E" curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" -k https://localhost/ln_create_invoice)
|
||||
[ "${rc}" -eq "403" ] && return 120
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
test_authorization_spender()
|
||||
{
|
||||
# Let's test autorization
|
||||
|
||||
local id=${1}
|
||||
# echo "id=${id}"
|
||||
local is_spender=${2}
|
||||
# echo "is_spender=${is_spender}"
|
||||
local k
|
||||
eval k='$ukey_'$id
|
||||
|
||||
local p64=$(echo "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64)
|
||||
local s=$(echo "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1)
|
||||
local token="$h64.$p64.$s"
|
||||
|
||||
local rc
|
||||
|
||||
# Spender can do what the watcher can do, plus:
|
||||
# getbalance
|
||||
echo -n " Testing getbalance... "
|
||||
rc=$(time -f "%E" curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" -k https://localhost/getbalance)
|
||||
[ ${is_spender} = true ] && [ "${rc}" -eq "403" ] && return 130
|
||||
[ ${is_spender} = false ] && [ "${rc}" -ne "403" ] && return 135
|
||||
|
||||
# getnewaddress
|
||||
echo -n " Testing getnewaddress... "
|
||||
rc=$(time -f "%E" curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" -k https://localhost/getnewaddress)
|
||||
[ ${is_spender} = true ] && [ "${rc}" -eq "403" ] && return 140
|
||||
[ ${is_spender} = false ] && [ "${rc}" -ne "403" ] && return 145
|
||||
|
||||
# spend
|
||||
echo -n " Testing spend... "
|
||||
rc=$(time -f "%E" curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" -k https://localhost/spend)
|
||||
[ ${is_spender} = true ] && [ "${rc}" -eq "403" ] && return 150
|
||||
[ ${is_spender} = false ] && [ "${rc}" -ne "403" ] && return 155
|
||||
|
||||
# addtobatch
|
||||
echo -n " Testing addtobatch... "
|
||||
rc=$(time -f "%E" curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" -k https://localhost/addtobatch)
|
||||
[ ${is_spender} = true ] && [ "${rc}" -eq "403" ] && return 160
|
||||
[ ${is_spender} = false ] && [ "${rc}" -ne "403" ] && return 165
|
||||
|
||||
# batchspend
|
||||
echo -n " Testing batchspend... "
|
||||
rc=$(time -f "%E" curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" -k https://localhost/batchspend)
|
||||
[ ${is_spender} = true ] && [ "${rc}" -eq "403" ] && return 170
|
||||
[ ${is_spender} = false ] && [ "${rc}" -ne "403" ] && return 175
|
||||
|
||||
# deriveindex
|
||||
echo -n " Testing deriveindex... "
|
||||
rc=$(time -f "%E" curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" -k https://localhost/deriveindex)
|
||||
[ ${is_spender} = true ] && [ "${rc}" -eq "403" ] && return 180
|
||||
[ ${is_spender} = false ] && [ "${rc}" -ne "403" ] && return 185
|
||||
|
||||
# derivepubpath
|
||||
echo -n " Testing derivepubpath... "
|
||||
rc=$(time -f "%E" curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" -k https://localhost/derivepubpath)
|
||||
[ ${is_spender} = true ] && [ "${rc}" -eq "403" ] && return 190
|
||||
[ ${is_spender} = false ] && [ "${rc}" -ne "403" ] && return 195
|
||||
|
||||
# ln_pay
|
||||
echo -n " Testing ln_pay... "
|
||||
rc=$(time -f "%E" curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" -k https://localhost/ln_pay)
|
||||
[ ${is_spender} = true ] && [ "${rc}" -eq "403" ] && return 200
|
||||
[ ${is_spender} = false ] && [ "${rc}" -ne "403" ] && return 205
|
||||
|
||||
# ln_newaddr
|
||||
echo -n " Testing ln_newaddr... "
|
||||
rc=$(time -f "%E" curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" -k https://localhost/ln_newaddr)
|
||||
[ ${is_spender} = true ] && [ "${rc}" -eq "403" ] && return 210
|
||||
[ ${is_spender} = false ] && [ "${rc}" -ne "403" ] && return 215
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
test_authorization_internal()
|
||||
{
|
||||
# Let's test autorization
|
||||
|
||||
local id=${1}
|
||||
# echo "id=${id}"
|
||||
local k
|
||||
eval k='$ukey_'$id
|
||||
|
||||
local p64=$(echo "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64)
|
||||
local s=$(echo "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1)
|
||||
local token="$h64.$p64.$s"
|
||||
|
||||
local rc
|
||||
|
||||
# Should be called from inside the Swarm:
|
||||
# conf
|
||||
echo -n " Testing conf... "
|
||||
rc=$(time -f "%E" curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" -k https://localhost/conf)
|
||||
[ "${rc}" -ne "403" ] && return 220
|
||||
|
||||
# executecallbacks
|
||||
echo -n " Testing executecallbacks... "
|
||||
rc=$(time -f "%E" curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" -k https://localhost/executecallbacks)
|
||||
[ "${rc}" -ne "403" ] && return 230
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
kapi_id="001";kapi_key="2df1eeea370eacdc5cf7e96c2d82140d1568079a5d4d87006ec8718a98883b36";kapi_groups="watcher";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="003";kapi_key="b9b8d527a1a27af2ad1697db3521f883760c342fc386dbc42c4efbb1a4d5e0af";kapi_groups="watcher,spender";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
kapi_id="005";kapi_key="6c009201b123e8c24c6b74590de28c0c96f3287e88cac9460a2173a53d73fb87";kapi_groups="watcher,spender,admin";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}
|
||||
h64=$(echo "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64)
|
||||
|
||||
# Let's test expiration: 1 second in payload, request 2 seconds later
|
||||
|
||||
echo 'test_expiration "001"'
|
||||
test_expiration "001" ; rc=$? ; [ $rc -ne 0 ] && echo $rc && return $rc
|
||||
echo 'test_expiration "003"'
|
||||
test_expiration "003" ; rc=$? ; [ $rc -ne 0 ] && echo $rc && return $rc
|
||||
echo 'test_expiration "005"'
|
||||
test_expiration "005" ; rc=$? ; [ $rc -ne 0 ] && echo $rc && return $rc
|
||||
|
||||
# Let's test authentication/signature
|
||||
|
||||
echo 'test_authentication "001"'
|
||||
test_authentication "001" ; rc=$? ; [ $rc -ne 0 ] && echo $rc && return $rc
|
||||
echo 'test_authentication "003"'
|
||||
test_authentication "003" ; rc=$? ; [ $rc -ne 0 ] && echo $rc && return $rc
|
||||
echo 'test_authentication "005"'
|
||||
test_authentication "005" ; rc=$? ; [ $rc -ne 0 ] && echo $rc && return $rc
|
||||
|
||||
# Let's test autorization for watcher actions
|
||||
|
||||
echo 'test_authorization_watcher "001"'
|
||||
test_authorization_watcher "001" ; rc=$? ; [ $rc -ne 0 ] && echo $rc && return $rc
|
||||
echo 'test_authorization_watcher "003"'
|
||||
test_authorization_watcher "003" ; rc=$? ; [ $rc -ne 0 ] && echo $rc && return $rc
|
||||
echo 'test_authorization_watcher "005"'
|
||||
test_authorization_watcher "005" ; rc=$? ; [ $rc -ne 0 ] && echo $rc && return $rc
|
||||
|
||||
# Let's test autorization for spender actions
|
||||
|
||||
echo 'test_authorization_spender "001" false'
|
||||
test_authorization_spender "001" false ; rc=$? ; [ $rc -ne 0 ] && echo $rc && return $rc
|
||||
echo 'test_authorization_spender "003" true'
|
||||
test_authorization_spender "003" true ; rc=$? ; [ $rc -ne 0 ] && echo $rc && return $rc
|
||||
echo 'test_authorization_spender "005" true'
|
||||
test_authorization_spender "005" true ; rc=$? ; [ $rc -ne 0 ] && echo $rc && return $rc
|
||||
|
||||
# Let's test autorization for admin actions
|
||||
|
||||
#test_authorization_admin "001"
|
||||
#test_authorization_admin "003"
|
||||
#test_authorization_admin "005"
|
||||
|
||||
# Let's test autorization for internal actions
|
||||
echo 'test_authorization_internal "001"'
|
||||
test_authorization_internal "001" ; rc=$? ; [ $rc -ne 0 ] && echo $rc && return $rc
|
||||
echo 'test_authorization_internal "003"'
|
||||
test_authorization_internal "003" ; rc=$? ; [ $rc -ne 0 ] && echo $rc && return $rc
|
||||
echo 'test_authorization_internal "005"'
|
||||
test_authorization_internal "005" ; rc=$? ; [ $rc -ne 0 ] && echo $rc && return $rc
|
||||
15
api_auth_docker/trace.sh
Normal file
15
api_auth_docker/trace.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
|
||||
trace()
|
||||
{
|
||||
if [ -n "${TRACING}" ]; then
|
||||
echo "[$(date +%Y-%m-%dT%H:%M:%S%z)] ${1}" 1>&2
|
||||
fi
|
||||
}
|
||||
|
||||
trace_rc()
|
||||
{
|
||||
if [ -n "${TRACING}" ]; then
|
||||
echo "[$(date +%Y-%m-%dT%H:%M:%S%z)] Last return code: ${1}" 1>&2
|
||||
fi
|
||||
}
|
||||
7
clients/README.md
Normal file
7
clients/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Client-side helpers
|
||||
|
||||
These are just examples of how clients can use Cyphernode in their code.
|
||||
|
||||
# Contributing
|
||||
|
||||
You are welcome to add more languages and/or improve current code.
|
||||
75
clients/javascript/cyphernode-client.js
Normal file
75
clients/javascript/cyphernode-client.js
Normal file
@@ -0,0 +1,75 @@
|
||||
CyphernodeClient = function(is_prod) {
|
||||
this.baseURL = is_prod ? 'https://cyphernode:443' : 'https://cyphernode-dev:443'
|
||||
this.h64 = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9Cg=='
|
||||
this.api_key = Meteor.settings.CYPHERNODE.api_key
|
||||
};
|
||||
|
||||
CyphernodeClient.prototype._post = function(url, postdata, cb) {
|
||||
let urlr = this.baseURL + url;
|
||||
|
||||
let current = Math.round(new Date().getTime/1000) + 10
|
||||
let p64 = btoa('{"id":"${id}","exp":' + current + '}')
|
||||
let s = CryptoJS.HmacSHA256(p64, this.api_key).toString()
|
||||
let token = this.h64 + '.' + p64 + '.' + s
|
||||
|
||||
HTTP.post(
|
||||
urlr,
|
||||
{
|
||||
data: postdata,
|
||||
headers: {'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer ' + token},
|
||||
}, function (err, resp) {
|
||||
cb(err, resp.data)
|
||||
}
|
||||
)
|
||||
};
|
||||
|
||||
CyphernodeClient.prototype._get = function(url, cb) {
|
||||
let urlr = this.baseURL + url;
|
||||
|
||||
let current = Math.round(new Date().getTime/1000) + 10
|
||||
let p64 = btoa('{"id":"${id}","exp":' + current + '}')
|
||||
let s = CryptoJS.HmacSHA256(p64, this.api_key).toString()
|
||||
let token = this.h64 + '.' + p64 + '.' + s
|
||||
|
||||
HTTP.get(urlr, {headers: {'Authorization': 'Bearer ' + token}}, function (err, resp) {
|
||||
cb(err, resp.data)
|
||||
})
|
||||
};
|
||||
|
||||
CyphernodeClient.prototype.watch = function(btcaddr, cb0conf, cb1conf, cbreply) {
|
||||
// BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","unconfirmedCallbackURL":"192.168.122.233:1111/callback0conf","confirmedCallbackURL":"192.168.122.233:1111/callback1conf"}
|
||||
let data = { address: btcaddr, unconfirmedCallbackURL: cb0conf, confirmedCallbackURL: cb1conf }
|
||||
this._post('/watch', data, cbreply);
|
||||
};
|
||||
|
||||
CyphernodeClient.prototype.unwatch = function(btcaddr, cbreply) {
|
||||
// 192.168.122.152:8080/unwatch/2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp
|
||||
this._get('/unwatch/' + btcaddr, cbreply);
|
||||
};
|
||||
|
||||
CyphernodeClient.prototype.getActiveWatches = function(cbreply) {
|
||||
// 192.168.122.152:8080/getactivewatches
|
||||
this._get('/getactivewatches', cbreply);
|
||||
};
|
||||
|
||||
CyphernodeClient.prototype.getTransaction = function(txid, cbreply) {
|
||||
// http://192.168.122.152:8080/gettransaction/af867c86000da76df7ddb1054b273ca9e034e8c89d049b5b2795f9f590f67648
|
||||
this._get('/gettransaction/' + txid, cbreply);
|
||||
};
|
||||
|
||||
CyphernodeClient.prototype.spend = function(btcaddr, amnt, cbreply) {
|
||||
// BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","amount":0.00233}
|
||||
let data = { address: btcaddr, amount: amnt }
|
||||
this._post('/spend', data, cbreply);
|
||||
};
|
||||
|
||||
CyphernodeClient.prototype.getBalance = function(cbreply) {
|
||||
// http://192.168.122.152:8080/getbalance
|
||||
this._get('/getbalance', cbreply);
|
||||
};
|
||||
|
||||
CyphernodeClient.prototype.getNewAddress = function(cbreply) {
|
||||
// http://192.168.122.152:8080/getnewaddress
|
||||
this._get('/getnewaddress', cbreply);
|
||||
};
|
||||
76
clients/shell/cyphernode-client.sh
Normal file
76
clients/shell/cyphernode-client.sh
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/bin/sh
|
||||
|
||||
. .cyphernode.conf
|
||||
|
||||
invoke_cyphernode()
|
||||
{
|
||||
local action=${1}
|
||||
local post=${2}
|
||||
|
||||
local p64=$(echo "{\"id\":\"${id}\",\"exp\":$((`date +"%s"`+10))}" | base64)
|
||||
local s=$(echo "$h64.$p64" | openssl dgst -hmac "$key" -sha256 -r | cut -sd ' ' -f1)
|
||||
local token="$h64.$p64.$s"
|
||||
|
||||
if [ -n "${post}" ]; then
|
||||
echo $(curl -v -H "Authorization: Bearer $token" -d "${post}" -k "https://cyphernode/${action}")
|
||||
return $?
|
||||
else
|
||||
echo $(curl -v -H "Authorization: Bearer $token" -k "https://cyphernode/${action}")
|
||||
return $?
|
||||
fi
|
||||
}
|
||||
|
||||
watch()
|
||||
{
|
||||
# BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","unconfirmedCallbackURL":"192.168.122.233:1111/callback0conf","confirmedCallbackURL":"192.168.122.233:1111/callback1conf"}
|
||||
local btcaddr=${1}
|
||||
local cb0conf=${2}
|
||||
local cb1conf=${3}
|
||||
local post="{\"address\":\"${btcaddr}\",\"unconfirmedCallbackURL\":\"${cb0conf}\",\"confirmedCallbackURL\":\"${cb1conf}\"}"
|
||||
|
||||
echo $(invoke_cyphernode "watch" ${post})
|
||||
}
|
||||
|
||||
unwatch()
|
||||
{
|
||||
# 192.168.122.152:8080/unwatch/2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp
|
||||
local btcaddr=${1}
|
||||
|
||||
echo $(invoke_cyphernode "unwatch/${btcaddr}")
|
||||
}
|
||||
|
||||
getactivewatches()
|
||||
{
|
||||
# 192.168.122.152:8080/getactivewatches
|
||||
echo $(invoke_cyphernode "getactivewatches")
|
||||
}
|
||||
|
||||
gettransaction()
|
||||
{
|
||||
# http://192.168.122.152:8080/gettransaction/af867c86000da76df7ddb1054b273ca9e034e8c89d049b5b2795f9f590f67648
|
||||
local txid=${1}
|
||||
|
||||
echo $(invoke_cyphernode "gettransaction/${txid}")
|
||||
}
|
||||
|
||||
spend()
|
||||
{
|
||||
# BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","amount":0.00233}
|
||||
local btcaddr=${1}
|
||||
local amount=${2}
|
||||
local post="{\"address\":\"${btcaddr}\",\"amount\":\"${amount}\"}"
|
||||
|
||||
echo $(invoke_cyphernode "spend" ${post})
|
||||
}
|
||||
|
||||
getbalance()
|
||||
{
|
||||
# http://192.168.122.152:8080/getbalance
|
||||
echo $(invoke_cyphernode "getbalance")
|
||||
}
|
||||
|
||||
getnewaddress()
|
||||
{
|
||||
# http://192.168.122.152:8080/getnewaddress
|
||||
echo $(invoke_cyphernode "getnewaddress")
|
||||
}
|
||||
4
clients/shell/cyphernode.conf
Normal file
4
clients/shell/cyphernode.conf
Normal file
@@ -0,0 +1,4 @@
|
||||
# Provided by cyphernode, see api_auth_docker/README.md
|
||||
h64='eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9Cg=='
|
||||
id=001
|
||||
key=2df1eeea370eacdc5cf7e96c2d82140d1568079a5d4d87006ec8718a98883b36
|
||||
@@ -38,6 +38,7 @@ vi proxy_docker/env.properties
|
||||
```shell
|
||||
vi cron_docker/env.properties
|
||||
vi pycoin_docker/env.properties
|
||||
vi api_auth_docker/env.properties
|
||||
```
|
||||
|
||||
## Create cyphernode user, create proxy DB folder and build images
|
||||
@@ -45,6 +46,9 @@ vi pycoin_docker/env.properties
|
||||
```shell
|
||||
sudo useradd cyphernode
|
||||
mkdir ~/btcproxydb ; sudo chown -R cyphernode:debian ~/btcproxydb ; sudo chmod g+ws ~/btcproxydb
|
||||
mkdir -p ~/cyphernode-ssl/certs ~/cyphernode-ssl/private
|
||||
openssl req -subj '/CN=localhost' -x509 -newkey rsa:4096 -nodes -keyout ~/cyphernode-ssl/private/key.pem -out ~/cyphernode-ssl/certs/cert.pem -days 365
|
||||
docker build -t authapi api_auth_docker/.
|
||||
docker build -t proxycronimg cron_docker/.
|
||||
docker build -t btcproxyimg proxy_docker/.
|
||||
docker build -t pycoinimg pycoin_docker/.
|
||||
@@ -130,6 +134,11 @@ sudo find ~/btcdata -type d -exec chmod 2775 {} \; ; sudo find ~/btcdata -type f
|
||||
|
||||
## Test the deployment
|
||||
|
||||
```shell
|
||||
id="001";h64=$(echo "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="2df1eeea370eacdc5cf7e96c2d82140d1568079a5d4d87006ec8718a98883b36";s=$(echo "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -H "Authorization: Bearer $token" -k https://localhost/getbestblockhash
|
||||
id="003";h64=$(echo "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="b9b8d527a1a27af2ad1697db3521f883760c342fc386dbc42c4efbb1a4d5e0af";s=$(echo "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -H "Authorization: Bearer $token" -k https://localhost/getbalance
|
||||
```
|
||||
|
||||
```shell
|
||||
echo "GET /getbestblockinfo" | docker run --rm -i --network=cyphernodenet alpine nc cyphernode:8888 -
|
||||
echo "GET /getbalance" | docker run --rm -i --network=cyphernodenet alpine nc cyphernode:8888 -
|
||||
|
||||
@@ -51,6 +51,7 @@ debian@dev:~/dev/Cyphernode$ docker network connect cyphernodenet yourappcontain
|
||||
debian@dev:~/dev/Cyphernode$ vi proxy_docker/env.properties
|
||||
debian@dev:~/dev/Cyphernode$ vi cron_docker/env.properties
|
||||
debian@dev:~/dev/Cyphernode$ vi pycoin_docker/env.properties
|
||||
debian@dev:~/dev/Cyphernode$ vi api_auth_docker/env.properties
|
||||
```
|
||||
|
||||
### Build cron image
|
||||
@@ -73,12 +74,17 @@ debian@dev:~/dev/Cyphernode$ vi pycoin_docker/env.properties
|
||||
|
||||
[See how to build clightning image](https://github.com/SatoshiPortal/dockers/tree/master/x86_64/LN/c-lightning)
|
||||
|
||||
### Build the authenticated HTTP API image
|
||||
|
||||
[See how to build authapi image](../api_auth_docker)
|
||||
|
||||
### Deploy
|
||||
|
||||
**Edit docker-compose.yml to specify special deployment constraints or if you want to run the Bitcoin node on the same machine: uncomment corresponding lines.**
|
||||
|
||||
```shell
|
||||
debian@dev:~/dev/Cyphernode$ USER=`id -u cyphernode`:`id -g cyphernode` docker stack deploy --compose-file docker-compose.yml cyphernodestack
|
||||
Creating service cyphernodestack_authapi
|
||||
Creating service cyphernodestack_cyphernode
|
||||
Creating service cyphernodestack_proxycronnode
|
||||
Creating service cyphernodestack_pycoinnode
|
||||
@@ -87,6 +93,8 @@ Creating service cyphernodestack_clightningnode
|
||||
|
||||
## Off-site Bitcoin Node
|
||||
|
||||
This section is useful if you already have a Bitcoin Core node running and you want to use it in Cyphernode. In that case, please comment out the btcnode section from docker-compose.yml.
|
||||
|
||||
### Join swarm created on Cyphernode server
|
||||
|
||||
```shell
|
||||
@@ -103,7 +111,14 @@ pi@SP-BTC01:~ $ docker swarm join --token SWMTKN-1-2pxouynn9g8si42e8g9ujwy0v9po4
|
||||
pi@SP-BTC01:~ $ docker network connect cyphernodenet btcnode
|
||||
```
|
||||
|
||||
## Test deployment (from any host)
|
||||
## Test deployment from outside of the Swarm
|
||||
|
||||
```shell
|
||||
id="001";h64=$(echo "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="2df1eeea370eacdc5cf7e96c2d82140d1568079a5d4d87006ec8718a98883b36";s=$(echo "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -H "Authorization: Bearer $token" -k https://localhost/getbestblockhash
|
||||
id="003";h64=$(echo "{\"alg\":\"HS256\",\"typ\":\"JWT\"}" | base64);p64=$(echo "{\"id\":\"$id\",\"exp\":$((`date +"%s"`+10))}" | base64);k="b9b8d527a1a27af2ad1697db3521f883760c342fc386dbc42c4efbb1a4d5e0af";s=$(echo "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r | cut -sd ' ' -f1);token="$h64.$p64.$s";curl -H "Authorization: Bearer $token" -k https://localhost/getbalance
|
||||
```
|
||||
|
||||
## Test deployment from any host of the swarm
|
||||
|
||||
```shell
|
||||
echo "GET /getbestblockinfo" | docker run --rm -i --network=cyphernodenet alpine nc cyphernode:8888 -
|
||||
|
||||
@@ -1,13 +1,28 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
authapi:
|
||||
# HTTP authentication API gate
|
||||
env_file:
|
||||
- api_auth_docker/env.properties
|
||||
image: authapi
|
||||
ports:
|
||||
# - "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- "~/cyphernode-ssl/certs:/etc/ssl/certs"
|
||||
- "~/cyphernode-ssl/private:/etc/ssl/private"
|
||||
# deploy:
|
||||
# placement:
|
||||
# constraints: [node.hostname==dev]
|
||||
networks:
|
||||
- cyphernodenet
|
||||
|
||||
cyphernode:
|
||||
# Bitcoin Mini Proxy
|
||||
env_file:
|
||||
- proxy_docker/env.properties
|
||||
image: btcproxyimg
|
||||
# ports:
|
||||
# - "8888:8888"
|
||||
volumes:
|
||||
# Variable substitutions don't work
|
||||
# Match with DB_PATH in proxy_docker/env.properties
|
||||
@@ -37,8 +52,6 @@ services:
|
||||
env_file:
|
||||
- pycoin_docker/env.properties
|
||||
image: pycoinimg
|
||||
# ports:
|
||||
# - "7777:7777"
|
||||
# deploy:
|
||||
# placement:
|
||||
# constraints: [node.hostname==dev]
|
||||
@@ -65,10 +78,8 @@ services:
|
||||
image: btcnode
|
||||
# ports:
|
||||
# - "18333:18333"
|
||||
# - "18332:18332"
|
||||
# - "29000:29000"
|
||||
# - "8333:8333"
|
||||
# - "8332:8332"
|
||||
volumes:
|
||||
- "~/btcdata:/.bitcoin"
|
||||
command: $USER bitcoind
|
||||
|
||||
Reference in New Issue
Block a user