Files
cyphernode/cyphernodeconf_docker/templates/installer/testfeatures.sh

491 lines
17 KiB
Bash

#!/bin/sh
apk add --update --no-cache openssl curl jq coreutils > /dev/null
. /gatekeeper/keys.properties
checkgatekeeper() {
echo -e "\r\n\e[1;36mTesting Gatekeeper...\e[0;32m" > /dev/console
local rc
local id="001"
local k
eval k='$ukey_'$id
local h64=$(echo -n '{"alg":"HS256","typ":"JWT"}' | basenc --base64url | tr -d '=')
# Let's test expiration: 1 second in payload, request 2 seconds later
local p64=$(echo -n '{"id":"'${id}'","exp":'$(date +"%s")'}' | basenc --base64url | tr -d '=')
local s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r -binary | basenc --base64url | tr -d '=')
local token="$h64.$p64.$s"
echo " Testing expired request... " > /dev/console
rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper:<%= gatekeeper_port %>/v0/getblockinfo)
[ "${rc}" -ne "403" ] && return 10
# Let's test authentication (signature)
p64=$(echo -n '{"id":"'${id}'","exp":'$((`date +"%s"`+10))'}' | basenc --base64url | tr -d '=')
s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r -binary | basenc --base64url | tr -d '=')
token="$h64.$p64.a$s"
echo " Testing bad signature... " > /dev/console
rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper:<%= gatekeeper_port %>/v0/getblockinfo)
[ "${rc}" -ne "401" ] && return 30
# Let's test authorization (action access for groups)
token="$h64.$p64.$s"
echo " Testing watcher trying to do a spender action... " > /dev/console
rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper:<%= gatekeeper_port %>/v0/getbalance)
[ "${rc}" -ne "403" ] && return 40
id="002"
eval k='$ukey_'$id
p64=$(echo -n '{"id":"'${id}'","exp":'$((`date +"%s"`+10))'}' | basenc --base64url | tr -d '=')
s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r -binary | basenc --base64url | tr -d '=')
token="$h64.$p64.$s"
echo " Testing spender trying to do an internal action call... " > /dev/console
rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper:<%= gatekeeper_port %>/v0/conf)
[ "${rc}" -ne "403" ] && return 50
id="003"
eval k='$ukey_'$id
p64=$(echo -n '{"id":"'${id}'","exp":'$((`date +"%s"`+10))'}' | basenc --base64url | tr -d '=')
s=$(echo -n "$h64.$p64" | openssl dgst -hmac "$k" -sha256 -r -binary | basenc --base64url | tr -d '=')
token="$h64.$p64.$s"
echo " Testing admin trying to do an internal action call... " > /dev/console
rc=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $token" --cacert /gatekeeper/certs/cert.pem https://gatekeeper:<%= gatekeeper_port %>/v0/conf)
[ "${rc}" -ne "403" ] && return 60
echo -e "\e[1;36mGatekeeper rocks!" > /dev/console
return 0
}
checkpycoin() {
echo -en "\r\n\e[1;36mTesting Pycoin... " > /dev/console
local rc
rc=$(curl -H "Content-Type: application/json" -d "{\"pub32\":\"upub5GtUcgGed1aGH4HKQ3vMYrsmLXwmHhS1AeX33ZvDgZiyvkGhNTvGd2TA5Lr4v239Fzjj4ZY48t6wTtXUy2yRgapf37QHgt6KWEZ6bgsCLpb\",\"path\":\"0/25-30\"}" -s -o /dev/null -w "%{http_code}" http://proxy:8888/derivepubpath)
[ "${rc}" -ne "200" ] && return 100
echo -e "\e[1;36mPycoin rocks!" > /dev/console
return 0
}
checkpostgres() {
echo -en "\r\n\e[1;36mTesting Postgres... " > /dev/console
local rc
# getbatcher needs the database to return correctly...
rc=$(curl -s -o /dev/null -w "%{http_code}" http://proxy:8888/getbatcher)
[ "${rc}" -ne "200" ] && return 105
echo -e "\e[1;36mPostgres rocks!" > /dev/console
return 0
}
checkbroker() {
echo -en "\r\n\e[1;36mTesting Broker... " > /dev/console
local rc
rc=$(mosquitto_pub -h broker -t "testtopic" -m "testbroker")
[ "$?" -ne "0" ] && return 110
echo -e "\e[1;36mBroker rocks!" > /dev/console
return 0
}
checknotifier() {
echo -en "\r\n\e[1;36mTesting Notifier... " > /dev/console
local response
local returncode
response=$(mosquitto_rr -h broker -W 15 -t notifier -e "response/$$" -m "{\"response-topic\":\"response/$$\",\"cmd\":\"web\",\"url\":\"http://proxy:8888/helloworld\",\"tor\":false}")
returncode=$?
[ "${returncode}" -ne "0" ] && return 115
http_code=$(echo "${response}" | jq -r ".http_code")
[ "${http_code}" -ge "400" ] && return 118
echo -e "\e[1;36mNotifier rocks!" > /dev/console
return 0
}
checkots() {
echo -en "\r\n\e[1;36mTesting OTSclient... " > /dev/console
local rc
rc=$(curl -s -H "Content-Type: application/json" -d '{"hash":"123","callbackUrl":"http://callback"}' http://proxy:8888/ots_stamp)
echo "${rc}" | grep "Invalid hash 123 for sha256" > /dev/null
[ "$?" -ne "0" ] && return 200
echo -e "\e[1;36mOTSclient rocks!" > /dev/console
return 0
}
checktor() {
echo -en "\r\n\e[1;36mTesting Tor... " > /dev/console
local rc
curl -s --socks5-hostname tor:9050 https://check.torproject.org/ | cat | grep -qm 1 Congratulations
[ "$?" -ne "0" ] && return 250
echo -e "\e[1;36mTor rocks!" > /dev/console
return 0
}
checkbitcoinnode() {
echo -en "\r\n\e[1;36mTesting Bitcoin... " > /dev/console
local rc
rc=$(curl -s -o /dev/null -w "%{http_code}" http://proxy:8888/getbestblockhash)
[ "${rc}" -ne "200" ] && return 300
echo -e "\e[1;36mBitcoin node rocks!" > /dev/console
return 0
}
checklnnode() {
echo -en "\r\n\e[1;36mTesting Lightning... " > /dev/console
local rc
rc=$(curl -s -o /dev/null -w "%{http_code}" http://proxy:8888/ln_getinfo)
[ "${rc}" -ne "200" ] && return 400
echo -e "\e[1;36mLN node rocks!" > /dev/console
return 0
}
checkservice() {
local interval=15
local totaltime=180
local outcome
local returncode=0
local endtime=$(($(date +%s) + ${totaltime}))
local result
echo -e "\r\n\e[1;36mTesting if Cyphernode is up and running... \e[0;36mI will keep trying during up to $((${totaltime} / 60)) minutes to give time to Docker to deploy everything...\e[0;32m" > /dev/console
while :
do
outcome=0
for container in gatekeeper proxy proxycron broker notifier pycoin postgres <%= (features.indexOf('otsclient') != -1)?'otsclient ':'' %><%= (features.indexOf('tor') != -1)?'tor ':'' %>bitcoin <%= (features.indexOf('lightning') != -1)?'lightning ':'' %>; do
echo -e " \e[0;32mVerifying \e[0;33m${container}\e[0;32m..." > /dev/console
(ping -c 10 ${container} 2> /dev/null | grep "0% packet loss" > /dev/null) &
eval ${container}=$!
done
for container in gatekeeper proxy proxycron broker notifier pycoin postgres <%= (features.indexOf('otsclient') != -1)?'otsclient ':'' %><%= (features.indexOf('tor') != -1)?'tor ':'' %>bitcoin <%= (features.indexOf('lightning') != -1)?'lightning ':'' %>; do
eval wait '$'${container} ; returncode=$? ; outcome=$((${outcome} + ${returncode}))
eval c_${container}=${returncode}
done
# If '0% packet loss' everywhere or 5 minutes passed, we get out of this loop
([ "${outcome}" -eq "0" ] || [ $(date +%s) -gt ${endtime} ]) && break
echo -e "\e[1;31mCyphernode still not ready, will retry every ${interval} seconds for $((${totaltime} / 60)) minutes ($((${endtime} - $(date +%s))) seconds left)." > /dev/console
sleep ${interval}
done
# "containers": [
# { "name": "gatekeeper", "active":true },
# { "name": "proxy", "active":true },
# { "name": "proxycron", "active":true },
# { "name": "pycoin", "active":true },
# { "name": "postgres", "active":true },
# { "name": "otsclient", "active":true },
# { "name": "tor", "active":true },
# { "name": "bitcoin", "active":true },
# { "name": "lightning", "active":true },
# ]
for container in gatekeeper proxy proxycron broker notifier pycoin postgres <%= (features.indexOf('otsclient') != -1)?'otsclient ':'' %><%= (features.indexOf('tor') != -1)?'tor ':'' %>bitcoin <%= (features.indexOf('lightning') != -1)?'lightning ':'' %>; do
[ -n "${result}" ] && result="${result},"
result="${result}{\"name\":\"${container}\",\"active\":"
eval "returncode=\$c_${container}"
if [ "${returncode}" -eq "0" ]; then
result="${result}true}"
else
result="${result}false}"
fi
done
result="\"containers\":[${result}]"
echo $result
return ${outcome}
}
timeout_feature() {
local interval=15
local totaltime=${2:-120}
local testwhat=${1}
local returncode
local endtime=$(($(date +%s) + ${totaltime}))
while :
do
eval ${testwhat}
returncode=$?
# If no error or 2 minutes passed, we get out of this loop
([ "${returncode}" -eq "0" ] || [ $(date +%s) -gt ${endtime} ]) && break
echo -e "\e[1;31mMaybe it's too early, I'll retry every ${interval} seconds for $((${totaltime} / 60)) minutes ($((${endtime} - $(date +%s))) seconds left)." > /dev/console
sleep ${interval}
done
return ${returncode}
}
feature_status() {
local returncode=${1}
local errormsg=${2}
[ "${returncode}" -eq "0" ] && echo "true"
[ "${returncode}" -ne "0" ] && echo "false" && echo -e "\e[1;31m${errormsg}" > /dev/console
}
# /proxy/installation.json will contain something like that:
#{
# "containers": [
# { "name": "gatekeeper", "active":true },
# { "name": "proxy", "active":true },
# { "name": "proxycron", "active":true },
# { "name": "pycoin", "active":true },
# { "name": "postgres", "active":true },
# { "name": "otsclient", "active":true },
# { "name": "tor", "active":true },
# { "name": "bitcoin", "active":true },
# { "name": "lightning", "active":true },
# ],
# "features": [
# { "name": "gatekeeper", "working":true },
# { "name": "pycoin", "working":true },
# { "name": "postgres", "working":true },
# { "name": "otsclient", "working":true },
# { "name": "tor", "working":true },
# { "name": "bitcoin", "working":true },
# { "name": "lightning", "working":true },
# ]
#}
# Let's first see if everything is up.
echo "EXIT_STATUS=1" > /dist/exitStatus.sh
#############################
# Ping containers and PROXY #
#############################
workingproxy="true"
containers=$(checkservice)
returncode=$?
finalreturncode=${returncode}
if [ "${returncode}" -ne "0" ]; then
echo -e "\e[1;31mCyphernode could not fully start properly within delay." > /dev/console
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"proxy\") | .active")
if [ "${status}" = "false" ]; then
echo -e "\e[1;31mThe Proxy, the main Cyphernode's component, is not responding. We will only test the gatekeeper if its container is up, but you'll see errors for the other components. Please check the logs." > /dev/console
workingproxy="false"
fi
else
echo -e "\e[1;36mCyphernode seems to be correctly deployed. Let's run more thourough tests..." > /dev/console
fi
# Let's now check each feature fonctionality...
# "features": [
# { "name": "gatekeeper", "working":true },
# { "name": "pycoin", "working":true },
# { "name": "postgres", "working":true },
# { "name": "otsclient", "working":true },
# { "name": "tor", "working":true },
# { "name": "bitcoin", "working":true },
# { "name": "lightning", "working":true },
# ]
#############################
# PROXY #
#############################
echo -e "\r\n\e[1;36mWaiting for Proxy to be ready... " > /dev/console
timeout_feature '[ -f "/container_monitor/proxy_ready" ]' 300
#############################
# POSTGRES #
#############################
result="${result},{\"coreFeature\":true, \"name\":\"postgres\",\"working\":"
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"postgres\") | .active")
if [[ "${workingproxy}" = "true" && "${status}" = "true" ]]; then
timeout_feature checkpostgres
returncode=$?
else
returncode=1
fi
finalreturncode=$((${returncode} | ${finalreturncode}))
result="${result}$(feature_status ${returncode} 'Postgres error!')}"
#############################
# GATEKEEPER #
#############################
result="${containers},\"features\":[{\"coreFeature\":true, \"name\":\"proxy\",\"working\":${workingproxy}}, {\"coreFeature\":true, \"name\":\"gatekeeper\",\"working\":"
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"gatekeeper\") | .active")
if [ "${status}" = "true" ]; then
timeout_feature checkgatekeeper
returncode=$?
else
returncode=1
fi
finalreturncode=$((${returncode} | ${finalreturncode}))
result="${result}$(feature_status ${returncode} 'Gatekeeper error!')}"
#############################
# BROKER #
#############################
result="${result},{\"coreFeature\":true, \"name\":\"broker\",\"working\":"
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"broker\") | .active")
if [[ "${workingproxy}" = "true" && "${status}" = "true" ]]; then
timeout_feature checkbroker
returncode=$?
else
returncode=1
fi
finalreturncode=$((${returncode} | ${finalreturncode}))
result="${result}$(feature_status ${returncode} 'Broker error!')}"
#############################
# NOTIFIER #
#############################
result="${result},{\"coreFeature\":true, \"name\":\"notifier\",\"working\":"
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"notifier\") | .active")
if [[ "${workingproxy}" = "true" && "${status}" = "true" ]]; then
timeout_feature checknotifier
returncode=$?
else
returncode=1
fi
finalreturncode=$((${returncode} | ${finalreturncode}))
result="${result}$(feature_status ${returncode} 'Notifier error!')}"
#############################
# PYCOIN #
#############################
result="${result},{\"coreFeature\":true, \"name\":\"pycoin\",\"working\":"
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"pycoin\") | .active")
if [[ "${workingproxy}" = "true" && "${status}" = "true" ]]; then
timeout_feature checkpycoin
returncode=$?
else
returncode=1
fi
finalreturncode=$((${returncode} | ${finalreturncode}))
result="${result}$(feature_status ${returncode} 'Pycoin error!')}"
<% if (features.indexOf('otsclient') != -1) { %>
#############################
# OTSCLIENT #
#############################
result="${result},{\"coreFeature\":false, \"name\":\"otsclient\",\"working\":"
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"otsclient\") | .active")
if [[ "${workingproxy}" = "true" && "${status}" = "true" ]]; then
timeout_feature checkots
returncode=$?
else
returncode=1
fi
finalreturncode=$((${returncode} | ${finalreturncode}))
result="${result}$(feature_status ${returncode} 'OTSclient error!')}"
<% } %>
<% if (features.indexOf('tor') != -1) { %>
#############################
# TOR #
#############################
echo -e "\r\n\e[1;36mWaiting for Tor to be ready... " > /dev/console
timeout_feature '[ -f "/container_monitor/tor_ready" ]'
result="${result},{\"coreFeature\":false, \"name\":\"tor\",\"working\":"
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"tor\") | .active")
if [[ "${workingproxy}" = "true" && "${status}" = "true" ]]; then
timeout_feature checktor
returncode=$?
else
returncode=1
fi
finalreturncode=$((${returncode} | ${finalreturncode}))
result="${result}$(feature_status ${returncode} 'Tor error!')}"
<% } %>
#############################
# BITCOIN #
#############################
echo -e "\r\n\e[1;36mWaiting for Bitcoin Core to be ready... " > /dev/console
timeout_feature '[ -f "/container_monitor/bitcoin_ready" ]'
result="${result},{\"coreFeature\":true, \"name\":\"bitcoin\",\"working\":"
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"bitcoin\") | .active")
if [[ "${workingproxy}" = "true" && "${status}" = "true" ]]; then
timeout_feature checkbitcoinnode
returncode=$?
else
returncode=1
fi
finalreturncode=$((${returncode} | ${finalreturncode}))
result="${result}$(feature_status ${returncode} 'Bitcoin error!')}"
<% if (features.indexOf('lightning') != -1) { %>
#############################
# LIGHTNING #
#############################
echo -e "\r\n\e[1;36mWaiting for C-Lightning to be ready... " > /dev/console
timeout_feature '[ -f "/container_monitor/lightning_ready" ]'
result="${result},{\"coreFeature\":false, \"name\":\"lightning\",\"working\":"
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"lightning\") | .active")
if [[ "${workingproxy}" = "true" && "${status}" = "true" ]]; then
timeout_feature checklnnode
returncode=$?
else
returncode=1
fi
finalreturncode=$((${returncode} | ${finalreturncode}))
result="${result}$(feature_status ${returncode} 'Lightning error!')}"
<% } %>
#############################
result="{${result}]}"
echo "${result}" > /gatekeeper/installation.json
echo "EXIT_STATUS=${finalreturncode}" > /dist/exitStatus.sh
<% if (features.indexOf('tor') !== -1 && torifyables && torifyables.indexOf('tor_traefik') !== -1) { %>
echo "TOR_TRAEFIK_HOSTNAME=$(cat /dist/.cyphernodeconf/tor/traefik/hidden_service/hostname)" >> /dist/exitStatus.sh
<% } %>