#!/bin/bash
# ----------------------------------------------------------------------
# File: eos-https-functional-test
# Author: Manuel Reis - CERN
# ----------------------------------------------------------------------
# ************************************************************************
# * EOS - the CERN Disk Storage System *
# * Copyright (C) 2018 CERN/Switzerland *
# * *
# * This program is free software: you can redistribute it and/or modify *
# * it under the terms of the GNU General Public License as published by *
# * the Free Software Foundation, either version 3 of the License, or *
# * (at your option) any later version. *
# * *
# * This program is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU General Public License for more details. *
# * *
# * You should have received a copy of the GNU General Public License *
# * along with this program. If not, see .*
# ************************************************************************
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# This is a simple script that aims to check HTTP transfers with different authentication methods #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
usage() { echo '''Usage: eos-https-functional-test [-h|--host [:| (-p|--port) ]]
[-c|--cert ]
[-k|--key ]
[-C|--cacert ]
[-P|--capath ]
[-t|--dir ]
[-v|--verbose]
[--help] - usage & exit
'''; }
# Parser from: https://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash
usage_error () { echo >&2 "$(basename "$0"): $1"; exit 2; }
assert_argument () { test "$1" != "$EOL" || usage_error "$2 requires an argument"; }
port="${EOS_HTTPS_PORT:-443}"
host=$(hostname -f)
cacert="/etc/grid-security/certificates/rootCA.pem"
capath="$(dirname "${cacert}")"
cert="/root/.globus/usercert.pem"
key="/root/.globus/userkey.pem"
# One loop, nothing more.
if [ "$#" != 0 ]; then
EOL=$(printf '\1\3\3\7')
set -- "$@" "$EOL"
while [ "$1" != "$EOL" ]; do
opt="$1"; shift
case "$opt" in
# Your options go here.
--macaroons) macaroons=1;;
--tpc) tpc=1;;
-v|--verbose) verbose="-v";;
-h|--host) assert_argument "$1" "$opt"; host="${1%:*}" && port="${1#*:}"; shift;;
-p|--port) assert_argument "$1" "$opt"; port="$1"; shift;;
-c|--cert) assert_argument "$1" "$opt"; cert="$1"; shift;;
-k|--key) assert_argument "$1" "$opt"; key="$1"; shift;;
-C|--cacert) assert_argument "$1" "$opt"; cacert="${1}" && capath="$(dirname "${cacert}")"; shift;;
-P|--capath) assert_argument "$1" "$opt"; capath="${1}"; shift;;
-t|--dir) assert_argument "$1" "$opt"; testdir="${1}"; shift;;
--help) usage $0; exit 0;;
# Arguments processing. You may remove any unneeded line after the 1st.
-|''|[!-]*) set -- "$@" "$opt";; # positional argument, rotate to the end
--*=*) set -- "${opt%%=*}" "${opt#*=}" "$@";; # convert '--name=arg' to '--name' 'arg'
-[!-]?*) set -- "$(echo "${opt#-}" | sed 's /g')" "$@";; # convert '-abc' to '-a' '-b' '-c'
--) while [ "$1" != "$EOL" ]; do set -- "$@" "$1"; shift; done;; # process remaining arguments as positional
-*) usage_error "unknown option: '$opt'";; # catch misspelled options
*) usage_error "this should NEVER happen ($opt)";; # sanity test for previous patterns
esac
done
shift # $EOL
fi
echo "Results:"
### X509
# Returns 0 if successful, 1 if upload failed, 2 if download failed, 3 if both failed
x509() {
respUpload="$(curl -sS ${verbose} -I -L --capath "${capath}" --cert "${cert}" --cacert "${cacert}" --key "${key}" "https://${host}:${port}/${testdir}/helloworld.txt" --upload-file <(echo ola) | grep HTTP | tr -d '\r' | paste -sd'+')"
echo -e "\tx509 upload -> ${respUpload}"
respDownload="$(curl -sS ${verbose} -I -L --capath "${capath}" --cert "${cert}" --cacert "${cacert}" --key "${key}" "https://${host}:${port}/${testdir}/helloworld.txt" |grep HTTP | tr -d '\r' | paste -sd'+')"
echo -e "\tx509 download -> ${respDownload}" # should be 0
[[ $respUpload == "HTTP/1.1 307 TEMPORARY_REDIRECT+HTTP/1.1 201 CREATED" ]]
upcode=$?
[[ $respDownload == "HTTP/1.1 200 OK" ]]
downcode=$?
return $((2#$downcode$upcode))
}
### Macaroon
# Returns 0 if successful, 1 if upload failed, 2 if download failed, 3 if both failed
macaroon() {
# certificate and private key
export X509_USER_CERT=${cert}
export X509_USER_KEY=${key}
# Use the macaroon-init tool to request a macaroon: yum install x509-scitokens-issuer-client --enablerepo="osg-development"
macaroon-init -v foo >/dev/null 2>&1 || { echo -e >&2 "I require macaroon-init but it's not installed. Aborting.\n"
"yum install https://repo.opensciencegrid.org/osg/3.4/osg-3.4-el$(uname -r | awk -F '.' '$0=$(NF-1)')-release-latest.rpm\n"
"yum install --enablerepo=osg-development -y x509-scitokens-issuer-client";
exit 256;
}
export MACAROON="$(macaroon-init https://${host}:${port}/${testdir}/ 60 UPLOAD,DOWNLOAD)"
respMacaroonUpload="$(curl -I -sS ${verbose} -L -H "Authorization: Bearer ${MACAROON}" https://${host}:${port}${testdir}/helloworld-macaroon.txt --upload-file <(echo ola) | grep HTTP | tr -d '\r' | paste -sd'+')"
echo -e "\tmacaroon upload -> ${respMacaroonUpload}"
respMacaroonDownload="$(curl -I -sS ${verbose} -L -H "Authorization: Bearer ${MACAROON}" https://${host}:${port}${testdir}/helloworld-macaroon.txt|grep HTTP | tr -d '\r' | paste -sd'+')"
echo -e "\tmacaroon download -> ${respMacaroonDownload}"
[[ $respMacaroonUpload == "HTTP/1.1 307 TEMPORARY_REDIRECT+HTTP/1.1 201 CREATED" ]]
macaroonupcode=$?
[[ $respMacaroonDownload == "HTTP/1.1 200 OK" ]]
macaroondowncode=$?
return $(( 2#$macaroondowncode$macaroonupcode ))
}
### TPC
# Returns 0 if successful, 1 if tcp pull failed, 2 if tcp push failed, 3 if both failed
tcp() {
export SRC=https://${host}:${port}/${testdir}/helloworld.txt
export DST_PULL=https://${host}:${port}/${testdir}/helloworld-tcp-pull.txt
export DST_PUSH=https://${host}:${port}/${testdir}/helloworld-tcp-push.txt
export TSRC=$(curl -sS -4 --capath "${capath}" --cert "${cert}" --cacert "${cacert}" --key "${key}" -X POST -H 'Content-Type: application/macaroon-request' -d '{"caveats": ["activity:DOWNLOAD"], "validity": "PT3000M"}' "$SRC" | jq -r '.macaroon')
export TDST_PULL=$(curl -sS -4 --capath "${capath}" --cert "${cert}" --cacert "${cacert}" --key "${key}" -X POST -H 'Content-Type: application/macaroon-request' -d '{"caveats": ["activity:UPLOAD,DELETE,LIST,MANAGE"], "validity": "PT3000M"}' "$DST_PULL" | jq -r '.macaroon')
export TDST_PUSH=$(curl -sS -4 --capath "${capath}" --cert "${cert}" --cacert "${cacert}" --key "${key}" -X POST -H 'Content-Type: application/macaroon-request' -d '{"caveats": ["activity:UPLOAD,DELETE,LIST,MANAGE"], "validity": "PT3000M"}' "$DST_PUSH" | jq -r '.macaroon')
if [[ ${verbose} == "-v" ]]; then
echo -n "Transfer Source Token: "
echo "${TSRC}" | base64 -d
echo -n "Pull Transfer Destination Token: "
echo "${TDST_PULL}" | base64 -d
echo -n "Push Transfer Destination Token: "
echo "${TDST_PUSH}" | base64 -d
fi
respTpcPull="$(curl -I -sS -4 ${verbose} --capath "${capath}" -L \
-X COPY \
-H 'Secure-Redirection: 1' \
-H 'X-No-Delegate: 1' \
-H 'Credentials: none' \
-H "Authorization: Bearer $TDST_PULL" \
-H "TransferHeaderAuthorization: Bearer $TSRC" \
-H "TransferHeaderTest: Test" \
-H "Source: $SRC" \
"$DST_PULL" |grep HTTP | tr -d '\r' | paste -sd'+')"
echo -e "\ttpc pull -> ${respTpcPull}"
[[ ${respTpcPull} == "HTTP/1.1 307 Temporary Redirect+HTTP/1.1 201 Created" ]]
tpcpullcode=$?
respTpcPush="$(curl -I -sS -4 ${verbose} --capath "${capath}" -L \
-X COPY \
-H 'Secure-Redirection: 1' \
-H 'X-No-Delegate: 1' \
-H 'Credentials: none' \
-H "Authorization: Bearer $TSRC" \
-H "TransferHeaderAuthorization: Bearer $TDST_PUSH" \
-H "TransferHeaderTest: Test" \
-H "Destination: $DST_PUSH" \
"$SRC" |grep HTTP | tr -d '\r' | paste -sd'+')"
echo -e "\ttpc push -> ${respTpcPush}"
[[ ${respTpcPush} == "HTTP/1.1 307 Temporary Redirect+HTTP/1.1 201 Created" ]]
tpcpushcode=$?
return $((2#$tpcpushcode$tpcpullcode))
}
# EOS token
eostoken() {
# certificate and private key
export X509_USER_CERT=${cert}
export X509_USER_KEY=${key}
echo "info: enable token generation"
eos space config default space.token.generation=1
EXPIRE=`date +%s`; LATER=$(($EXPIRE+300));
echo "info: create client proxy certificate"
voms-proxy-init
# Make destination directory only accessible to eos-user
eos chmod 700 ${testdir}
export EOS_RW_TOKEN=`XRD_LOGLEVEL=Dump XrdSecPROTOCOL=gsi eos root://${host}/ token --path ${testdir}/ --tree --permissions rwx --expires $LATER`
if [[ $EOS_RW_TOKEN != "zteos64:"* ]]; then
echo -e '\terror: eos rw token command failed'
return 1
fi
export EOS_RO_TOKEN=`XrdSecPROTOCOL=gsi eos root://${host}/ token --path ${testdir}/ --tree --permissions rx --expires $LATER`
if [[ $EOS_RO_TOKEN != "zteos64:"* ]]; then
echo -e '\terror: eos ro token command failed'
return 1
fi
respEosTokenRwUpload="$(curl -I -sS ${verbose} -L -H "Authorization: Bearer ${EOS_RW_TOKEN}" https://${host}:${port}${testdir}/helloworld-eostoken.txt --upload-file <(echo ola-token) | grep HTTP | tr -d '\r' | paste -sd'+')"
echo -e "\teostoken rw upload -> ${respEosTokenRwUpload}"
[[ $respEosTokenRwUpload == "HTTP/1.1 307 TEMPORARY_REDIRECT+HTTP/1.1 201 CREATED" ]]
eostoken_rw_up=$?
respEosTokenRwDownload="$(curl -I -sS ${verbose} -L -H "Authorization: Bearer ${EOS_RW_TOKEN}" https://${host}:${port}${testdir}/helloworld-eostoken.txt|grep HTTP | tr -d '\r' | paste -sd'+')"
echo -e "\teostoken rw download -> ${respEosTokenRwDownload}"
[[ $respEosTokenRwDownload == "HTTP/1.1 200 OK" ]]
eostoken_rw_down=$?
respEosTokenRoDownload="$(curl -I -sS ${verbose} -L -H "Authorization: Bearer ${EOS_RO_TOKEN}" https://${host}:${port}${testdir}/helloworld-eostoken.txt|grep HTTP | tr -d '\r' | paste -sd'+')"
echo -e "\teostoken ro download -> ${respEosTokenRoDownload}"
[[ $respEosTokenRoDownload == "HTTP/1.1 200 OK" ]]
eostoken_ro_down=$?
# Make destination directory read-only and try to upload with RO token
eos chmod 500 ${testdir}
respEosTokenRoUpload="$(curl -I -sS ${verbose} -L -H "Authorization: Bearer ${EOS_RO_TOKEN}" https://${host}:${port}${testdir}/helloworld-eostoken-ro.txt --upload-file <(echo ola-token) | grep HTTP | tr -d '\r' | paste -sd'+')"
echo -e "\teostoken ro upload -> ${respEosTokenRoUpload}"
[[ $respEosTokenRoUpload == "HTTP/1.1 403 FORBIDDEN" ]]
eostoken_ro_up=$?
eos chmod 750 ${testdir}
echo "info: destroy client proxy certificate"
voms-proxy-destroy
return $((2#$eostoken_rw_up$eostoken_rw_down$eostoken_ro_down$eostoken_ro_up))
}
echo "info: prepare https test directory ${testdir}"
eos mkdir -p ${testdir}
eos chown eos-user:eos-user ${testdir}
x509
x509code=$?
echo "x509:"$x509code
# @todo(esindril) disabled for the moment since macaroon-init not available
# any more from the osg repos
#macaroon
#macarooncode=$?
#echo "macaroon:"$macarooncode
eostoken
eostokencode=$?
echo "eostoken:"$eostokencode
tcp
tcpcode=$?
echo "tpc:"$tcpcode
echo "info: cleanup https test directory"
eos ls -lrta ${testdir}
eos rm -rF ${testdir}
exit $x509code$macarooncode$eostokencode$tpccode