{"record":{"id":"a6fd94633239031a","repo":"apache/hadoop","slug":"cannot-create-a-secured-connection-if-datanode-lis","errorCode":null,"errorMessage":"Cannot create a secured connection if DataNode listens on unprivileged port (%d) and no protection is defined in configuration property %s.","messagePattern":"Cannot create a secured connection if DataNode listens on unprivileged port \\((.+?)\\) and no protection is defined in configuration property (.+?)\\.","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/protocol/datatransfer/sasl/SaslDataTransferServer.java","lineNumber":154,"sourceCode":"      LOG.debug(\n        \"SASL server doing general handshake for peer = {}, datanodeId = {}\",\n        peer, datanodeId);\n      return getSaslStreams(peer, underlyingOut, underlyingIn);\n    } else if (dnConf.getIgnoreSecurePortsForTesting()) {\n      // It's a secured cluster using non-privileged ports, but no SASL.  The\n      // only way this can happen is if the DataNode has\n      // ignore.secure.ports.for.testing configured, so this is a rare edge case.\n      LOG.debug(\n        \"SASL server skipping handshake in secured configuration with no SASL \"\n        + \"protection configured for peer = {}, datanodeId = {}\",\n        peer, datanodeId);\n      return new IOStreamPair(underlyingIn, underlyingOut);\n    } else {\n      // The error message here intentionally does not mention\n      // ignore.secure.ports.for.testing.  That's intended for dev use only.\n      // This code path is not expected to execute ever, because DataNode startup\n      // checks for invalid configuration and aborts.\n      throw new IOException(String.format(\"Cannot create a secured \" +\n        \"connection if DataNode listens on unprivileged port (%d) and no \" +\n        \"protection is defined in configuration property %s.\",\n        datanodeId.getXferPort(), DFS_DATA_TRANSFER_PROTECTION_KEY));\n    }\n  }\n\n  /**\n   * Receives SASL negotiation for specialized encrypted handshake.\n   *\n   * @param peer connection peer\n   * @param underlyingOut connection output stream\n   * @param underlyingIn connection input stream\n   * @return new pair of streams, wrapped after SASL negotiation\n   * @throws IOException for any error\n   */\n  private IOStreamPair getEncryptedStreams(Peer peer,\n      OutputStream underlyingOut, InputStream underlyingIn) throws IOException {\n    if (peer.hasSecureChannel() ||","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/protocol/datatransfer/sasl/SaslDataTransferServer.java#L136-L172","documentation":"SaslDataTransferServer only trusts a data connection without SASL when the DataNode listens on a privileged (<1024) xfer port; for unprivileged ports it requires dfs.data.transfer.protection to be configured. When a secured handshake is attempted on an unprivileged port with no protection configured, it throws this IOException. The source comments note this path should be unreachable because DataNode startup validates the combination (only ignore.secure.ports.for.testing can produce it), and the message intentionally omits that testing key.","triggerScenarios":"A client negotiates SASL with a DataNode whose xfer port is >= 1024 while dfs.data.transfer.protection is unset on the DataNode - typically because clients have dfs.data.transfer.protection (or secure data transfer expectations) but the DataNode config was never updated.","commonSituations":"Partial security rollout: dfs.data.transfer.protection=privacy set on clients/gateway but missing on DNs; editing the client hdfs-site.xml only; testing flags leaking into production configs.","solutions":["Set dfs.data.transfer.protection (e.g. authentication, integrity or privacy) on all DataNodes to the same value as the clients and restart them","If the cluster is not actually secured, remove dfs.data.transfer.protection / dfs.encrypt.data.transfer from the client configuration","Verify the DataNode's effective config and xfer port (jmx/dfsadmin) after the change"],"exampleFix":"// before: only clients have protection configured\nclient hdfs-site.xml: dfs.data.transfer.protection=privacy\n// after: same key set on every DataNode, then restart\n<property><name>dfs.data.transfer.protection</name><value>privacy</value></property>","handlingStrategy":"validation","validationCode":"// DataNode-side preflight (what DN startup already does - keep it that way)\nimport org.apache.hadoop.net.NetUtils;\n\nString protection = conf.getTrimmed(\"dfs.data.transfer.protection\", \"\");\nint xferPort = conf.getSocketAddr(\"dfs.datanode.address\", \"0.0.0.0:9866\").getPort();\nboolean privileged = xferPort < 1024;\nif (!protection.isEmpty() && !privileged) {\n  // SASL will be used: fine, but clients must match this value\n} else if (protection.isEmpty() && !privileged\n    && conf.getBoolean(\"ignore.secure.ports.for.testing\", false)) {\n  throw new IllegalStateException(\n      \"DN on unprivileged port \" + xferPort\n      + \" without dfs.data.transfer.protection - clients requiring SASL will fail\");\n}","typeGuard":null,"tryCatchPattern":"catch (IOException e) on the DN during peer handling when the message contains 'unprivileged port'; the connection cannot be salvaged - log the peer and ensure cluster-wide dfs.data.transfer.protection consistency.","preventionTips":["Set dfs.data.transfer.protection cluster-wide (same value on clients and every DataNode) via config management, never per-node","Never enable dfs.data.transfer.protection on clients before the DataNodes","After security changes, verify effective DN config via JMX before rolling clients"],"tags":["hdfs","sasl","security","datatransfer","datanode","configuration-mismatch"],"backgroundTag":"sasl-protection-mismatch","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}