diff --git a/Jenkinsfile.public b/Jenkinsfile.public index 5bdc4e3bb..94fdcc9ac 100644 --- a/Jenkinsfile.public +++ b/Jenkinsfile.public @@ -89,6 +89,7 @@ pipeline { steps { sh 'psql -U postgres -c \'create database teststorj2;\'' sh 'make test-sim' + sh 'make test-certificates' } } diff --git a/Makefile b/Makefile index 13379f942..780c0b126 100644 --- a/Makefile +++ b/Makefile @@ -97,10 +97,10 @@ test-sim: ## Test source with storj-sim (jenkins) @echo "Running ${@}" @./scripts/test-sim.sh -.PHONY: test-certificate-signing -test-certificate-signing: ## Test certificate signing service and storagenode setup (jenkins) +.PHONY: test-certificates +test-certificates: ## Test certificate signing service and storagenode setup (jenkins) @echo "Running ${@}" - @./scripts/test-certificate-signing.sh + @./scripts/test-certificates.sh .PHONY: test-docker test-docker: ## Run tests in Docker diff --git a/cmd/certificates/auth.go b/cmd/certificates/auth.go index 9cd3ada80..caf3a42b9 100644 --- a/cmd/certificates/auth.go +++ b/cmd/certificates/auth.go @@ -257,7 +257,12 @@ func writeAuthExport(ctx context.Context, authDB *certificates.AuthorizationDB, var authErrs errs.Group for _, auth := range auths { - if err := w.Write([]string{email, auth.Token.String()}); err != nil { + isClaimed := "false" + if auth.Claim != nil { + isClaimed = "true" + } + + if err := w.Write([]string{email, auth.Token.String(), isClaimed}); err != nil { authErrs.Add(err) } } diff --git a/cmd/certificates/main.go b/cmd/certificates/main.go index 92bc0b960..309dfd537 100644 --- a/cmd/certificates/main.go +++ b/cmd/certificates/main.go @@ -77,8 +77,7 @@ func main() { defaultConfDir := fpath.ApplicationDir("storj", "cert-signing") defaultIdentityDir := fpath.ApplicationDir("storj", "identity", "certificates") cfgstruct.SetupFlag(zap.L(), rootCmd, &confDir, "config-dir", defaultConfDir, "main directory for certificates configuration") - //cfgstruct.SetupFlag(zap.L(), rootCmd, &identityDir, "identity-dir", fpath.ApplicationDir("storj", "identity", "bootstrap"), "main directory for bootstrap identity credentials") - rootCmd.PersistentFlags().StringVar(&identityDir, "identity-dir", defaultIdentityDir, "main directory for storagenode identity credentials") + cfgstruct.SetupFlag(zap.L(), rootCmd, &identityDir, "identity-dir", defaultIdentityDir, "main directory for bootstrap identity credentials") defaults := cfgstruct.DefaultsFlag(rootCmd) rootCmd.AddCommand(authCmd) diff --git a/scripts/test-certificate-signing.sh b/scripts/test-certificate-signing.sh deleted file mode 100755 index fd510b36e..000000000 --- a/scripts/test-certificate-signing.sh +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/env bash -set -o errexit - -trap "echo ERROR: exiting due to error; exit" ERR -trap "exit" INT TERM - -. $(dirname $0)/utils.sh - -failures=3 -user_id="user@mail.test" -signer_address="127.0.0.1:8888" -difficulty=16 - -cleanup() { - if [[ ! -z ${bg+x} ]]; then - kill ${bg} - fi - - dirs="$tmp $tmp_build_dir" - for dir in ${dirs}; do - if [[ ! -z ${dir+x} ]]; then - rm -rf ${dir} - fi - done -} -if [[ ${TRAVIS} == true ]]; then - declare_cmds storagenode certificates -else - temp_build storagenode certificates -fi -tmp=$(mktemp -d) -trap "cleanup" EXIT - - -certificates_dir=${tmp}/cert-signing -storagenode_dir=${tmp}/storagenode - -# TODO: create separate signer CA and use `--signer.ca` options -# --signer.ca.cert-path ${signer_cert} \ -# --signer.ca.key-path ${signer_key} \ - -echo "setting up certificate signing server" -$certificates setup --config-dir ${certificates_dir} \ - --signer.min-difficulty ${difficulty} - -echo "creating test authorization" -$certificates auth create --config-dir ${certificates_dir} \ - 1 ${user_id} >/dev/null 2>&1 - - -export_tokens() { - $certificates auth export --config-dir ${certificates_dir} \ - --out - - -} -token=$(export_tokens 2>&1|cut -d , -f 2|grep -oE "$user_id:\w+") - -echo "starting certificate signing server" -$certificates run --config-dir ${certificates_dir} \ - --server.address ${signer_address} >/dev/null 2>&1 & - -bg=$! -sleep 1 - -echo "setting up storage node" -$storagenode setup --config-dir ${storagenode_dir} \ - --ca.difficulty ${difficulty} \ - --signer.address ${signer_address} \ - --signer.auth-token ${token} - -ca_chain_len=$(cat ${storagenode_dir}/ca.cert|grep "BEGIN CERTIFICATE"|wc -l) -ident_chain_len=$(cat ${storagenode_dir}/identity.cert|grep "BEGIN CERTIFICATE"|wc -l) - -echo "Checks (${failures}):" - -if [[ ${ca_chain_len} == 2 ]]; then - echo " - ca chain length is correct" - failures=$((failures-1)) -else - echo " - FAIL: incorrect storage node CA chain length; expected: 2; actual: ${ca_chain_len}" -fi -if [[ ${ident_chain_len} == 3 ]]; then - echo " - identity chain length is correct" - failures=$((failures-1)) -else - echo " - FAIL: incorrect storage node identity chain length; expected: 2; actual: ${ident_chain_len}" -fi - -verify=$(${certificates} verify --config-dir ${certificates_dir} --log.level error 2>&1) -if [[ ! -n ${verify} ]]; then - echo " - certificates verified" - failures=$((failures-1)) -else - echo " - FAIL: certificate verification error" - echo " ${verify}" -fi - -if [[ ${failures} == 0 ]]; then - echo "SUCCESS: all expectations met!" -else - echo "FAILURE: ${failures} checks failed" -fi - -exit ${failures} diff --git a/scripts/test-certificates.sh b/scripts/test-certificates.sh new file mode 100755 index 000000000..808e92409 --- /dev/null +++ b/scripts/test-certificates.sh @@ -0,0 +1,124 @@ +#!/usr/bin/env bash +set -ueo pipefail +source $(dirname $0)/utils.sh + +TMPDIR=$(mktemp -d -t tmp.XXXXXXXXXX) +IDENTS_DIR=$TMPDIR/identities +CERTS_DIR=$TMPDIR/certificates +CERTS_ADDR=127.0.0.4:11000 +CERTS_ADDR_PRIV=127.0.0.4:11001 + +kill_certificates_server() { + kill $CERTS_PID +} + +cleanup() { + if [[ -n $(ps | grep "certificates") ]]; then + kill_certificates_server + fi + rm -rf "$TMPDIR" + echo "cleaned up test successfully" +} + +trap cleanup EXIT INT + +_certificates() { + subcommand=$1 + shift + + ident_dir="${IDENTS_DIR}/certificates" + ca_cert_path="${ident_dir}/ca.cert" + ca_key_path="${ident_dir}/ca.key" + rev_dburl="bolt://${CERTS_DIR}/revocations.db" + + # NB: `--identity-dir` and `--config-dir` flags are only bound globally to subcommands + exec certificates --identity-dir "$ident_dir" \ + --config-dir "$CERTS_DIR" \ + "$subcommand" \ + --signer.ca.cert-path "$ca_cert_path" \ + --signer.ca.key-path "$ca_key_path" \ + --server.address "$CERTS_ADDR" \ + --server.private-address "$CERTS_ADDR_PRIV" \ + --server.revocation-dburl="$rev_dburl" \ + --log.level warn \ + "$@" +} + +_identity() { + subcommand=$1 + rev_dburl="bolt://${IDENTS_DIR}/revocations.db" + shift + + # NB: `--identity-dir` and `--config-dir` flags are only bound globally to subcommands + identity --identity-dir "$IDENTS_DIR" \ + "$subcommand" \ + --signer.tls.revocation-dburl "$rev_dburl" \ + --log.level info \ + "$@" +} + +_identity_create() { + _identity create $1 --difficulty 0 --concurrency 1 >/dev/null +} + +_identity_create 'certificates' +_certificates setup & +wait + +for i in {0..4}; do + email="testuser${i}@mail.example" + ident_name="testidentity${i}" + + _identity_create $ident_name + + if [[ i -gt 0 ]]; then + _certificates auth create "$i" "$email" & + wait + fi +done + +exported_auths=$(_certificates auth export) +_certificates run --signer.min-difficulty 0 & +CERTS_PID=$! + +sleep 1 + +for i in {1..4}; do + email="testuser${i}@mail.example" + ident_name="testidentity${i}" + + token=$(echo "$exported_auths" | grep "$email" | head -n 1 | awk -F , '{print $2}') + _identity authorize --signer.address "$CERTS_ADDR" "$ident_name" "$token" > /dev/null +done + +# NB: Certificates server uses bolt by default so it must be shut down before we can export. +kill_certificates_server + +# Expect 10 authorizations total. +auths=$(_certificates auth export) +require_lines 10 "$auths" $LINENO + +for i in {1..4}; do + email="testuser${i}@mail.example" + claimed_auth_count=0 + + # Expect number of auths for a given user to equal the identity/email number. + # (e.g. testidentity3/testuser3@mail.example should have 3 auths) + match_auths=$(echo "$auths" | grep "$email" ) + require_lines $i "$match_auths" $LINENO + + for auth in $match_auths; do + claimed=$(echo "$auth" | awk -F , '{print $3}') + if [[ $claimed == "true" ]]; then + ((++claimed_auth_count)) + continue + fi + # Expect unclaimed auths to have "false" as the third field. + require_equal "false" "$claimed" $LINENO + done + + # Expect 4 auths (one for each user) to be claimed. + require_equal "1" "$claimed_auth_count" $LINENO +done + +echo "TEST COMPLETED SUCCESSFULLY!" diff --git a/scripts/utils.sh b/scripts/utils.sh index b48042fcc..e7c36297e 100644 --- a/scripts/utils.sh +++ b/scripts/utils.sh @@ -1,5 +1,41 @@ #!/usr/bin/env bash +new_error() { + file=$0 + err_msg=$1 + line_no=$2 + + echo -e "ERROR: ${file}: line ${line_no}: ${err_msg}" + exit 1 +} + +require_empty() { + line_no=$2 + + if [[ -z $(sed -e 's/^[[:space:]]*//') ]]; then + new_error "expected \"$1\" to be an empty string" $line_no + fi +} + +require_equal() { + a=$1 + b=$2 + line_no=$3 + + if [[ "$a" != "$b" ]]; then + new_error "expected equal:\n$(diff <(echo $a) <(echo $b))" $line_no + fi +} + +require_lines() { + line_no=$3 + string=$2 + line_count=$(echo "$string" | wc -l) + if [[ "$line_count" -lt "$1" ]]; then + new_error "expected number of lines ${line_count} to be ${1}:\n$2" $line_no + fi +} + dots() { echo -n "." sleep 1