Changeset 36879


Ignore:
Timestamp:
2022-11-03T17:49:18+13:00 (4 weeks ago)
Author:
anupama
Message:

GS3 shouldn't allow system commands to go through unless it's either from the same PC (IP) as the GS3 server machine (so cmdline building and activate.pl work), or the user is administrator, or the system command is a collection-level command and the user has permissions for this or all collections. If not the case, the user will be asked to login.

Location:
main/trunk/greenstone3
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • main/trunk/greenstone3/build.properties.svn

    r36571 r36879  
    2222# but not remotely
    2323tomcat.server=localhost
     24
     25# If your machine's IP doesn't resolve to any of the usual local IP addresses,
     26# and instead has any specific IP(s) associated with it, set or add them here within
     27# the round brackets, with a vertical bar to connect them with the rest of the line
     28tomcat.server.IPregex=(127\\\\.\\\\d+\\\\.\\\\d+\\\\.\\\\d+|::1|0:0:0:0:0:0:0:1(%\\\\d)?)
    2429
    2530# server.protocols must contain 'http' or 'https' or both (in order of preference) separated by commas
  • main/trunk/greenstone3/build.xml

    r36760 r36879  
    18781878    <filter token="localhost.port.http" value="${localhost.port.http}"/>
    18791879    <filter token="tomcat.port.https" value="${tomcat.port.https}"/>
    1880     <filter token="restrict.http.to.local" value="${restrict.http.to.local}"/>
     1880    <filter token="restrict.http.to.local" value="${restrict.http.to.local}"/>
     1881    <filter token="tomcat.server.IPregex" value="${tomcat.server.IPregex}"/>
     1882
    18811883    <filter token="greenstone.context" value="${greenstone.context}"/>
    18821884    <filter token="solr.context" value="${solr.context}"/>
  • main/trunk/greenstone3/resources/web/global.properties.svn

    r32432 r36879  
    2929localhost.port.http=@localhost.port.http@
    3030restrict.http.to.local=@restrict.http.to.local@
     31tomcat.server.IPregex=@tomcat.server.IPregex@
    3132
    3233
  • main/trunk/greenstone3/src/java/org/greenstone/gsdl3/LibraryServlet.java

    r35507 r36879  
    562562    should_cache = false;
    563563
     564    // System commands now need to be logged in/have permissions
     565    // like configuring, activation and deactivation
     566    if(!configureSecurityCheck(request, userContext, out, baseURL, lang, output, queryMap)) {
     567        return;
     568    }
     569   
    564570    // we may want to remove all collection cache info, or just a specific collection
    565571    boolean clean_all = true;
     
    575581      }
    576582    else
    577       {
     583      {       
    578584        // check other system types
    579585        if (subaction.equals("a") || subaction.equals("d"))
     
    10741080    }
    10751081
     1082    /**
     1083     * request.getRemoteAddr() returns the IP of the client *or* of a proxy.
     1084     * We want the client IP, so we can check if it's truly local or not, since proxies can be
     1085     * local, thus masking a client connecting from an external IP.
     1086     * The following code is from
     1087     * https://www.javaprogramto.com/2020/11/how-to-get-client-ip-address-in-java.html
     1088     */
     1089    public String getClientIpAddress(HttpServletRequest request)
     1090    {
     1091    final String[] VALID_IP_HEADER_CANDIDATES = {
     1092        "X-Forwarded-For",
     1093        "Proxy-Client-IP",
     1094        "WL-Proxy-Client-IP",
     1095        "HTTP_X_FORWARDED_FOR",
     1096        "HTTP_X_FORWARDED",
     1097        "HTTP_X_CLUSTER_CLIENT_IP",
     1098        "HTTP_CLIENT_IP",
     1099        "HTTP_FORWARDED_FOR",
     1100        "HTTP_FORWARDED",
     1101        "HTTP_VIA",
     1102        "REMOTE_ADDR"
     1103    };
     1104   
     1105   
     1106    for (String header : VALID_IP_HEADER_CANDIDATES) {
     1107        String ipAddress = request.getHeader(header);
     1108        if (ipAddress != null && ipAddress.length() != 0 && !"unknown".equalsIgnoreCase(ipAddress)) {
     1109        return ipAddress;
     1110        }
     1111    }
     1112    return request.getRemoteAddr(); // fallback to whatever is the incoming IP (could be proxy)
     1113    }
     1114
     1115
     1116    private void debugCheckAgainstRegex() {
     1117   
     1118    // Load the global.properties file, get the tomcat.server.IPregex and check for any of it
     1119    // matching against the local IP
     1120    String tomcatServerIPregex = GlobalProperties.getProperty("tomcat.server.IPregex", "");
     1121   
     1122    logger.info("@@@ tomcatServerIPregex: " + tomcatServerIPregex);
     1123   
     1124    String[] testIPs = {"127.0.0.1", "::1", "0:0:0:0:0:0:0:1", "0:0:0:0:0:0:0:1%3"};
     1125   
     1126    //if(ipOfClient.equals(request.getLocalAddr()) || ipOfClient.equals("127.0.0.1")
     1127    //   || ipOfClient.equals("::1") || ipOfClient.startsWith("0:0:0:0:0:0:0:1")) {
     1128   
     1129    for(String testIP : testIPs) {
     1130        if(testIP.matches(tomcatServerIPregex)) {
     1131        logger.info("testIP " + testIP + " matches regex.");
     1132        } else {
     1133        logger.info("testIP " + testIP + " DOESN'T match regex.");
     1134        }
     1135    }   
     1136    }
     1137   
     1138   
     1139    /**
     1140     * Clients on the same machine as the GS3 server can reconfigure and activate/deactivate,
     1141     * Users in the administrator group also have the right to do such system commands.
     1142     * For collection-level system commands, users with permissions to edit all collections or
     1143     * the specific collection have a right to activate/deactivate a collection.
     1144     * Useful links:
     1145     * https://stackoverflow.com/questions/13563351/how-to-restrict-acces-to-specific-servlet-by-ip-via-container-configuration
     1146     * https://stackoverflow.com/questions/35015514/restricting-access-to-localhost-for-java-servlet-endpoint
     1147     * Reverse of: https://stackoverflow.com/questions/2539461/how-to-access-java-servlet-running-on-my-pc-from-outside
     1148    */
     1149    private boolean configureSecurityCheck(HttpServletRequest request, UserContext userContext,
     1150                       PrintWriter out, String baseURL, String lang,
     1151                       String output, Map<String, String[]> queryMap)
     1152    {
     1153    // if request emanates on same machine as GS server/from local machine, let it go through
     1154    String ipOfClient = getClientIpAddress(request); //request.getRemoteAddr();
     1155    logger.info("@@@ Request local IP: " + request.getLocalAddr());
     1156    logger.info("@@@ Client/remote IP: " + ipOfClient);
     1157   
     1158    //debugCheckAgainstRegex();
     1159
     1160    //if(ipOfClient.equals(request.getLocalAddr()) || ipOfClient.equals("127.0.0.1")
     1161    //   || ipOfClient.equals("::1") || ipOfClient.startsWith("0:0:0:0:0:0:0:1")) {
     1162    //    return true;
     1163    //}
     1164   
     1165    // Load the global.properties file, get the tomcat.server.IPregex and check for any of it
     1166    // matching against the local IP
     1167    String tomcatServerIPregex = GlobalProperties.getProperty("tomcat.server.IPregex", "");
     1168    if(ipOfClient.equals(request.getLocalAddr()) || ipOfClient.matches(tomcatServerIPregex)) {
     1169        return true;
     1170    }
     1171   
     1172    // TODO: Want regex for localhost situations and then make it a property in build.props
     1173    // In build.props: comment, you might like to leave the IP of your server machine
     1174    // at the end of the line   
     1175    // Check with another machine: browser, retest cmdline and activate.pl
     1176    // Final phase is testing with apache set up
     1177   
     1178    // if we have sc=colname, user needs to be colname-|all-collection-editor
     1179    // if we have st=collection&sn=colname, same as above
     1180    // otherwise user needs to be administrator
     1181
     1182    // We can have MODULE_TYPE=collection And MODULE_NAME=collname,
     1183    // OR SYSTEM_CLUSTER=collname
     1184    // If none of these specified, then assume the user should be administrator
     1185    // If not a collection level operation, the user should be administrator
     1186
     1187    String coll = null;
     1188    String module_type = getFirstParam(GSParams.SYSTEM_MODULE_TYPE, queryMap);
     1189    if(module_type != null && module_type.equals("collection")) {
     1190        // To deactivate demo:library?excerptid=gs_content&a=s&sa=d&sc=lucene-jdbm-demo
     1191        coll = getFirstParam(GSParams.SYSTEM_MODULE_NAME, queryMap);
     1192    } else {
     1193        // To reconfigure demo: library?excerptid=gs_content&a=s&sa=c&sc=lucene-jdbm-demo
     1194        coll = getFirstParam(GSParams.SYSTEM_CLUSTER, queryMap);
     1195        if (coll != null && coll.equals("")) {
     1196        coll = null;
     1197        }
     1198    }
     1199    String collname_editor = (coll == null) ? null : (coll+"-collections-editor");
     1200
     1201    for (String group : userContext.getGroups()) {
     1202
     1203        // if user is admin, all is good: they can do reconfigure (on service, collection or
     1204        // any module) or activate/deactivate a collection or any module
     1205        if (group.equals("administrator")) {       
     1206        return true;
     1207        }
     1208
     1209        // if collection level operation, user must have permissions for the collection
     1210        // so the user must be in at least one of all-|personal-|collname-collections-editor
     1211        if (coll != null) { // collname_editor won't be null
     1212        if(group.equals("all-collections-editor") || group.equals(collname_editor)) {
     1213            return true; // do we add || group.equals("personal-collections-editor")?
     1214        }
     1215        }
     1216       
     1217    }
     1218
     1219    errorAndLoginPage(request, userContext, out, baseURL, lang, output);
     1220    return false;   
     1221    }
    10761222
    10771223    private boolean runCollectionSecurityCheck(HttpServletRequest request, UserContext userContext, PrintWriter out, String baseURL, String collection, String document, String lang, String output) {
     
    11121258        if (!found)
    11131259        {
    1114 
    1115         String error_message = "";
    1116         if (request.getAuthType() == null)
    1117         {
    1118             error_message = getTextString("auth.error.login", lang);
    1119         }
    1120         else
    1121         {
    1122             error_message = getTextString("auth.error.incorrect_login", lang);
    1123         }
    1124        
    1125         HttpSession session = request.getSession();
    1126         ServletContext context = session.getServletContext();
    1127         String redirect_url_string = request.getRequestURI().replaceFirst(context.getContextPath() + "/", "");
    1128 
    1129         if (request.getQueryString() != null && request.getQueryString().length() > 0)
    1130         {
    1131             redirect_url_string += "?" + request.getQueryString().replace("&", "&amp;");
    1132         }
    1133 
    1134         generateLoginPage(redirect_url_string, error_message, userContext, out, baseURL, output);
     1260        errorAndLoginPage(request, userContext, out, baseURL, lang, output);
    11351261        return false;
    11361262        }
     
    11391265    }
    11401266
     1267
     1268    private void errorAndLoginPage(HttpServletRequest request, UserContext userContext,
     1269                   PrintWriter out, String baseURL, String lang, String output)
     1270    {   
     1271    String error_message = "";
     1272    if (request.getAuthType() == null)
     1273        {
     1274        error_message = getTextString("auth.error.login", lang);
     1275        }
     1276    else
     1277        {
     1278        error_message = getTextString("auth.error.incorrect_login", lang);
     1279        }
     1280   
     1281    HttpSession session = request.getSession();
     1282    ServletContext context = session.getServletContext();
     1283    String redirect_url_string = request.getRequestURI().replaceFirst(context.getContextPath() + "/", "");
     1284   
     1285    if (request.getQueryString() != null && request.getQueryString().length() > 0)
     1286        {
     1287        redirect_url_string += "?" + request.getQueryString().replace("&", "&amp;");
     1288        }
     1289   
     1290    generateLoginPage(redirect_url_string, error_message, userContext, out, baseURL, output);
     1291    }
     1292
     1293   
    11411294  private String getTextString(String key, String lang) {
    11421295      return Dictionary.getTextString(key, lang, null, null, null, null, null);
Note: See TracChangeset for help on using the changeset viewer.