implemented switch for admin authorization from localhost:

- access is granted for localhost users to administration pages by default
- the default setting can be changed in the BasicConfig.html page
- if the BasicConfig page was accessed with post and no password was submitted, a random password is generated
- a headless installation MUST give a password upon first call of the configuration page, otherwise they will not be able to access it again
- if no password is given within 10 minutes after start-up, a random password is generated

git-svn-id: https://svn.berlios.de/svnroot/repos/yacy/trunk@4804 6c8d7289-2bf4-0310-a012-ef5d649a1542
This commit is contained in:
orbiter 2008-05-15 11:26:43 +00:00
parent cfe6790498
commit 3bd1db776a
9 changed files with 93 additions and 49 deletions

View File

@ -353,11 +353,22 @@ serverAccountBase64MD5=
# settings through the web interface
# should be set to a secret. By default it is without a password
# but you are encouraged to set it to another value on the page
# http://localhost:8080/
# http://localhost:8080/ConfigBasic.html
#adminAccount=admin:mysecretpassword
adminAccount=
adminAccountBase64MD5=
# special access handling for users from localhost:
# access from localhost may be granted with administration authority
# if this flag is set. It is set to true by default to make usage of YaCy easy
# if you use YaCy on a headless server, you should set this to false
# or configure this on http://localhost:8080/ConfigBasic.html
# during the first 10 minutes of operation of YaCy;
# if the admin account password is still empty after 10 minutes a random
# password is generated an access is then ONLY from localhost, which will cause
# inaccessibility for installations on headless servers.
adminAccountForLocalhost=true
# if you are running a principal peer, you must update the following variables
# The upload method that should be used to upload the seed-list file to
# a public accessible webserver where it can be loaded by other peers.
@ -424,7 +435,7 @@ yacyDebugMode=false
#staticIP if you have a static IP, you can use this setting
staticIP=
# each time the proxy starts up, it can trigger the local browser to show the
# each time YaCy starts up, it can trigger the local browser to show the
# status page. This is active by default, to make it easier for first-time
# users to understand what this application does. You can disable browser
# pop-up here or set a different start page, like the search page

View File

@ -25,15 +25,28 @@
<form action="ConfigBasic.html" method="post" enctype="multipart/form-data">
<ol>
<li>
<img src="/env/grafics/ok.png" height="32" width="32" alt="ok" />&nbsp;Select a language for the interface:<br />
<img src="/env/grafics/ok.png" height="16" width="16" alt="ok" />&nbsp;Select a language for the interface:<br />
<fieldset>
<input type="radio" name="language" id="lang_de" value="de" #(langDeutsch)#::checked="checked"#(/langDeutsch)# /><label for="lang_de">Deutsch</label>&nbsp;
<input type="radio" name="language" value="default" id="lang_en" #(langEnglish)#::checked="checked"#(/langEnglish)# /><label for="lang_en">English</label>
</fieldset>
</li>
<li>
#(statusPassword)#<img src="/env/grafics/bad.png" height="32" width="32" alt="warning" />&nbsp;Please set a password for your peer to protect your settings (> 3 characters); if this is successful you will be asked to log in with these values immediately.::<img src="/env/grafics/ok.png" height="32" width="32" alt="ok" />&nbsp;Password is set#(/statusPassword)#<br />
<fieldset>
#(statusPassword)#<img src="/env/grafics/ok.png" height="16" width="16" alt="warning" />&nbsp;Access to localhost granted without password::<img src="/env/grafics/ok.png" height="16" width="16" alt="ok" />&nbsp;Password is set#(/statusPassword)#<br />
<fieldset>
<legend>
<input type="radio" name="access" id="access" value="localhost"#(localhost.checked)#:: checked="checked"#(/localhost.checked)# />
<label for="localhost">Access from localhost without account</label>
</legend>
Access to your peer from your own computer (localhost access) is granted. No need to configure an administration account.
</fieldset>
<fieldset>
<legend>
<input type="radio" name="access" id="access" value="account"#(account.checked)#:: checked="checked"#(/account.checked)# />
<label for="account">Access only with account</label>
</legend>
You need this only if you want a remote access to your peer.
<dl class="userConfig">
<dt><label for="adminuser">Peer User:</label></dt>
<dd><input type="text" name="adminuser" id="adminuser" value="#[defaultUser]#" size="16" maxlength="32" /></dd>
@ -42,10 +55,12 @@
<dt><label for="adminpw2">Repeat Peer Password:</label></dt>
<dd><input type="password" name="adminpw2" id="adminpw2" value="" size="16" maxlength="32" /></dd>
</dl>
</fieldset>
</fieldset>
</li>
<li>
#(statusName)#<img src="/env/grafics/bad.png" height="32" width="32" alt="warning" />&nbsp;Your peer name has not been customized; please set your own peer name::<img src="/env/grafics/ok.png" height="32" width="32" alt="ok" />&nbsp;You have a nice peer name#(/statusName)#<br />
#(statusName)#<img src="/env/grafics/bad.png" height="16" width="16" alt="warning" />&nbsp;Your peer name has not been customized; please set your own peer name::<img src="/env/grafics/ok.png" height="16" width="16" alt="ok" />&nbsp;You have a nice peer name#(/statusName)#<br />
<fieldset>
<dl>
<dt><label for="peername">Peer Name: </label></dt>
@ -56,7 +71,7 @@
</fieldset>
</li>
<li>
#(statusPort)#<img src="/env/grafics/bad.png" height="32" width="32" alt="warning" />&nbsp;Your peer cannot be reached from outside (which is not fatal, but would be good for the YaCy network); please open your firewall for this port and/or set a virtual server option in your router to allow connections on this port.::<img src="/env/grafics/ok.png" height="32" width="32" alt="ok" />&nbsp;Your peer can be reached by other peers#(/statusPort)#<br />
#(statusPort)#<img src="/env/grafics/bad.png" height="16" width="16" alt="warning" />&nbsp;Your peer cannot be reached from outside (which is not fatal, but would be good for the YaCy network); please open your firewall for this port and/or set a virtual server option in your router to allow connections on this port.::<img src="/env/grafics/ok.png" height="16" width="16" alt="ok" />&nbsp;Your peer can be reached by other peers#(/statusPort)#<br />
<fieldset>
<dl>
<dt><label for="port">Peer Port: </label></dt>

