From e54ab39958301a320e2e426410cef1edd123327d Mon Sep 17 00:00:00 2001 From: Michael Peter Christen Date: Wed, 9 Dec 2020 02:36:55 +0100 Subject: [PATCH] Going back to basic authentication for console/shell commands This does not affect security because: - it is going to localhost only - only users who have already access to the pw hash can do this - no clear text pw is transmitted because that is not stored anywhere The switch to basic is required because these commands are required in the context of hosting on root servers and docker containers where a password change must be done. But the password shell command was not working without password which made the concept unusable. This deficit made it virtually impossible for root server operators to use YaCy because they had been unable to set up a proper password. --- bin/apicall.sh | 19 ++++++----- bin/clearall.sh | 2 +- bin/clearcache.sh | 2 +- bin/clearindex.sh | 2 +- bin/deleteurl.sh | 2 +- bin/importmediawiki.sh | 2 +- bin/passwd.sh | 4 +-- .../yacy/http/Jetty9YaCySecurityHandler.java | 34 +++++++++---------- stopYACY.sh | 2 +- 9 files changed, 35 insertions(+), 34 deletions(-) diff --git a/bin/apicall.sh b/bin/apicall.sh index bfdcbc680..9f9ad70b5 100755 --- a/bin/apicall.sh +++ b/bin/apicall.sh @@ -21,10 +21,11 @@ port=$(grep ^port= "$YACY_DATA_PATH/SETTINGS/yacy.conf" |cut -d= -f2) admin=$(grep ^adminAccountUserName= "$YACY_DATA_PATH/SETTINGS/yacy.conf" |cut -d= -f2) adminAccountForLocalhost=$(grep ^adminAccountForLocalhost= "$YACY_DATA_PATH/SETTINGS/yacy.conf" | cut -d= -f2) -if grep "BASIC" "$YACY_APP_PATH/defaults/web.xml" > /dev/null; then - # When authentication method is in basic mode, use directly the password hash from the configuration file - YACY_ADMIN_PASSWORD=$(grep ^adminAccountBase64MD5= "$YACY_DATA_PATH/SETTINGS/yacy.conf" |cut -d= -f2) -fi +# Use directly the password hash from the configuration file. This is accepted as PW when the call comes from localhost. +# This exception in authorization handling makes it possible that users with access to the YaCy configuration files can administrate +# a peer without manual authentication input. This works only with Basic auth method. +# This is not a huge security problem because the target address is always localhost. +YACY_ADMIN_PASSWORD=$(grep ^adminAccountBase64MD5= "$YACY_DATA_PATH/SETTINGS/yacy.conf" |cut -d= -f2) if which curl > /dev/null; then if [ "$adminAccountForLocalhost" = "true" ]; then @@ -32,21 +33,21 @@ if which curl > /dev/null; then curl -sSf "http://127.0.0.1:$port/$1" elif [ -n "$YACY_ADMIN_PASSWORD" ]; then # admin password is provided as environment variable : let's use it - curl -sSf --anyauth -u "$admin:$YACY_ADMIN_PASSWORD" "http://127.0.0.1:$port/$1" + curl -sSf --basic -u "$admin:$YACY_ADMIN_PASSWORD" "http://127.0.0.1:$port/$1" else # no password environment variable : it will be asked interactively - curl -sSf --anyauth -u "$admin" "http://127.0.0.1:$port/$1" + curl -sSf --basic -u "$admin" "http://127.0.0.1:$port/$1" fi elif which wget > /dev/null; then if [ "$adminAccountForLocalhost" = "true" ]; then # localhost access as administrator without authentication is enabled - wget -nv -t 1 --timeout=120 "http://127.0.0.1:$port/$1" -O - + wget -nv --auth-no-challenge -t 1 --timeout=120 "http://127.0.0.1:$port/$1" -O - elif [ -n "$YACY_ADMIN_PASSWORD" ]; then # admin password is provided as environment variable : let's use it - wget -nv -t 1 --timeout=120 --http-user "$admin" --http-password "$YACY_ADMIN_PASSWORD" "http://127.0.0.1:$port/$1" -O - + wget -nv --auth-no-challenge -t 1 --timeout=120 --http-user "$admin" --http-password "$YACY_ADMIN_PASSWORD" "http://127.0.0.1:$port/$1" -O - else # no password environment variable : it will be asked interactively - wget -nv -t 1 --timeout=120 --http-user "$admin" --ask-password "http://127.0.0.1:$port/$1" -O - + wget -nv --auth-no-challenge -t 1 --timeout=120 --http-user "$admin" --ask-password "http://127.0.0.1:$port/$1" -O - fi else echo "Please install curl or wget" > /dev/stderr diff --git a/bin/clearall.sh b/bin/clearall.sh index 885379c9c..2f157f6a9 100755 --- a/bin/clearall.sh +++ b/bin/clearall.sh @@ -1,3 +1,3 @@ #!/usr/bin/env sh cd "`dirname $0`" -./protectedPostApiCall.sh "IndexControlURLs_p.html" "deletecomplete=&deleteIndex=on&deleteSolr=on&deleteCrawlQueues=on&deleteRobots=on&deleteSearchFl=on&deleteCache=on" \ No newline at end of file +./apicall.sh "IndexControlURLs_p.html" "deletecomplete=&deleteIndex=on&deleteSolr=on&deleteCrawlQueues=on&deleteRobots=on&deleteSearchFl=on&deleteCache=on" > /dev/null diff --git a/bin/clearcache.sh b/bin/clearcache.sh index 926d70e78..97de5f40e 100755 --- a/bin/clearcache.sh +++ b/bin/clearcache.sh @@ -1,3 +1,3 @@ #!/usr/bin/env sh cd "`dirname $0`" -./protectedPostApiCall.sh "IndexControlURLs_p.html" "deleteIndex=off&deleteSolr=off&deleteCache=on&deleteCrawlQueues=off&deleteRobots=on&deleteSearchFl=on&deletecomplete=" +./apicall.sh "IndexControlURLs_p.html" "deleteIndex=off&deleteSolr=off&deleteCache=on&deleteCrawlQueues=off&deleteRobots=on&deleteSearchFl=on&deletecomplete=" > /dev/null diff --git a/bin/clearindex.sh b/bin/clearindex.sh index 9c78bb997..773bb824b 100755 --- a/bin/clearindex.sh +++ b/bin/clearindex.sh @@ -1,3 +1,3 @@ #!/usr/bin/env sh cd "`dirname $0`" -./protectedPostApiCall.sh "IndexControlURLs_p.html" "deletecomplete=&deleteIndex=on&deleteSolr=on&deleteCrawlQueues=on&deleteRobots=on&deleteSearchFl=on&deleteCache=off" +./apicall.sh "IndexControlURLs_p.html" "deletecomplete=&deleteIndex=on&deleteSolr=on&deleteCrawlQueues=on&deleteRobots=on&deleteSearchFl=on&deleteCache=off" > /dev/null diff --git a/bin/deleteurl.sh b/bin/deleteurl.sh index 94aefcfb3..e5cb00da3 100755 --- a/bin/deleteurl.sh +++ b/bin/deleteurl.sh @@ -1,3 +1,3 @@ #!/usr/bin/env sh cd "`dirname $0`" -./protectedPostApiCall.sh "IndexControlURLs_p.html" "urlhashdeleteall=&urlstring=$1" +./apicall.sh "IndexControlURLs_p.html" "urlhashdeleteall=&urlstring=$1" > /dev/null diff --git a/bin/importmediawiki.sh b/bin/importmediawiki.sh index df1b4c7ae..7f5e829f8 100755 --- a/bin/importmediawiki.sh +++ b/bin/importmediawiki.sh @@ -1,3 +1,3 @@ #!/usr/bin/env sh cd "`dirname $0`" -./protectedPostApiCall.sh "IndexImportMediawiki_p.html" "file=$1" +./apicall.sh "IndexImportMediawiki_p.html" "file=$1" > /dev/null diff --git a/bin/passwd.sh b/bin/passwd.sh index b858929a1..770429b18 100755 --- a/bin/passwd.sh +++ b/bin/passwd.sh @@ -38,7 +38,7 @@ if [ -f "$YACY_DATA_PATH/yacy.running" ]; then echo "YaCy server appears to be running. Calling the ConfigAccounts_p API..." # When the server is running we can not directly modify the yacy.conf file so we use the ConfigAccounts_p API. # Otherwise the new password provided here could be overwritten by the server when it saves its in-memory configuration to the yacy.conf file - (./protectedPostApiCall.sh "ConfigAccounts_p.html" "setAdmin=&adminuser=$YACY_ADMIN_USER_NAME&adminpw1=$YACY_ADMIN_PASSWORD&adminpw2=$YACY_ADMIN_PASSWORD&access=" && \ + (./apicall.sh "ConfigAccounts_p.html" "setAdmin=&adminuser=$YACY_ADMIN_USER_NAME&adminpw1=$YACY_ADMIN_PASSWORD&adminpw2=$YACY_ADMIN_PASSWORD&access=" && \ echo "Password successfully changed for User Name '$YACY_ADMIN_USER_NAME'.") || \ (echo "Password setting failed." && exit 1) else @@ -54,4 +54,4 @@ else mv "$YACY_CONF_FILE".tmp "$YACY_CONF_FILE" && \ echo "Password successfully changed for User Name '$YACY_ADMIN_USER_NAME'.") || \ (echo "Password setting failed." && exit 1) -fi \ No newline at end of file +fi diff --git a/source/net/yacy/http/Jetty9YaCySecurityHandler.java b/source/net/yacy/http/Jetty9YaCySecurityHandler.java index 204418c60..cb58ef7ff 100644 --- a/source/net/yacy/http/Jetty9YaCySecurityHandler.java +++ b/source/net/yacy/http/Jetty9YaCySecurityHandler.java @@ -45,7 +45,7 @@ import org.eclipse.jetty.server.Request; * and updates AccessTracker */ public class Jetty9YaCySecurityHandler extends ConstraintSecurityHandler { - + /** * create the constraint for the given path * for urls containing *_p. (like info_p.html) admin access is required, @@ -66,40 +66,40 @@ public class Jetty9YaCySecurityHandler extends ConstraintSecurityHandler { // update AccessTracker final String remoteip = RequestHeader.client(request); serverAccessTracker.track(remoteip, pathInContext); - + try { refererHost = new MultiProtocolURL(request.getHeader(RequestHeader.REFERER)).getHost(); } catch (MalformedURLException e) { refererHost = null; - } + } final boolean accessFromLocalhost = Domains.isLocalhost(remoteip) && (refererHost == null || refererHost.length() == 0 || Domains.isLocalhost(refererHost)); // ! note : accessFromLocalhost compares localhost ip pattern final boolean grantedForLocalhost = adminAccountGrantedForLocalhost && accessFromLocalhost; - - /* Even when all pages are protected, we don't want to block those used for peer-to-peer or cluster communication (except in private robinson mode) - * (examples : /yacy/hello.html is required for p2p and cluster network presence and /solr/select for remote Solr search requests) */ - boolean protectedPage = (adminAccountNeededForAllPages && ((sb.isRobinsonMode() && !sb.isPublicRobinson()) || - !(pathInContext.startsWith("/yacy/") || pathInContext.startsWith("/solr/")))); - - /* Pages suffixed with "_p" are by the way always considered protected */ - protectedPage = protectedPage || (pathInContext.indexOf("_p.") > 0); - + + /* Even when all pages are protected, we don't want to block those used for peer-to-peer or cluster communication (except in private robinson mode) + * (examples : /yacy/hello.html is required for p2p and cluster network presence and /solr/select for remote Solr search requests) */ + boolean protectedPage = (adminAccountNeededForAllPages && ((sb.isRobinsonMode() && !sb.isPublicRobinson()) || + !(pathInContext.startsWith("/yacy/") || pathInContext.startsWith("/solr/")))); + + /* Pages suffixed with "_p" are by the way always considered protected */ + protectedPage = protectedPage || (pathInContext.indexOf("_p.") > 0); + // check "/gsa" and "/solr" if not publicSearchpage if (!protectedPage && !sb.getConfigBool(SwitchboardConstants.PUBLIC_SEARCHPAGE, true)) { - protectedPage = pathInContext.startsWith("/solr/") || pathInContext.startsWith("/gsa/"); + protectedPage = pathInContext.startsWith("/solr/") || pathInContext.startsWith("/gsa/"); } if (protectedPage) { if (grantedForLocalhost) { - return null; // quick return for local admin + return null; } else if (accessFromLocalhost) { - // last chance to authentify using the admin from localhost + // last chance to authorize using the admin from localhost final String credentials = request.getHeader(RequestHeader.AUTHORIZATION); - if (credentials != null && credentials.length() < 60 && credentials.startsWith("Basic ")) { // Basic credentials are short "Basic " + b64(user:pwd) + if (credentials != null && credentials.length() < 120 && credentials.startsWith("Basic ")) { // Basic credentials are short "Basic " + b64(user:pwd) final String foruser = sb.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_USER_NAME, "admin"); final String adminAccountBase64MD5 = sb.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, ""); final String b64 = Base64Order.standardCoder.encodeString(foruser + ":" + adminAccountBase64MD5); // TODO: is this valid? ; consider "MD5:" prefixed config - if ((credentials.substring(6)).equals(b64)) return null; // lazy authentification for local access with credential from config (only a user with read access to DATA can do that) + if ((credentials.substring(6)).equals(b64)) return null; // lazy authentication for local access with credential from config (only a user with read access to DATA can do that) } } RoleInfo roleinfo = new RoleInfo(); diff --git a/stopYACY.sh b/stopYACY.sh index 557db43b7..4818819f3 100755 --- a/stopYACY.sh +++ b/stopYACY.sh @@ -20,7 +20,7 @@ if [ ! -f "$YACY_DATA_PATH/yacy.running" ]; then exit 1 fi -(bin/protectedPostApiCall.sh "Steering.html" "shutdown=true" && \ +(bin/apicall.sh "Steering.html" "shutdown=true" && \ echo "Please wait until the YaCy daemon process terminates [wget]" && \ echo "You can monitor this with 'tail -f $YACY_DATA_PATH/LOG/yacy00.log' and 'fuser $YACY_DATA_PATH/LOG/yacy00.log'") || \ exit $?