Make it possible to set an empty password disabling the authentication

protocol completely
If you set now an empty password, then the http server will not ask to
authentify. This is required for environment where we attach an outside
authentification service like keycloak or similar using authentication
in an ingress proxy.
This change is part of the approach to run YaCy inside of a kubernetes
cluster where we do not want individual authentication of peers and want
to apply a ingress authentication.
pull/402/head
Michael Peter Christen 4 years ago
parent 198826c362
commit 0ae8ccf657

@ -74,11 +74,11 @@ public class ConfigAccounts_p {
final String pw2 = post.get("adminpw2", "");
int inputerror=0;
// may be overwritten if new password is given
if (user.length() > 0 && pw1.length() > 2 && pw1.equals(pw2)) {
if (user.length() > 0 && pw1.equals(pw2)) {
String oldusername = env.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_USER_NAME,user);
// check passed. set account:
// old: // env.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, Digest.encodeMD5Hex(Base64Order.standardCoder.encodeString(user + ":" + pw1)));
env.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "MD5:"+Digest.encodeMD5Hex(user + ":" + sb.getConfig(SwitchboardConstants.ADMIN_REALM,"YaCy")+":"+ pw1));
env.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, sb.encodeDigestAuth(user, pw1));
env.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_USER_NAME,user);
// make sure server accepts new credentials
Jetty9HttpServerImpl jhttpserver = (Jetty9HttpServerImpl)sb.getHttpServer();
@ -210,7 +210,7 @@ public class ConfigAccounts_p {
if (!"".equals(pw1)) { //change only if set
// MD5 according to HTTP Digest RFC 2617 (3.2.2) name:realm:pwd (use seed.hash as realm)
// with prefix of encoding method (supported MD5: )
mem.put(UserDB.Entry.MD5ENCODED_USERPWD_STRING, "MD5:"+Digest.encodeMD5Hex(username + ":" + sb.getConfig(SwitchboardConstants.ADMIN_REALM,"YaCy")+":"+pw1));
mem.put(UserDB.Entry.MD5ENCODED_USERPWD_STRING, sb.encodeDigestAuth(username, pw1));
}
mem.put(UserDB.Entry.USER_FIRSTNAME, firstName);
@ -238,11 +238,8 @@ public class ConfigAccounts_p {
if (entry != null) {
try{
if (!"".equals(pw1)) {
// with prefix of encoding method (supported MD5: )
entry.setProperty(UserDB.Entry.MD5ENCODED_USERPWD_STRING, "MD5:"+Digest.encodeMD5Hex(username+ ":" + sb.getConfig(SwitchboardConstants.ADMIN_REALM,"YaCy") + ":"+pw1));
}
// with prefix of encoding method (supported MD5: )
entry.setProperty(UserDB.Entry.MD5ENCODED_USERPWD_STRING, sb.encodeDigestAuth(username, pw1));
entry.setProperty(UserDB.Entry.USER_FIRSTNAME, firstName);
entry.setProperty(UserDB.Entry.USER_LASTNAME, lastName);
entry.setProperty(UserDB.Entry.USER_ADDRESS, address);

@ -121,11 +121,8 @@ public class ConfigUser_p {
if (entry != null) {
try {
if (!"".equals(pw1)) {
// with prefix of encoding method (supported MD5: )
entry.setProperty(UserDB.Entry.MD5ENCODED_USERPWD_STRING, "MD5:" + Digest.encodeMD5Hex(username + ":" + sb.getConfig(SwitchboardConstants.ADMIN_REALM, "YaCy") + ":" + pw1));
}
// with prefix of encoding method (supported MD5: )
entry.setProperty(UserDB.Entry.MD5ENCODED_USERPWD_STRING, sb.encodeDigestAuth(username, pw1));
entry.setProperty(UserDB.Entry.USER_FIRSTNAME, firstName);
entry.setProperty(UserDB.Entry.USER_LASTNAME, lastName);
entry.setProperty(UserDB.Entry.USER_ADDRESS, address);

@ -91,7 +91,7 @@ public class SettingsAck_p {
return prop;
}
// check passed. set account:
env.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "MD5:"+Digest.encodeMD5Hex(user + ":" + sb.getConfig(SwitchboardConstants.ADMIN_REALM,"YaCy") + ":" + pw1));
env.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, sb.encodeDigestAuth(user, pw1));
env.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_USER_NAME, user);
prop.put("info", "5");//admin account changed
prop.putHTML("info_user", user);

@ -134,12 +134,12 @@ public class User{
prop.put("status", "1"); //password
if (entry.getMD5EncodedUserPwd().startsWith("MD5:") ?
entry.getMD5EncodedUserPwd().equals("MD5:"+Digest.encodeMD5Hex(entry.getUserName() + ":" + sb.getConfig(SwitchboardConstants.ADMIN_REALM,"YaCy") + ":" + post.get("oldpass", ""))) :
entry.getMD5EncodedUserPwd().equals(Digest.encodeMD5Hex(entry.getUserName() + ":" + post.get("oldpass", "")))) {
entry.getMD5EncodedUserPwd().equals(sb.encodeDigestAuth(entry.getUserName(), post.get("oldpass", ""))) :
entry.getMD5EncodedUserPwd().equals(sb.encodeBasicAuth(entry.getUserName() , post.get("oldpass", "")))) {
if (post.get("newpass").equals(post.get("newpass2"))) {
if (!post.get("newpass", "").equals("")) {
try {
entry.setProperty(UserDB.Entry.MD5ENCODED_USERPWD_STRING, "MD5:" + Digest.encodeMD5Hex(entry.getUserName() + ":" + sb.getConfig(SwitchboardConstants.ADMIN_REALM,"YaCy") + ":" + post.get("newpass", "")));
entry.setProperty(UserDB.Entry.MD5ENCODED_USERPWD_STRING, sb.encodeDigestAuth(entry.getUserName(), post.get("newpass", "")));
prop.put("status_password", "0"); //changes
} catch (final Exception e) {
ConcurrentLog.logException(e);

@ -63,28 +63,36 @@ public class TransactionManager {
*/
private static String getUserName(final RequestHeader header) {
String userName = header.getRemoteUser();
if (userName == null && header.accessFromLocalhost() && Switchboard.getSwitchboard() != null) {
final String adminAccountUserName = Switchboard.getSwitchboard().getConfig(SwitchboardConstants.ADMIN_ACCOUNT_USER_NAME, "admin");
final String adminAccountBase64MD5 = Switchboard.getSwitchboard().getConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "");
if(Switchboard.getSwitchboard()
.getConfigBool(SwitchboardConstants.ADMIN_ACCOUNT_FOR_LOCALHOST, false)) {
/* Unauthenticated local access as administrator can be enabled */
userName = adminAccountUserName;
} else {
/* authorization by encoded password, only for localhost access (used by bash scripts)*/
String pass = Base64Order.standardCoder.encodeString(adminAccountUserName + ":" + adminAccountBase64MD5);
/* get the authorization string from the header */
final String realmProp = (header.get(RequestHeader.AUTHORIZATION, "")).trim();
final String realmValue = realmProp.isEmpty() ? null : realmProp.substring(6); // take out "BASIC "
if (pass.equals(realmValue)) { // assume realmValue as is in cfg
userName = adminAccountUserName;
}
}
}
Switchboard sb = Switchboard.getSwitchboard();
if (sb != null) {
final String adminAccountBase64MD5 = sb.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "");
final String adminAccountUserName = sb.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_USER_NAME, "admin");
if (adminAccountBase64MD5.equals(sb.emptyPasswordAdminAccount)) {
// admin users with empty passwords do not need to authentify, thus do not have
// this header present. We just consoder the name is "admin"
userName = adminAccountUserName;
}
if (userName == null && header.accessFromLocalhost()) {
if (sb.getConfigBool(SwitchboardConstants.ADMIN_ACCOUNT_FOR_LOCALHOST, false)) {
/* Unauthenticated local access as administrator can be enabled */
userName = adminAccountUserName;
} else {
/* authorization by encoded password, only for localhost access (used by bash scripts)*/
String pass = Base64Order.standardCoder.encodeString(adminAccountUserName + ":" + adminAccountBase64MD5);
/* get the authorization string from the header */
final String realmProp = (header.get(RequestHeader.AUTHORIZATION, "")).trim();
final String realmValue = realmProp.isEmpty() ? null : realmProp.substring(6); // take out "BASIC "
if (pass.equals(realmValue)) { // assume realmValue as is in cfg
userName = adminAccountUserName;
}
}
}
}
return userName;
}

@ -76,14 +76,18 @@ public class Jetty9YaCySecurityHandler extends ConstraintSecurityHandler {
// ! 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) */
// 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 */
// Pages suffixed with "_p" are by the way always considered protected
protectedPage = protectedPage || (pathInContext.indexOf("_p.") > 0);
// ..except that the password for the admin account is empty
final String adminAccountBase64MD5 = sb.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "");
protectedPage = protectedPage && !adminAccountBase64MD5.equals(sb.emptyPasswordAdminAccount);
// check "/gsa" and "/solr" if not publicSearchpage
if (!protectedPage && !sb.getConfigBool(SwitchboardConstants.PUBLIC_SEARCHPAGE, true)) {
protectedPage = pathInContext.startsWith("/solr/") || pathInContext.startsWith("/gsa/");
@ -97,7 +101,6 @@ public class Jetty9YaCySecurityHandler extends ConstraintSecurityHandler {
final String credentials = request.getHeader(RequestHeader.AUTHORIZATION);
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 authentication for local access with credential from config (only a user with read access to DATA can do that)
}

@ -324,6 +324,7 @@ public final class Switchboard extends serverSwitch {
private boolean startupAction = true; // this is set to false after the first event
private static Switchboard sb;
public HashMap<String, Object[]> crawlJobsStatus = new HashMap<String, Object[]>();
public String emptyPasswordAdminAccount;
public Switchboard(final File dataPath, final File appPath, final String initPath, final String configPath) {
super(dataPath, appPath, initPath, configPath);
@ -456,6 +457,9 @@ public final class Switchboard extends serverSwitch {
}
}.start();
// define the "non-password password"
emptyPasswordAdminAccount = encodeDigestAuth(getConfig(SwitchboardConstants.ADMIN_ACCOUNT_USER_NAME,"admin"), "");
// init the language detector
this.log.config("Loading language profiles");
try {
@ -3980,6 +3984,8 @@ public final class Switchboard extends serverSwitch {
* - access from localhost is granted and access comes from localhost: auth-level 3
* - a password is configured and access comes from localhost and the realm-value
* of a http-authentify String is equal to the stored base64MD5: auth-level 3
* - an empty password is configured an access comes from anywhere: auth-level 3
* This may be used in cluster installations where the cluster has an outside protection but inside is none needed.
* - a password is configured and access comes with matching http-authentify: auth-level 4
*
* @param requestHeader
@ -4006,6 +4012,11 @@ public final class Switchboard extends serverSwitch {
return 2; // no password stored; this should not happen for older peers
}
// authorization in case that administrators have stored an empty password; this authorizes all users as admin regardless of the give auth
if (adminAccountBase64MD5.equals(emptyPasswordAdminAccount)) {
return 3; // everyone is admin from everywhere
}
// authorization for localhost, only if flag is set to grant localhost access as admin
final boolean accessFromLocalhost = requestHeader.accessFromLocalhost();
if (accessFromLocalhost && getConfigBool(SwitchboardConstants.ADMIN_ACCOUNT_FOR_LOCALHOST, false)) {
@ -4083,6 +4094,14 @@ public final class Switchboard extends serverSwitch {
return 1;
}
public String encodeDigestAuth(String user, String pw) {
return "MD5:" + Digest.encodeMD5Hex(user + ":" + sb.getConfig(SwitchboardConstants.ADMIN_REALM,"YaCy") + ":" + pw);
}
public String encodeBasicAuth(String user, String pw) {
return Digest.encodeMD5Hex(user + ":" + pw);
}
/**
* @param header servlet request headers
* @return true when the headers contains valid admin authentication information

@ -791,7 +791,7 @@ public final class yacy {
} else {
username = ss.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_USER_NAME, "admin");
}
ss.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "MD5:" + Digest.encodeMD5Hex(username + ":" + ss.getConfig(SwitchboardConstants.ADMIN_REALM, "YaCy") + ":" + pwdtxt));
ss.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, sb.encodeDigestAuth(username, pwdtxt));
System.out.println("Set property " + SwitchboardConstants.ADMIN_ACCOUNT_B64MD5 + " = " + ss.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, ""));
}
} else {

Loading…
Cancel
Save