#!/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