Browse Source

HDFS-14305. Fix serial number calculation in BlockTokenSecretManager to avoid token key ID overlap between NameNodes. Contributed by He Xiaoqiao.

Erik Krogen 6 years ago
parent
commit
b170626c74

+ 15 - 6
hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/security/token/block/BlockTokenSecretManager.java

@@ -63,6 +63,17 @@ public class BlockTokenSecretManager extends
 
 
   public static final Token<BlockTokenIdentifier> DUMMY_TOKEN = new Token<BlockTokenIdentifier>();
   public static final Token<BlockTokenIdentifier> DUMMY_TOKEN = new Token<BlockTokenIdentifier>();
 
 
+  /**
+   * In order to prevent serial No. of different NameNode from overlapping,
+   * Using 6 bits (identify 64=2^6 namenodes, and presuppose that no scenario
+   * where deploy more than 64 namenodes (include ANN, SBN, Observers, etc.)
+   * in one namespace) to identify index of NameNode, and the remainder 26 bits
+   * auto-incr to change the serial No.
+   */
+  @VisibleForTesting
+  public static final int NUM_VALID_BITS = 26;
+  private static final int LOW_MASK = (1 << NUM_VALID_BITS) - 1;
+
   private final boolean isMaster;
   private final boolean isMaster;
 
 
   /**
   /**
@@ -79,8 +90,8 @@ public class BlockTokenSecretManager extends
   private String blockPoolId;
   private String blockPoolId;
   private final String encryptionAlgorithm;
   private final String encryptionAlgorithm;
 
 
-  private final int intRange;
-  private final int nnRangeStart;
+  private final int nnIndex;
+
   private final boolean useProto;
   private final boolean useProto;
 
 
   private final SecureRandom nonceGenerator = new SecureRandom();
   private final SecureRandom nonceGenerator = new SecureRandom();
@@ -129,8 +140,7 @@ public class BlockTokenSecretManager extends
   private BlockTokenSecretManager(boolean isMaster, long keyUpdateInterval,
   private BlockTokenSecretManager(boolean isMaster, long keyUpdateInterval,
       long tokenLifetime, String blockPoolId, String encryptionAlgorithm,
       long tokenLifetime, String blockPoolId, String encryptionAlgorithm,
       int nnIndex, int numNNs, boolean useProto) {
       int nnIndex, int numNNs, boolean useProto) {
-    this.intRange = Integer.MAX_VALUE / numNNs;
-    this.nnRangeStart = intRange * nnIndex;
+    this.nnIndex = nnIndex;
     this.isMaster = isMaster;
     this.isMaster = isMaster;
     this.keyUpdateInterval = keyUpdateInterval;
     this.keyUpdateInterval = keyUpdateInterval;
     this.tokenLifetime = tokenLifetime;
     this.tokenLifetime = tokenLifetime;
@@ -144,8 +154,7 @@ public class BlockTokenSecretManager extends
 
 
   @VisibleForTesting
   @VisibleForTesting
   public synchronized void setSerialNo(int serialNo) {
   public synchronized void setSerialNo(int serialNo) {
-    // we mod the serial number by the range and then add that times the index
-    this.serialNo = (serialNo % intRange) + (nnRangeStart);
+    this.serialNo = (serialNo & LOW_MASK) | (nnIndex << NUM_VALID_BITS);
   }
   }
 
 
   public void setBlockPoolId(String blockPoolId) {
   public void setBlockPoolId(String blockPoolId) {

+ 30 - 1
hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestFailoverWithBlockTokensEnabled.java

@@ -116,7 +116,36 @@ public class TestFailoverWithBlockTokensEnabled {
       }
       }
     }
     }
   }
   }
-  
+
+  @Test
+  public void testSerialNumberMaskMatchIndex() {
+    BlockTokenSecretManager btsm1 = cluster.getNamesystem(0).getBlockManager()
+        .getBlockTokenSecretManager();
+    BlockTokenSecretManager btsm2 = cluster.getNamesystem(1).getBlockManager()
+        .getBlockTokenSecretManager();
+    BlockTokenSecretManager btsm3 = cluster.getNamesystem(2).getBlockManager()
+        .getBlockTokenSecretManager();
+    int[] testSet = {0, Integer.MAX_VALUE, Integer.MIN_VALUE,
+        Integer.MAX_VALUE / 2, Integer.MIN_VALUE / 2,
+        Integer.MAX_VALUE / 3, Integer.MIN_VALUE / 3};
+    for (int i = 0; i < testSet.length; i++) {
+      setAndCheckHighBitsSerialNumber(testSet[i], btsm1, 0);
+      setAndCheckHighBitsSerialNumber(testSet[i], btsm2, 1);
+      setAndCheckHighBitsSerialNumber(testSet[i], btsm3, 2);
+    }
+  }
+
+  /**
+   * Check mask of serial number if equal to index of NameNode.
+   */
+  private void setAndCheckHighBitsSerialNumber(int serialNumber,
+      BlockTokenSecretManager btsm, int nnIndex) {
+    btsm.setSerialNo(serialNumber);
+    int serialNo = btsm.getSerialNoForTesting();
+    int index = serialNo >> BlockTokenSecretManager.NUM_VALID_BITS;
+    assertEquals(index, nnIndex);
+  }
+
   @Test
   @Test
   public void ensureInvalidBlockTokensAreRejected() throws IOException,
   public void ensureInvalidBlockTokensAreRejected() throws IOException,
       URISyntaxException {
       URISyntaxException {