View File

@ -65,12 +65,12 @@ import de.anomic.server.serverSwitch;
import de.anomic.yacy.yacySeed;
public class ConfigBasic {
private static final int NEXTSTEP_FINISHED = 0;
private static final int NEXTSTEP_PWD = 1;
private static final int NEXTSTEP_PEERNAME = 2;
private static final int NEXTSTEP_PEERPORT = 3;
private static final int NEXTSTEP_RECONNECT = 4;
private static final int NEXTSTEP_FINISHED = 0;
//private static final int NEXTSTEP_PWD = 1;
private static final int NEXTSTEP_PEERNAME = 2;
private static final int NEXTSTEP_PEERPORT = 3;
private static final int NEXTSTEP_RECONNECT = 4;
public static serverObjects respond(httpHeader header, serverObjects post, serverSwitch<?> env) {
@ -101,6 +101,7 @@ public class ConfigBasic {
}
// password settings
boolean localhostAccess = (post == null) ? sb.getConfigBool("adminAccountForLocalhost", false) : post.get("access", "").equals("localhost");
String user = (post == null) ? "" : (String) post.get("adminuser", "");
String pw1 = (post == null) ? "" : (String) post.get("adminpw1", "");
String pw2 = (post == null) ? "" : (String) post.get("adminpw2", "");
@ -110,18 +111,26 @@ public class ConfigBasic {
// port settings
String port = env.getConfig("port", "8080"); //this allows a low port, but it will only get one, if the user edits the config himself.
if(post!=null && Integer.parseInt((String)post.get("port"))>1023){
if (post != null && Integer.parseInt((String) post.get("port")) > 1023) {
port = post.get("port", "8080");
}
// admin password
if ((user.length() > 0) && (pw1.length() > 3) && (pw1.equals(pw2))) {
sb.setConfig("adminAccountForLocalhost", localhostAccess);
prop.put("localhost.checked", (localhostAccess) ? 1 : 0);
prop.put("account.checked", (localhostAccess) ? 0 : 1);
// if an localhost access is configured, check if a local password is given
// if not, set a random password
if (post != null && localhostAccess && env.getConfig(httpd.ADMIN_ACCOUNT_B64MD5, "").length() == 0) {
// make a 'random' password
env.setConfig(httpd.ADMIN_ACCOUNT_B64MD5, "0000" + serverCodings.encodeMD5Hex(System.getProperties().toString() + System.currentTimeMillis()));
env.setConfig("adminAccount", "");
}
// may be overwritten if new password is given
if ((user.length() > 0) && (pw1.length() > 3) && (pw1.equals(pw2))) {
// check passed. set account:
env.setConfig(httpd.ADMIN_ACCOUNT_B64MD5, serverCodings.encodeMD5Hex(kelondroBase64Order.standardCoder.encodeString(user + ":" + pw1)));
env.setConfig("adminAccount", "");
// authenticate immediately
//prop.put("AUTHENTICATE", "admin log-in");
//return prop;
}
// check if peer name already exists
@ -166,22 +175,19 @@ public class ConfigBasic {
}
// check if values are proper
boolean properPW = (env.getConfig("adminAccount", "").length() == 0) && (env.getConfig(httpd.ADMIN_ACCOUNT_B64MD5, "").length() > 0);
boolean properName = (env.getConfig("peerName","").length() >= 3) && (!(yacySeed.isDefaultPeerName(env.getConfig("peerName",""))));
boolean properPort = (sb.webIndex.seedDB.mySeed().isSenior()) || (sb.webIndex.seedDB.mySeed().isPrincipal());
if ((properPW) && (env.getConfig("defaultFiles", "").startsWith("ConfigBasic.html,"))) {
if ((env.getConfig("defaultFiles", "").startsWith("ConfigBasic.html,"))) {
env.setConfig("defaultFiles", env.getConfig("defaultFiles", "").substring(17));
httpdFileHandler.initDefaultPath();
}
prop.put("statusName", properName ? "1" : "0");
prop.put("statusPassword", properPW ? "1" : "0");
prop.put("statusPassword", localhostAccess ? "0" : "1");
prop.put("statusPort", properPort ? "1" : "0");
if (reconnect) {
prop.put("nextStep", NEXTSTEP_RECONNECT);
} else if (!properPW) {
prop.put("nextStep", NEXTSTEP_PWD);
} else if (!properName) {
prop.put("nextStep", NEXTSTEP_PEERNAME);
} else if (!properPort) {

View File

@ -29,6 +29,7 @@
if(document.ConfigForm.network[0].checked) {
document.ConfigForm.indexDistribute.checked = true;
document.ConfigForm.indexReceive.checked = true;
document.ConfigForm.crawlResponse.checked = true;
}
}
//-->
@ -45,11 +46,11 @@
#(/commit)#
#(commitCrawlPlea)#::<div class="error">P2P operation can run without remote indexing, but runs better with remote indexing switched on. Please switch 'Accept Remote Crawl Requests' on.</div>#(/commitCrawlPlea)#
#(commitDHTIsRobinson)#::<div class="error">For P2P operation, at least DHT distribution or DHT receive (or both) must be set. You have thus defined a Robinson configuration.</div>#(/commitDHTIsRobinson)#
#(commitDHTNoGlobalSearch)#::<div class="error">Global Search in P2P configuration is only allowed, if both, index receive and distribution is switched on. You have a P2P configuration, but are not allowed to search other peers.</div>#(/commitDHTNoGlobalSearch)#
#(commitDHTNoGlobalSearch)#::<div class="error">Global Search in P2P configuration is only allowed, if index receive is switched on. You have a P2P configuration, but are not allowed to search other peers.</div>#(/commitDHTNoGlobalSearch)#
#(commitRobinson)#::<div class="commit">For Robinson Mode, index distribution and receive is switched off.</div>#(/commitRobinson)#
#(commitRobinsonWithRemoteIndexing)#::<div class="commit">This Robinson Mode switches remote indexing on, but limits targets to peers within the same cluster. Remote indexing requests from peers within the same cluster are accepted.</div>#(/commitRobinsonWithRemoteIndexing)#
#(commitRobinsonWithoutRemoteIndexing)#::<div class="commit">This Robinson Mode does not allow any remote indexing (neither requests remote indexing, nor accepts it).</div>#(/commitRobinsonWithoutRemoteIndexing)#
<form name="ConfigForm" method="post" action="ConfigNetwork_p.html" enctype="multipart/form-data" accept-charset="UTF-8">
<form name="NetworkForm" method="post" action="ConfigNetwork_p.html" enctype="multipart/form-data" accept-charset="UTF-8">
<fieldset>
<legend>
<label for="domain">Network and Domain Specification</label>

View File

@ -109,7 +109,7 @@ public class ConfigNetwork_p {
} else if (indexDistribute && indexReceive) {
commit = 1;
} else {
prop.put("commitDHTNoGlobalSearch", "1");
if (!indexReceive) prop.put("commitDHTNoGlobalSearch", "1");
commit = 1;
}
if (!crawlResponse) {

View File

@ -40,7 +40,7 @@
<h3>Peer Control</h3>
<ul class="menu">
<li><a href="/Status.html" class="MenuItemLink">Admin Console</a></li>
<li><a href="/terminal_p.html" accesskey="t" class="MenuItemLink">Terminal</a></li>
<li><a href="/terminal_p.html" accesskey="t" class="MenuItemLink lock">Terminal</a></li>
<li><a href="/Messages_p.html" class="MenuItemLink lock">Messages <img src="/notifier.gif" alt="New Messages" /></a></li>
<li><a href="/Steering.html?restart=" class="MenuItemLink lock" onclick="return confirm('Confirm Restart')">Re-Start</a></li>
<li><a href="/Steering.html?shutdown=" class="MenuItemLink lock" onclick="return confirm('Confirm Shutdown')">Shutdown</a></li>

View File

@ -296,9 +296,9 @@ public final class httpd implements serverHandler {
}
public static int staticAdminAuthenticated(String authorization, serverSwitch<?> sw) {
if (authorization==null) return 1;
// the authorization string must be given with the truncated 6 bytes at the beginning
if (authorization == null) return 1;
//if (authorization.length() < 6) return 1; // no authentication information given
//authorization = authorization.trim().substring(6);
String adminAccountBase64MD5 = sw.getConfig(ADMIN_ACCOUNT_B64MD5, "");
if (adminAccountBase64MD5.length() == 0) return 2; // no passwrd stored
if (adminAccountBase64MD5.equals(serverCodings.encodeMD5Hex(authorization))) return 4; // hard-authenticated, all ok

View File

@ -303,21 +303,15 @@ public final class httpdFileHandler {
int pos = path.lastIndexOf(".");
if ((!clientIP.equals("localhost")) &&
(!clientIP.startsWith("0:0:0:0:0:0:0:1")) &&
(path.substring(0,(pos==-1)?path.length():pos)).endsWith("_p") &&
(adminAccountBase64MD5.length() != 0)) {
//authentication required
//userDB
if(sb.userDB.hasAdminRight(authorization, conProp.getProperty(httpHeader.CONNECTION_PROP_CLIENTIP), requestHeader.getHeaderCookies())){
//Authentication successful. remove brute-force flag
serverCore.bfHost.remove(conProp.getProperty(httpHeader.CONNECTION_PROP_CLIENTIP));
//static
}else if(authorization != null && httpd.staticAdminAuthenticated(authorization.trim().substring(6), switchboard)==4){
//Authentication successful. remove brute-force flag
serverCore.bfHost.remove(conProp.getProperty(httpHeader.CONNECTION_PROP_CLIENTIP));
//no auth
}else if (authorization == null) {
boolean adminAccountForLocalhost = sb.getConfigBool("adminAccountForLocalhost", false);
boolean accessFromLocalhost = clientIP.equals("localhost") || clientIP.startsWith("0:0:0:0:0:0:0:1");
boolean grantedForLocalhost = adminAccountForLocalhost && accessFromLocalhost;
boolean protectedPage = (path.substring(0,(pos==-1)?path.length():pos)).endsWith("_p");
boolean accountEmpty = adminAccountBase64MD5.length() == 0;
if (!grantedForLocalhost && protectedPage && !accountEmpty) {
// authentication required
if (authorization == null) {
// no authorization given in response. Ask for that
httpHeader headers = getDefaultHeaders(path);
headers.put(httpHeader.WWW_AUTHENTICATE,"Basic realm=\"admin log-in\"");
@ -327,6 +321,11 @@ public final class httpdFileHandler {
//TODO: separate errorpage Wrong Login / No Login
httpd.sendRespondError(conProp, out, 5, 401, "Wrong Authentication", "", new File("proxymsg/authfail.inc"), tp, null, headers);
return;
} else if (
(httpd.staticAdminAuthenticated(authorization.trim().substring(6), switchboard) == 4) ||
(sb.userDB.hasAdminRight(authorization, conProp.getProperty(httpHeader.CONNECTION_PROP_CLIENTIP), requestHeader.getHeaderCookies()))) {
//Authentication successful. remove brute-force flag
serverCore.bfHost.remove(conProp.getProperty(httpHeader.CONNECTION_PROP_CLIENTIP));
} else {
// a wrong authentication was given or the userDB user does not have admin access. Ask again
serverLog.logInfo("HTTPD", "Wrong log-in for account 'admin' in http file handler for path '" + path + "' from host '" + clientIP + "'");
@ -343,7 +342,6 @@ public final class httpdFileHandler {
}
}
// parse arguments
serverObjects args = new serverObjects();
int argc;

View File

@ -148,6 +148,7 @@ import de.anomic.kelondro.kelondroNaturalOrder;
import de.anomic.plasma.parser.ParserException;
import de.anomic.server.serverAbstractSwitch;
import de.anomic.server.serverBusyThread;
import de.anomic.server.serverCodings;
import de.anomic.server.serverDomains;
import de.anomic.server.serverFileUtils;
import de.anomic.server.serverInstantBusyThread;
@ -1139,7 +1140,7 @@ public final class plasmaSwitchboard extends serverAbstractSwitch<IndexingStack.
moreMemory.schedule(new MoreMemory(), 300000, 600000);
deployThread(CLEANUP, "Cleanup", "simple cleaning process for monitoring information", null,
new serverInstantBusyThread(this, CLEANUP_METHOD_START, CLEANUP_METHOD_JOBCOUNT, CLEANUP_METHOD_FREEMEM), 10000); // all 5 Minutes
new serverInstantBusyThread(this, CLEANUP_METHOD_START, CLEANUP_METHOD_JOBCOUNT, CLEANUP_METHOD_FREEMEM), 600000); // all 5 Minutes, wait 10 minutes until first run
deployThread(CRAWLSTACK, "Crawl URL Stacker", "process that checks url for double-occurrences and for allowance/disallowance by robots.txt", null,
new serverInstantBusyThread(crawlStacker, CRAWLSTACK_METHOD_START, CRAWLSTACK_METHOD_JOBCOUNT, CRAWLSTACK_METHOD_FREEMEM), 8000);
deployThread(INDEXER, "Indexing", "thread that either initiates a parsing/indexing queue, distributes the index into the DHT, stores parsed documents or flushes the index cache", "/IndexCreateIndexingQueue_p.html",
@ -1774,6 +1775,13 @@ public final class plasmaSwitchboard extends serverAbstractSwitch<IndexingStack.
try {
boolean hasDoneSomething = false;
// set a random password if no password is configured
if (getConfigBool("adminAccountForLocalhost", false) && getConfig(httpd.ADMIN_ACCOUNT_B64MD5, "").length() == 0) {
// make a 'random' password
setConfig(httpd.ADMIN_ACCOUNT_B64MD5, "0000" + serverCodings.encodeMD5Hex(System.getProperties().toString() + System.currentTimeMillis()));
setConfig("adminAccount", "");
}
// close unused connections
JakartaCommonsHttpClient.cleanup();
@ -2254,15 +2262,20 @@ public final class plasmaSwitchboard extends serverAbstractSwitch<IndexingStack.
public int adminAuthenticated(httpHeader header) {
//String adminAccountBase64MD5 = getConfig(httpd.ADMIN_ACCOUNT_B64MD5, "");
// authorization for localhost, only if flag is set to grant localhost access as admin
String clientIP = (String) header.get(httpHeader.CONNECTION_PROP_CLIENTIP, "");
boolean accessFromLocalhost = clientIP.equals("localhost") || clientIP.startsWith("0:0:0:0:0:0:0:1");
if (getConfigBool("adminAccountForLocalhost", false) && accessFromLocalhost) return 3; // soft-authenticated for localhost
// get the authorization string from the header
String authorization = ((String) header.get(httpHeader.AUTHORIZATION, "xxxxxx")).trim().substring(6);
// security check against too long authorization strings
if (authorization.length() > 256) return 0;
// authorization by encoded password, only for localhost access
String clientIP = (String) header.get(httpHeader.CONNECTION_PROP_CLIENTIP, "");
if ((clientIP.equals("localhost") || clientIP.startsWith("0:0:0:0:0:0:0:1")) /*&& (adminAccountBase64MD5.equals(authorization))*/) return 3; // soft-authenticated for localhost
String adminAccountBase64MD5 = getConfig(httpd.ADMIN_ACCOUNT_B64MD5, "");
if (accessFromLocalhost && (adminAccountBase64MD5.equals(authorization))) return 3; // soft-authenticated for localhost
// authorization by hit in userDB
if (userDB.hasAdminRight((String) header.get(httpHeader.AUTHORIZATION, "xxxxxx"), ((String) header.get(httpHeader.CONNECTION_PROP_CLIENTIP, "")), header.getHeaderCookies())) return 4; //return, because 4=max