{"record":{"id":"935625b43af45426","repo":"apache/hadoop","slug":"only-namenode-secondary-namenode-and-administrat","errorCode":null,"errorMessage":"Only Namenode, Secondary Namenode, and administrators may access this servlet","messagePattern":"Only Namenode, Secondary Namenode, and administrators may access this servlet","errorType":"http","errorClass":"IOException","httpStatus":403,"severity":"error","filePath":"hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/ImageServlet.java","lineNumber":243,"sourceCode":"      response.getOutputStream().close();\n    }\n  }\n\n  private void validateRequest(ServletContext context, Configuration conf,\n      HttpServletRequest request, HttpServletResponse response,\n      FSImage nnImage, String theirStorageInfoString) throws IOException {\n\n    if (UserGroupInformation.isSecurityEnabled()\n        && !isValidRequestor(context, request.getUserPrincipal().getName(),\n            conf)) {\n      String errorMsg = \"Only Namenode, Secondary Namenode, and administrators may access \"\n          + \"this servlet\";\n      sendError(response, HttpServletResponse.SC_FORBIDDEN, errorMsg);\n      LOG.warn(\"Received non-NN/SNN/administrator request for image or edits from \"\n          + request.getUserPrincipal().getName()\n          + \" at \"\n          + request.getRemoteHost());\n      throw new IOException(errorMsg);\n    }\n\n    String myStorageInfoString = nnImage.getStorage().toColonSeparatedString();\n    if (theirStorageInfoString != null\n        && !myStorageInfoString.equals(theirStorageInfoString)) {\n      String errorMsg = \"This namenode has storage info \" + myStorageInfoString\n          + \" but the secondary expected \" + theirStorageInfoString;\n      sendError(response, HttpServletResponse.SC_FORBIDDEN, errorMsg);\n      LOG.warn(\"Received an invalid request file transfer request \"\n          + \"from a secondary with storage info \" + theirStorageInfoString);\n      throw new IOException(errorMsg);\n    }\n  }\n\n  public static void setFileNameHeaders(HttpServletResponse response,\n      File file) {\n    response.setHeader(CONTENT_DISPOSITION, \"attachment; filename=\" +\n        file.getName());","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/ImageServlet.java#L225-L261","documentation":"ImageServlet.validateRequest throws this when Kerberos security is enabled and the authenticated principal fetching an image or edit log is not recognized by isValidRequestor, which admits the NameNode, Secondary/Standby NameNode principals and configured administrators (dfs.cluster.administrators). It is the authorization guard for the checkpoint-transfer endpoint and answers 403 Forbidden before any namespace data is sent. The accompanying LOG.warn names the rejected principal and remote host.","triggerScenarios":"UserGroupInformation.isSecurityEnabled() is true and request.getUserPrincipal() is a principal outside the allowed set: a 2NN/Standby running with a different or missing Kerberos keytab/principal, a cross-realm principal whose string does not match the configured pattern, or an operator fetching with a non-admin kinit.","commonSituations":"Secondary NameNode principal not covered by dfs.namenode.secondary kerberos principal patterns; Standby configured with its own principal while the servlet only accepts the NN pattern; dfs.cluster.administrators ACL stale after team changes; missing cross-realm trust so the principal string differs from what was configured.","solutions":["Take the rejected principal from the NN log line 'Received non-NN/SNN/administrator request ... at HOST' and compare it with the principal your 2NN/Standby actually authenticates with (klist -kt).","Make the Secondary/Standby run with the expected Kerberos principal and keytab (dfs.namenode.keytab.* and dfs.namenode.secondary.* / http_principal settings).","Add legitimate human operators or groups to dfs.cluster.administrators if they need to fetch images.","If realm mapping differs, fix krb5.conf domain_realm or use fully-qualified principal names in the ACL."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before starting the Secondary, prove the identity it will present to the servlet\nUserGroupInformation.loginUserFromKeytab(principal, keytabPath);\nString me = UserGroupInformation.getCurrentUser().getUserName();\nString admins = conf.get(\"dfs.cluster.administrators\", \"*\");\nboolean adminOk = admins.equals(\"*\") || admins.contains(me);\nif (!adminOk && !me.startsWith(\"nn/\") && !me.startsWith(\"nn_\")) {\n  throw new IllegalStateException(\"Principal \" + me + \" will be 403-rejected by ImageServlet; fix keytab/admins ACL\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  TransferFsImage.getFileClient(...); // any image fetch\n} catch (IOException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"Only Namenode, Secondary Namenode\")) {\n    throw new ConfigurationException(\"2NN principal not authorized for image servlet - check keytab and dfs.cluster.administrators\", e);\n  }\n  throw e;\n}","preventionTips":["Run the Secondary/Standby with a keytab whose principal matches the NN-side kerberos principal patterns.","Keep dfs.cluster.administrators in config management so team changes do not lock out operators.","Smoke-test the servlet after any Kerberos/ACL change: curl --negotiate -u : http://nn:9870/imagetransfer?getimage=1."],"tags":["hdfs","security","kerberos","authorization","checkpoint"],"backgroundTag":"authorization-denied","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}