Repository: hadoop
Updated Branches:
  refs/heads/trunk 13cc0f50e -> 285d2c075


YARN-8449. RM HA for AM web server HTTPS Support. (Contributed by Robert Kanter)


Project: http://git-wip-us.apache.org/repos/asf/hadoop/repo
Commit: http://git-wip-us.apache.org/repos/asf/hadoop/commit/285d2c07
Tree: http://git-wip-us.apache.org/repos/asf/hadoop/tree/285d2c07
Diff: http://git-wip-us.apache.org/repos/asf/hadoop/diff/285d2c07

Branch: refs/heads/trunk
Commit: 285d2c07531a92067368ac4bdd21d309e6e81bc4
Parents: 13cc0f5
Author: Haibo Chen <haiboc...@apache.org>
Authored: Thu Oct 18 21:23:48 2018 -0700
Committer: Haibo Chen <haiboc...@apache.org>
Committed: Thu Oct 18 21:24:36 2018 -0700

----------------------------------------------------------------------
 .../server/resourcemanager/ResourceManager.java |   3 +
 .../recovery/FileSystemRMStateStore.java        |  53 +++++++++
 .../recovery/LeveldbRMStateStore.java           |  62 +++++++++++
 .../recovery/MemoryRMStateStore.java            |  18 +++
 .../recovery/NullRMStateStore.java              |   9 ++
 .../resourcemanager/recovery/RMStateStore.java  | 100 ++++++++++++++++-
 .../recovery/RMStateStoreEventType.java         |   1 +
 .../recovery/RMStateStoreProxyCAEvent.java      |  49 +++++++++
 .../recovery/ZKRMStateStore.java                |  60 +++++++++-
 .../security/ProxyCAManager.java                |  23 +++-
 .../recovery/RMStateStoreTestBase.java          |  33 ++++++
 .../recovery/TestFSRMStateStore.java            |   1 +
 .../recovery/TestLeveldbRMStateStore.java       |   6 +
 .../recovery/TestZKRMStateStore.java            |   1 +
 .../security/TestProxyCAManager.java            |  54 +++++++++
 .../hadoop/yarn/server/webproxy/ProxyCA.java    |  33 ++++++
 .../yarn/server/webproxy/TestProxyCA.java       | 109 +++++++++++++++++++
 17 files changed, 610 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hadoop/blob/285d2c07/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java
----------------------------------------------------------------------
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java
index 5f4ae6e..a89069a 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java
@@ -1510,6 +1510,9 @@ public class ResourceManager extends CompositeService
     // recover applications
     rmAppManager.recover(state);
 
+    // recover ProxyCA
+    rmContext.getProxyCAManager().recover(state);
+
     setSchedulerRecoveryStartAndWaitTime(state, conf);
   }
 

http://git-wip-us.apache.org/repos/asf/hadoop/blob/285d2c07/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/FileSystemRMStateStore.java
----------------------------------------------------------------------
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/FileSystemRMStateStore.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/FileSystemRMStateStore.java
index b797283..ed0486a 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/FileSystemRMStateStore.java
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/FileSystemRMStateStore.java
@@ -24,6 +24,8 @@ import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.HashMap;
@@ -114,6 +116,7 @@ public class FileSystemRMStateStore extends RMStateStore {
 
   Path amrmTokenSecretManagerRoot;
   private Path reservationRoot;
+  private Path proxyCARoot;
 
   @Override
   public synchronized void initInternal(Configuration conf)
@@ -125,6 +128,7 @@ public class FileSystemRMStateStore extends RMStateStore {
     amrmTokenSecretManagerRoot =
         new Path(rootDirPath, AMRMTOKEN_SECRET_MANAGER_ROOT);
     reservationRoot = new Path(rootDirPath, RESERVATION_SYSTEM_ROOT);
+    proxyCARoot = new Path(rootDirPath, PROXY_CA_ROOT);
     fsNumRetries =
         conf.getInt(YarnConfiguration.FS_RM_STATE_STORE_NUM_RETRIES,
             YarnConfiguration.DEFAULT_FS_RM_STATE_STORE_NUM_RETRIES);
@@ -157,6 +161,7 @@ public class FileSystemRMStateStore extends RMStateStore {
     mkdirsWithRetries(rmAppRoot);
     mkdirsWithRetries(amrmTokenSecretManagerRoot);
     mkdirsWithRetries(reservationRoot);
+    mkdirsWithRetries(proxyCARoot);
   }
 
   @Override
@@ -228,6 +233,8 @@ public class FileSystemRMStateStore extends RMStateStore {
     loadAMRMTokenSecretManagerState(rmState);
     // recover reservation state
     loadReservationSystemState(rmState);
+    // recover ProxyCAManager state
+    loadProxyCAManagerState(rmState);
     return rmState;
   }
 
@@ -395,6 +402,30 @@ public class FileSystemRMStateStore extends RMStateStore {
     }
   }
 
+  private void loadProxyCAManagerState(RMState rmState) throws Exception {
+    checkAndResumeUpdateOperation(proxyCARoot);
+
+    Path caCertPath = getNodePath(proxyCARoot, PROXY_CA_CERT_NODE);
+    Path caPrivateKeyPath = getNodePath(proxyCARoot, 
PROXY_CA_PRIVATE_KEY_NODE);
+
+    if (!existsWithRetries(caCertPath)
+        || !existsWithRetries(caPrivateKeyPath)) {
+      LOG.warn("Couldn't find Proxy CA data");
+      return;
+    }
+
+    FileStatus caCertFileStatus = getFileStatus(caCertPath);
+    byte[] caCertData = readFileWithRetries(caCertPath,
+        caCertFileStatus.getLen());
+
+    FileStatus caPrivateKeyFileStatus = getFileStatus(caPrivateKeyPath);
+    byte[] caPrivateKeyData = readFileWithRetries(caPrivateKeyPath,
+        caPrivateKeyFileStatus.getLen());
+
+    rmState.getProxyCAState().setCaCert(caCertData);
+    rmState.getProxyCAState().setCaPrivateKey(caPrivateKeyData);
+  }
+
   @Override
   public synchronized void storeApplicationStateInternal(ApplicationId appId,
       ApplicationStateData appStateDataPB) throws Exception {
@@ -593,6 +624,28 @@ public class FileSystemRMStateStore extends RMStateStore {
     }
   }
 
+  @Override
+  synchronized protected void storeProxyCACertState(
+      X509Certificate caCert, PrivateKey caPrivateKey) throws Exception {
+    byte[] caCertData = caCert.getEncoded();
+    byte[] caPrivateKeyData = caPrivateKey.getEncoded();
+
+    Path caCertPath = getNodePath(proxyCARoot, PROXY_CA_CERT_NODE);
+    Path caPrivateKeyPath = getNodePath(proxyCARoot, 
PROXY_CA_PRIVATE_KEY_NODE);
+
+    if (existsWithRetries(caCertPath)) {
+      updateFile(caCertPath, caCertData, true);
+    } else {
+      writeFileWithRetries(caCertPath, caCertData, true);
+    }
+
+    if (existsWithRetries(caPrivateKeyPath)) {
+      updateFile(caPrivateKeyPath, caPrivateKeyData, true);
+    } else {
+      writeFileWithRetries(caPrivateKeyPath, caPrivateKeyData, true);
+    }
+  }
+
   private Path getAppDir(Path root, ApplicationId appId) {
     return getNodePath(root, appId.toString());
   }

http://git-wip-us.apache.org/repos/asf/hadoop/blob/285d2c07/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/LeveldbRMStateStore.java
----------------------------------------------------------------------
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/LeveldbRMStateStore.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/LeveldbRMStateStore.java
index e7fb02f..0a91161 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/LeveldbRMStateStore.java
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/LeveldbRMStateStore.java
@@ -27,6 +27,8 @@ import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.File;
 import java.io.IOException;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
 import java.util.HashMap;
 import java.util.Map.Entry;
 import java.util.Timer;
@@ -129,6 +131,14 @@ public class LeveldbRMStateStore extends RMStateStore {
         + reservationId;
   }
 
+  private String getProxyCACertNodeKey() {
+    return PROXY_CA_ROOT + SEPARATOR + PROXY_CA_CERT_NODE;
+  }
+
+  private String getProxyCAPrivateKeyNodeKey() {
+    return PROXY_CA_ROOT + SEPARATOR + PROXY_CA_PRIVATE_KEY_NODE;
+  }
+
   @Override
   protected void initInternal(Configuration conf) throws Exception {
     compactionIntervalMsec = conf.getLong(
@@ -274,6 +284,7 @@ public class LeveldbRMStateStore extends RMStateStore {
      loadRMApps(rmState);
      loadAMRMTokenSecretManagerState(rmState);
     loadReservationState(rmState);
+    loadProxyCAManagerState(rmState);
     return rmState;
    }
 
@@ -578,6 +589,34 @@ public class LeveldbRMStateStore extends RMStateStore {
     }
   }
 
+  private void loadProxyCAManagerState(RMState rmState) throws Exception {
+    byte[] caCertData;
+    byte[] caPrivateKeyData;
+
+    String caCertKey = getProxyCACertNodeKey();
+    String caPrivateKeyKey = getProxyCAPrivateKeyNodeKey();
+
+    try {
+      caCertData = db.get(bytes(caCertKey));
+    } catch (DBException e) {
+      throw new IOException(e);
+    }
+
+    try {
+      caPrivateKeyData = db.get(bytes(caPrivateKeyKey));
+    } catch (DBException e) {
+      throw new IOException(e);
+    }
+
+    if (caCertData == null || caPrivateKeyData == null) {
+      LOG.warn("Couldn't find Proxy CA data");
+      return;
+    }
+
+    rmState.proxyCAState.setCaCert(caCertData);
+    rmState.proxyCAState.setCaPrivateKey(caPrivateKeyData);
+  }
+
   @Override
   protected void storeApplicationStateInternal(ApplicationId appId,
       ApplicationStateData appStateData) throws IOException {
@@ -812,6 +851,29 @@ public class LeveldbRMStateStore extends RMStateStore {
   }
 
   @Override
+  protected void storeProxyCACertState(
+      X509Certificate caCert, PrivateKey caPrivateKey) throws Exception {
+    byte[] caCertData = caCert.getEncoded();
+    byte[] caPrivateKeyData = caPrivateKey.getEncoded();
+
+    String caCertKey = getProxyCACertNodeKey();
+    String caPrivateKeyKey = getProxyCAPrivateKeyNodeKey();
+
+    try {
+      WriteBatch batch = db.createWriteBatch();
+      try {
+        batch.put(bytes(caCertKey), caCertData);
+        batch.put(bytes(caPrivateKeyKey), caPrivateKeyData);
+        db.write(batch);
+      } finally {
+        batch.close();
+      }
+    } catch (DBException e) {
+      throw new IOException(e);
+    }
+  }
+
+  @Override
   public void deleteStore() throws IOException {
     Path root = getStorageDir();
     LOG.info("Deleting state database at " + root);

http://git-wip-us.apache.org/repos/asf/hadoop/blob/285d2c07/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/MemoryRMStateStore.java
----------------------------------------------------------------------
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/MemoryRMStateStore.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/MemoryRMStateStore.java
index 219e10a..8c82af8 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/MemoryRMStateStore.java
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/MemoryRMStateStore.java
@@ -19,6 +19,8 @@
 package org.apache.hadoop.yarn.server.resourcemanager.recovery;
 
 import java.io.IOException;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
@@ -78,6 +80,15 @@ public class MemoryRMStateStore extends RMStateStore {
         state.amrmTokenSecretManagerState == null ? null
             : AMRMTokenSecretManagerState
               .newInstance(state.amrmTokenSecretManagerState);
+    if (state.proxyCAState.getCaCert() != null) {
+      byte[] caCertData = state.proxyCAState.getCaCert().getEncoded();
+      returnState.proxyCAState.setCaCert(caCertData);
+    }
+    if (state.proxyCAState.getCaPrivateKey() != null) {
+      byte[] caPrivateKeyData
+          = state.proxyCAState.getCaPrivateKey().getEncoded();
+      returnState.proxyCAState.setCaPrivateKey(caPrivateKeyData);
+    }
     return returnState;
   }
   
@@ -278,6 +289,13 @@ public class MemoryRMStateStore extends RMStateStore {
   }
 
   @Override
+  protected void storeProxyCACertState(
+      X509Certificate caCert, PrivateKey caPrivateKey) throws Exception {
+    state.getProxyCAState().setCaCert(caCert);
+    state.getProxyCAState().setCaPrivateKey(caPrivateKey);
+  }
+
+  @Override
   protected Version loadVersion() throws Exception {
     return null;
   }

http://git-wip-us.apache.org/repos/asf/hadoop/blob/285d2c07/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/NullRMStateStore.java
----------------------------------------------------------------------
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/NullRMStateStore.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/NullRMStateStore.java
index 4e134ac..1068f33 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/NullRMStateStore.java
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/NullRMStateStore.java
@@ -31,6 +31,9 @@ import 
org.apache.hadoop.yarn.server.resourcemanager.recovery.records.AMRMTokenS
 import 
org.apache.hadoop.yarn.server.resourcemanager.recovery.records.ApplicationAttemptStateData;
 import 
org.apache.hadoop.yarn.server.resourcemanager.recovery.records.ApplicationStateData;
 
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
 @Unstable
 public class NullRMStateStore extends RMStateStore {
 
@@ -174,4 +177,10 @@ public class NullRMStateStore extends RMStateStore {
   public void removeApplication(ApplicationId removeAppId) throws Exception {
     // Do nothing
   }
+
+  @Override
+  protected void storeProxyCACertState(
+      X509Certificate caCert, PrivateKey caPrivateKey) throws Exception {
+    // Do nothing
+  }
 }

http://git-wip-us.apache.org/repos/asf/hadoop/blob/285d2c07/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStore.java
----------------------------------------------------------------------
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStore.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStore.java
index ccd6fc9..deb79a5 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStore.java
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStore.java
@@ -18,6 +18,15 @@
 
 package org.apache.hadoop.yarn.server.resourcemanager.recovery;
 
+import java.io.ByteArrayInputStream;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -101,6 +110,9 @@ public abstract class RMStateStore extends AbstractService {
       "AMRMTokenSecretManagerRoot";
   protected static final String RESERVATION_SYSTEM_ROOT =
       "ReservationSystemRoot";
+  protected static final String PROXY_CA_ROOT = "ProxyCARoot";
+  protected static final String PROXY_CA_CERT_NODE = "caCert";
+  protected static final String PROXY_CA_PRIVATE_KEY_NODE = "caPrivateKey";
   protected static final String VERSION_NODE = "RMVersionNode";
   protected static final String EPOCH_NODE = "EpochNode";
   protected long baseEpoch;
@@ -183,6 +195,10 @@ public abstract class RMStateStore extends AbstractService 
{
           new RemoveReservationAllocationTransition())
       .addTransition(RMStateStoreState.ACTIVE, RMStateStoreState.FENCED,
           RMStateStoreEventType.FENCED)
+      .addTransition(RMStateStoreState.ACTIVE,
+          EnumSet.of(RMStateStoreState.ACTIVE, RMStateStoreState.FENCED),
+          RMStateStoreEventType.STORE_PROXY_CA_CERT,
+          new StoreProxyCACertTransition())
       .addTransition(RMStateStoreState.FENCED, RMStateStoreState.FENCED,
           EnumSet.of(
           RMStateStoreEventType.STORE_APP,
@@ -198,7 +214,8 @@ public abstract class RMStateStore extends AbstractService {
           RMStateStoreEventType.UPDATE_DELEGATION_TOKEN,
           RMStateStoreEventType.UPDATE_AMRM_TOKEN,
           RMStateStoreEventType.STORE_RESERVATION,
-          RMStateStoreEventType.REMOVE_RESERVATION));
+          RMStateStoreEventType.REMOVE_RESERVATION,
+          RMStateStoreEventType.STORE_PROXY_CA_CERT));
 
   private final StateMachine<RMStateStoreState,
                              RMStateStoreEventType,
@@ -615,6 +632,31 @@ public abstract class RMStateStore extends AbstractService 
{
     }
   }
 
+  private static class StoreProxyCACertTransition implements
+      MultipleArcTransition<RMStateStore, RMStateStoreEvent,
+          RMStateStoreState> {
+    @Override
+    public RMStateStoreState transition(RMStateStore store,
+        RMStateStoreEvent event) {
+      if (!(event instanceof RMStateStoreProxyCAEvent)) {
+        // should never happen
+        LOG.error("Illegal event type: " + event.getClass());
+        return RMStateStoreState.ACTIVE;
+      }
+      boolean isFenced = false;
+      RMStateStoreProxyCAEvent caEvent = (RMStateStoreProxyCAEvent) event;
+      try {
+        LOG.info("Storing CA Certificate and Private Key");
+        store.storeProxyCACertState(
+            caEvent.getCaCert(), caEvent.getCaPrivateKey());
+      } catch (Exception e) {
+        LOG.error("Error While Storing CA Certificate and Private Key", e);
+        isFenced = store.notifyStoreOperationFailedInternal(e);
+      }
+      return finalState(isFenced);
+    }
+  }
+
   private static RMStateStoreState finalState(boolean isFenced) {
     return isFenced ? RMStateStoreState.FENCED : RMStateStoreState.ACTIVE;
   }
@@ -676,6 +718,39 @@ public abstract class RMStateStore extends AbstractService 
{
     }
   }
 
+  public static class ProxyCAState {
+    private X509Certificate caCert;
+    private PrivateKey caPrivateKey;
+
+    public X509Certificate getCaCert() {
+      return caCert;
+    }
+
+    public PrivateKey getCaPrivateKey() {
+      return caPrivateKey;
+    }
+
+    public void setCaCert(X509Certificate caCert) {
+      this.caCert = caCert;
+    }
+
+    public void setCaPrivateKey(PrivateKey caPrivateKey) {
+      this.caPrivateKey = caPrivateKey;
+    }
+
+    public void setCaCert(byte[] caCertData) throws CertificateException {
+      ByteArrayInputStream bais = new ByteArrayInputStream(caCertData);
+      caCert = (X509Certificate)
+          CertificateFactory.getInstance("X.509").generateCertificate(bais);
+    }
+
+    public void setCaPrivateKey(byte[] caPrivateKeyData)
+        throws NoSuchAlgorithmException, InvalidKeySpecException {
+      caPrivateKey = KeyFactory.getInstance("RSA").generatePrivate(
+          new PKCS8EncodedKeySpec(caPrivateKeyData));
+    }
+  }
+
   /**
    * State of the ResourceManager
    */
@@ -690,6 +765,8 @@ public abstract class RMStateStore extends AbstractService {
     private Map<String, Map<ReservationId, ReservationAllocationStateProto>>
         reservationState = new TreeMap<>();
 
+    ProxyCAState proxyCAState = new ProxyCAState();
+
     public Map<ApplicationId, ApplicationStateData> getApplicationState() {
       return appState;
     }
@@ -706,6 +783,10 @@ public abstract class RMStateStore extends AbstractService 
{
         getReservationState() {
       return reservationState;
     }
+
+    public ProxyCAState getProxyCAState() {
+      return proxyCAState;
+    }
   }
     
   private Dispatcher rmDispatcher;
@@ -1273,4 +1354,21 @@ public abstract class RMStateStore extends 
AbstractService {
   protected EventHandler getRMStateStoreEventHandler() {
     return dispatcher.getEventHandler();
   }
+
+  /**
+   * ProxyCAManager calls this to store the CA Certificate and Private Key.
+   */
+  public void storeProxyCACert(X509Certificate caCert,
+      PrivateKey caPrivateKey) {
+    handleStoreEvent(new RMStateStoreProxyCAEvent(caCert, caPrivateKey,
+        RMStateStoreEventType.STORE_PROXY_CA_CERT));
+  }
+
+  /**
+   * Blocking API
+   * Derived classes must implement this method to store the CA Certificate
+   * and Private Key
+   */
+  protected abstract void storeProxyCACertState(
+      X509Certificate caCert, PrivateKey caPrivateKey) throws Exception;
 }

http://git-wip-us.apache.org/repos/asf/hadoop/blob/285d2c07/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreEventType.java
----------------------------------------------------------------------
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreEventType.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreEventType.java
index b34634d..3d60fd2 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreEventType.java
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreEventType.java
@@ -36,4 +36,5 @@ public enum RMStateStoreEventType {
   UPDATE_AMRM_TOKEN,
   STORE_RESERVATION,
   REMOVE_RESERVATION,
+  STORE_PROXY_CA_CERT,
 }

http://git-wip-us.apache.org/repos/asf/hadoop/blob/285d2c07/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreProxyCAEvent.java
----------------------------------------------------------------------
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreProxyCAEvent.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreProxyCAEvent.java
new file mode 100644
index 0000000..3664376
--- /dev/null
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreProxyCAEvent.java
@@ -0,0 +1,49 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.yarn.server.resourcemanager.recovery;
+
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+/**
+ * A event used to store ProxyCA information.
+ */
+public class RMStateStoreProxyCAEvent extends RMStateStoreEvent {
+  private X509Certificate caCert;
+  private PrivateKey caPrivateKey;
+
+  public RMStateStoreProxyCAEvent(RMStateStoreEventType type) {
+    super(type);
+  }
+
+  public RMStateStoreProxyCAEvent(X509Certificate caCert,
+      PrivateKey caPrivateKey, RMStateStoreEventType type) {
+    this(type);
+    this.caCert = caCert;
+    this.caPrivateKey = caPrivateKey;
+  }
+
+  public X509Certificate getCaCert() {
+    return caCert;
+  }
+
+  public PrivateKey getCaPrivateKey() {
+    return caPrivateKey;
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/285d2c07/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/ZKRMStateStore.java
----------------------------------------------------------------------
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/ZKRMStateStore.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/ZKRMStateStore.java
index bd76a8c..bcdbcfd 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/ZKRMStateStore.java
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/ZKRMStateStore.java
@@ -68,6 +68,8 @@ import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
 import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -153,6 +155,10 @@ import java.util.Set;
  *        |      ....
  *        |------PLAN_2
  *        ....
+ * |-- PROXY_CA_ROOT
+ *        |----- caCert
+ *        |----- caPrivateKey
+ *
  * Note: Changes from 1.1 to 1.2 - AMRMTokenSecretManager state has been saved
  * separately. The currentMasterkey and nextMasterkey have been stored.
  * Also, AMRMToken has been removed from ApplicationAttemptState.
@@ -198,6 +204,7 @@ public class ZKRMStateStore extends RMStateStore {
   private String dtSequenceNumberPath;
   private String amrmTokenSecretManagerRoot;
   private String reservationRoot;
+  private String proxyCARoot;
 
   @VisibleForTesting
   protected String znodeWorkingPath;
@@ -357,6 +364,7 @@ public class ZKRMStateStore extends RMStateStore {
         RM_DT_SEQUENTIAL_NUMBER_ZNODE_NAME);
     amrmTokenSecretManagerRoot =
         getNodePath(zkRootNodePath, AMRMTOKEN_SECRET_MANAGER_ROOT);
+    proxyCARoot = getNodePath(zkRootNodePath, PROXY_CA_ROOT);
     reservationRoot = getNodePath(zkRootNodePath, RESERVATION_SYSTEM_ROOT);
     zkManager = resourceManager.getZKManager();
     if(zkManager==null) {
@@ -402,6 +410,7 @@ public class ZKRMStateStore extends RMStateStore {
     create(dtSequenceNumberPath);
     create(amrmTokenSecretManagerRoot);
     create(reservationRoot);
+    create(proxyCARoot);
   }
 
   private void logRootNodeAcls(String prefix) throws Exception {
@@ -517,7 +526,8 @@ public class ZKRMStateStore extends RMStateStore {
     loadAMRMTokenSecretManagerState(rmState);
     // recover reservation state
     loadReservationSystemState(rmState);
-
+    // recover ProxyCAManager state
+    loadProxyCAManagerState(rmState);
     return rmState;
   }
 
@@ -813,6 +823,28 @@ public class ZKRMStateStore extends RMStateStore {
     }
   }
 
+  private void loadProxyCAManagerState(RMState rmState) throws Exception {
+    String caCertPath = getNodePath(proxyCARoot, PROXY_CA_CERT_NODE);
+    String caPrivateKeyPath = getNodePath(proxyCARoot,
+        PROXY_CA_PRIVATE_KEY_NODE);
+
+    if (!exists(caCertPath) || !exists(caPrivateKeyPath)) {
+      LOG.warn("Couldn't find Proxy CA data");
+      return;
+    }
+
+    byte[] caCertData = getData(caCertPath);
+    byte[] caPrivateKeyData = getData(caPrivateKeyPath);
+
+    if (caCertData == null || caPrivateKeyData == null) {
+      LOG.warn("Couldn't recover Proxy CA data");
+      return;
+    }
+
+    rmState.getProxyCAState().setCaCert(caCertData);
+    rmState.getProxyCAState().setCaPrivateKey(caPrivateKeyData);
+  }
+
   @Override
   public synchronized void storeApplicationStateInternal(ApplicationId appId,
       ApplicationStateData appStateDataPB) throws Exception {
@@ -1243,6 +1275,32 @@ public class ZKRMStateStore extends RMStateStore {
     }
   }
 
+  @Override
+  protected void storeProxyCACertState(
+      X509Certificate caCert, PrivateKey caPrivateKey) throws Exception {
+    byte[] caCertData = caCert.getEncoded();
+    byte[] caPrivateKeyData = caPrivateKey.getEncoded();
+
+    String caCertPath = getNodePath(proxyCARoot, PROXY_CA_CERT_NODE);
+    String caPrivateKeyPath = getNodePath(proxyCARoot,
+        PROXY_CA_PRIVATE_KEY_NODE);
+
+    if (exists(caCertPath)) {
+      zkManager.safeSetData(caCertPath, caCertData, -1, zkAcl,
+          fencingNodePath);
+    } else {
+      zkManager.safeCreate(caCertPath, caCertData, zkAcl,
+          CreateMode.PERSISTENT, zkAcl, fencingNodePath);
+    }
+    if (exists(caPrivateKeyPath)) {
+      zkManager.safeSetData(caPrivateKeyPath, caPrivateKeyData, -1, zkAcl,
+          fencingNodePath);
+    } else {
+      zkManager.safeCreate(caPrivateKeyPath, caPrivateKeyData, zkAcl,
+          CreateMode.PERSISTENT, zkAcl, fencingNodePath);
+    }
+  }
+
   /**
    * Get alternate path for app id if path according to configured split index
    * does not exist. We look for path based on all possible split indices.

http://git-wip-us.apache.org/repos/asf/hadoop/blob/285d2c07/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/security/ProxyCAManager.java
----------------------------------------------------------------------
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/security/ProxyCAManager.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/security/ProxyCAManager.java
index 12b677e..9ae343c 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/security/ProxyCAManager.java
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/security/ProxyCAManager.java
@@ -28,6 +28,11 @@ import org.apache.hadoop.yarn.server.webproxy.ProxyCA;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
 /**
  * Manager for {@link ProxyCA}, which contains the Certificate Authority for
  * AMs to have certificates for HTTPS communication with the RM Proxy.
@@ -40,16 +45,23 @@ public class ProxyCAManager extends AbstractService 
implements Recoverable {
 
   private ProxyCA proxyCA;
   private RMContext rmContext;
+  private boolean wasRecovered;
 
   public ProxyCAManager(ProxyCA proxyCA, RMContext rmContext) {
     super(ProxyCAManager.class.getName());
     this.proxyCA = proxyCA;
     this.rmContext = rmContext;
+    wasRecovered = false;
   }
 
   @Override
   protected void serviceStart() throws Exception {
-    proxyCA.init();
+    if (!wasRecovered) {
+      proxyCA.init();
+    }
+    wasRecovered = false;
+    rmContext.getStateStore().storeProxyCACert(
+        proxyCA.getCaCert(), proxyCA.getCaKeyPair().getPrivate());
     super.serviceStart();
   }
 
@@ -62,7 +74,12 @@ public class ProxyCAManager extends AbstractService 
implements Recoverable {
     return proxyCA;
   }
 
-  public void recover(RMState state) {
-    // TODO: RM HA YARN-8449
+  public void recover(RMState state)
+      throws GeneralSecurityException, IOException {
+    LOG.info("Recovering CA Certificate and Private Key");
+    X509Certificate caCert = state.getProxyCAState().getCaCert();
+    PrivateKey caPrivateKey = state.getProxyCAState().getCaPrivateKey();
+    proxyCA.init(caCert, caPrivateKey);
+    wasRecovered = true;
   }
 }

http://git-wip-us.apache.org/repos/asf/hadoop/blob/285d2c07/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreTestBase.java
----------------------------------------------------------------------
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreTestBase.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreTestBase.java
index 3454d72..cd44dda 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreTestBase.java
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreTestBase.java
@@ -83,6 +83,7 @@ import 
org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttemptS
 import 
org.apache.hadoop.yarn.server.resourcemanager.security.AMRMTokenSecretManager;
 import 
org.apache.hadoop.yarn.server.resourcemanager.security.ClientToAMTokenSecretManagerInRM;
 import org.apache.hadoop.yarn.server.security.MasterKeyData;
+import org.apache.hadoop.yarn.server.webproxy.ProxyCA;
 import org.apache.hadoop.yarn.util.ConverterUtils;
 import org.apache.hadoop.yarn.util.resource.DefaultResourceCalculator;
 import org.apache.hadoop.yarn.util.resource.ResourceCalculator;
@@ -892,6 +893,38 @@ public class RMStateStoreTestBase {
     Assert.assertNull(reservations);
   }
 
+  public void testProxyCA(
+      RMStateStoreHelper stateStoreHelper) throws Exception {
+    RMStateStore store = stateStoreHelper.getRMStateStore();
+    TestDispatcher dispatcher = new TestDispatcher();
+    store.setRMDispatcher(dispatcher);
+
+    ProxyCA originalProxyCA = new ProxyCA();
+    originalProxyCA.init();
+    store.storeProxyCACert(originalProxyCA.getCaCert(),
+        originalProxyCA.getCaKeyPair().getPrivate());
+
+    RMStateStore.ProxyCAState proxyCAState =
+        store.loadState().getProxyCAState();
+    Assert.assertEquals(originalProxyCA.getCaCert(), proxyCAState.getCaCert());
+    Assert.assertEquals(originalProxyCA.getCaKeyPair().getPrivate(),
+        proxyCAState.getCaPrivateKey());
+
+    // Try replacing with a different ProxyCA
+    ProxyCA newProxyCA = new ProxyCA();
+    newProxyCA.init();
+    Assert.assertNotEquals(originalProxyCA.getCaCert(), 
newProxyCA.getCaCert());
+    Assert.assertNotEquals(originalProxyCA.getCaKeyPair().getPrivate(),
+        newProxyCA.getCaKeyPair().getPrivate());
+    store.storeProxyCACert(newProxyCA.getCaCert(),
+        newProxyCA.getCaKeyPair().getPrivate());
+
+    proxyCAState = store.loadState().getProxyCAState();
+    Assert.assertEquals(newProxyCA.getCaCert(), proxyCAState.getCaCert());
+    Assert.assertEquals(newProxyCA.getCaKeyPair().getPrivate(),
+        proxyCAState.getCaPrivateKey());
+  }
+
   private void validateStoredReservation(
       RMStateStoreHelper stateStoreHelper, TestDispatcher dispatcher,
       RMContext rmContext, ReservationId r1, String planName,

http://git-wip-us.apache.org/repos/asf/hadoop/blob/285d2c07/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestFSRMStateStore.java
----------------------------------------------------------------------
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestFSRMStateStore.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestFSRMStateStore.java
index 14f5404..764424d 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestFSRMStateStore.java
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestFSRMStateStore.java
@@ -205,6 +205,7 @@ public class TestFSRMStateStore extends 
RMStateStoreTestBase {
       testRemoveAttempt(fsTester);
       testAMRMTokenSecretManagerStateStore(fsTester);
       testReservationStateStore(fsTester);
+      testProxyCA(fsTester);
     } finally {
       cluster.shutdown();
     }

http://git-wip-us.apache.org/repos/asf/hadoop/blob/285d2c07/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestLeveldbRMStateStore.java
----------------------------------------------------------------------
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestLeveldbRMStateStore.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestLeveldbRMStateStore.java
index 576ee7f..7a4ead4 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestLeveldbRMStateStore.java
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestLeveldbRMStateStore.java
@@ -125,6 +125,12 @@ public class TestLeveldbRMStateStore extends 
RMStateStoreTestBase {
   }
 
   @Test(timeout = 60000)
+  public void testProxyCA() throws Exception {
+    LeveldbStateStoreTester tester = new LeveldbStateStoreTester();
+    testProxyCA(tester);
+  }
+
+  @Test(timeout = 60000)
   public void testCompactionCycle() throws Exception {
     final DB mockdb = mock(DB.class);
     conf.setLong(YarnConfiguration.RM_LEVELDB_COMPACTION_INTERVAL_SECS, 1);

http://git-wip-us.apache.org/repos/asf/hadoop/blob/285d2c07/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestZKRMStateStore.java
----------------------------------------------------------------------
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestZKRMStateStore.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestZKRMStateStore.java
index 11be3b1..ce9e68d 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestZKRMStateStore.java
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestZKRMStateStore.java
@@ -289,6 +289,7 @@ public class TestZKRMStateStore extends 
RMStateStoreTestBase {
     testReservationStateStore(zkTester);
     ((TestZKRMStateStoreTester.TestZKRMStateStoreInternal)
         zkTester.getRMStateStore()).testRetryingCreateRootDir();
+    testProxyCA(zkTester);
   }
 
   @Test

http://git-wip-us.apache.org/repos/asf/hadoop/blob/285d2c07/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/security/TestProxyCAManager.java
----------------------------------------------------------------------
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/security/TestProxyCAManager.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/security/TestProxyCAManager.java
index 8633519..109ced9 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/security/TestProxyCAManager.java
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/security/TestProxyCAManager.java
@@ -21,14 +21,20 @@ package 
org.apache.hadoop.yarn.server.resourcemanager.security;
 
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
 import org.apache.hadoop.yarn.server.resourcemanager.RMContext;
+import org.apache.hadoop.yarn.server.resourcemanager.recovery.RMStateStore;
 import org.apache.hadoop.yarn.server.webproxy.ProxyCA;
 import org.junit.Assert;
 import org.junit.Test;
 
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 public class TestProxyCAManager {
 
@@ -36,16 +42,64 @@ public class TestProxyCAManager {
   public void testBasics() throws Exception {
     ProxyCA proxyCA = spy(new ProxyCA());
     RMContext rmContext = mock(RMContext.class);
+    RMStateStore rmStateStore = mock(RMStateStore.class);
+    when(rmContext.getStateStore()).thenReturn(rmStateStore);
     ProxyCAManager proxyCAManager = new ProxyCAManager(proxyCA, rmContext);
     proxyCAManager.init(new YarnConfiguration());
     Assert.assertEquals(proxyCA, proxyCAManager.getProxyCA());
+    verify(rmContext, times(0)).getStateStore();
+    verify(rmStateStore, times(0)).storeProxyCACert(any(), any());
     verify(proxyCA, times(0)).init();
     Assert.assertNull(proxyCA.getCaCert());
     Assert.assertNull(proxyCA.getCaKeyPair());
 
     proxyCAManager.start();
+    verify(rmContext, times(1)).getStateStore();
+    verify(rmStateStore, times(1)).storeProxyCACert(proxyCA.getCaCert(),
+        proxyCA.getCaKeyPair().getPrivate());
     verify(proxyCA, times(1)).init();
     Assert.assertNotNull(proxyCA.getCaCert());
     Assert.assertNotNull(proxyCA.getCaKeyPair());
   }
+
+  @Test
+  public void testRecover() throws Exception {
+    ProxyCA proxyCA = spy(new ProxyCA());
+    RMContext rmContext = mock(RMContext.class);
+    RMStateStore rmStateStore = mock(RMStateStore.class);
+    when(rmContext.getStateStore()).thenReturn(rmStateStore);
+    ProxyCAManager proxyCAManager = new ProxyCAManager(proxyCA, rmContext);
+    proxyCAManager.init(new YarnConfiguration());
+    Assert.assertEquals(proxyCA, proxyCAManager.getProxyCA());
+    verify(rmContext, times(0)).getStateStore();
+    verify(rmStateStore, times(0)).storeProxyCACert(any(), any());
+    verify(proxyCA, times(0)).init();
+    Assert.assertNull(proxyCA.getCaCert());
+    Assert.assertNull(proxyCA.getCaKeyPair());
+
+    RMStateStore.RMState rmState = mock(RMStateStore.RMState.class);
+    RMStateStore.ProxyCAState proxyCAState =
+        mock(RMStateStore.ProxyCAState.class);
+    // We need to use a real certificate + private key because of validation
+    // so just grab them from another ProxyCA
+    ProxyCA otherProxyCA = new ProxyCA();
+    otherProxyCA.init();
+    X509Certificate certificate = otherProxyCA.getCaCert();
+    when(proxyCAState.getCaCert()).thenReturn(certificate);
+    PrivateKey privateKey = otherProxyCA.getCaKeyPair().getPrivate();
+    when(proxyCAState.getCaPrivateKey()).thenReturn(privateKey);
+    when(rmState.getProxyCAState()).thenReturn(proxyCAState);
+    proxyCAManager.recover(rmState);
+    verify(proxyCA, times(1)).init(certificate, privateKey);
+    Assert.assertEquals(certificate, proxyCA.getCaCert());
+    Assert.assertEquals(privateKey, proxyCA.getCaKeyPair().getPrivate());
+
+    proxyCAManager.start();
+    verify(rmContext, times(1)).getStateStore();
+    verify(rmStateStore, times(1)).storeProxyCACert(proxyCA.getCaCert(),
+        proxyCA.getCaKeyPair().getPrivate());
+    verify(proxyCA, times(0)).init();
+    Assert.assertEquals(certificate, proxyCA.getCaCert());
+    Assert.assertEquals(privateKey, proxyCA.getCaKeyPair().getPrivate());
+  }
 }

http://git-wip-us.apache.org/repos/asf/hadoop/blob/285d2c07/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/ProxyCA.java
----------------------------------------------------------------------
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/ProxyCA.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/ProxyCA.java
index 26760d3..303ad97 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/ProxyCA.java
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/ProxyCA.java
@@ -69,6 +69,7 @@ import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.SecureRandom;
 import java.security.Security;
+import java.security.Signature;
 import java.security.SignatureException;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
@@ -107,7 +108,24 @@ public class ProxyCA {
 
   public void init() throws GeneralSecurityException, IOException {
     createCACertAndKeyPair();
+    initInternal();
+  }
+
+  public void init(X509Certificate caCert, PrivateKey caPrivateKey)
+      throws GeneralSecurityException, IOException {
+    if (caCert == null || caPrivateKey == null
+        || !verifyCertAndKeys(caCert, caPrivateKey)) {
+      LOG.warn("Could not verify Certificate, Public Key, and Private Key: " +
+          "regenerating");
+      createCACertAndKeyPair();
+    } else {
+      this.caCert = caCert;
+      this.caKeyPair = new KeyPair(caCert.getPublicKey(), caPrivateKey);
+    }
+    initInternal();
+  }
 
+  private void initInternal() throws GeneralSecurityException, IOException {
     defaultTrustManager = null;
     TrustManagerFactory factory = TrustManagerFactory.getInstance(
         TrustManagerFactory.getDefaultAlgorithm());
@@ -405,4 +423,19 @@ public class ProxyCA {
   public KeyPair getCaKeyPair() {
     return caKeyPair;
   }
+
+  private boolean verifyCertAndKeys(X509Certificate cert,
+      PrivateKey privateKey) throws GeneralSecurityException {
+    PublicKey publicKey = cert.getPublicKey();
+    byte[] data = new byte[2000];
+    srand.nextBytes(data);
+    Signature signer = Signature.getInstance("SHA512withRSA");
+    signer.initSign(privateKey);
+    signer.update(data);
+    byte[] sig = signer.sign();
+    signer = Signature.getInstance("SHA512withRSA");
+    signer.initVerify(publicKey);
+    signer.update(data);
+    return signer.verify(sig);
+  }
 }

http://git-wip-us.apache.org/repos/asf/hadoop/blob/285d2c07/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/TestProxyCA.java
----------------------------------------------------------------------
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/TestProxyCA.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/TestProxyCA.java
index 67bcea2..af97396 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/TestProxyCA.java
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/TestProxyCA.java
@@ -34,6 +34,7 @@ import javax.net.ssl.X509TrustManager;
 import javax.security.auth.x500.X500Principal;
 import java.security.InvalidKeyException;
 import java.security.Key;
+import java.security.KeyPair;
 import java.security.KeyStore;
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
@@ -70,6 +71,89 @@ public class TestProxyCA {
   }
 
   @Test
+  public void testInit2Null() throws Exception {
+    ProxyCA proxyCA = new ProxyCA();
+    Assert.assertNull(proxyCA.getCaCert());
+    Assert.assertNull(proxyCA.getCaKeyPair());
+    Assert.assertNull(proxyCA.getX509KeyManager());
+    Assert.assertNull(proxyCA.getHostnameVerifier());
+
+    // null certificate and private key
+    proxyCA.init(null, null);
+    Assert.assertNotNull(proxyCA.getCaCert());
+    Assert.assertNotNull(proxyCA.getCaKeyPair());
+    Assert.assertNotNull(proxyCA.getX509KeyManager());
+    Assert.assertNotNull(proxyCA.getHostnameVerifier());
+  }
+
+  @Test
+  public void testInit2Mismatch() throws Exception {
+    ProxyCA proxyCA = new ProxyCA();
+    Assert.assertNull(proxyCA.getCaCert());
+    Assert.assertNull(proxyCA.getCaKeyPair());
+    Assert.assertNull(proxyCA.getX509KeyManager());
+    Assert.assertNull(proxyCA.getHostnameVerifier());
+
+    // certificate and private key don't match
+    CertKeyPair pair1 = createCertAndKeyPair();
+    CertKeyPair pair2 = createCertAndKeyPair();
+    Assert.assertNotEquals(pair1.getCert(), pair2.getCert());
+    Assert.assertNotEquals(pair1.getKeyPair().getPrivate(),
+        pair2.getKeyPair().getPrivate());
+    Assert.assertNotEquals(pair1.getKeyPair().getPublic(),
+        pair2.getKeyPair().getPublic());
+    proxyCA.init(pair1.getCert(), pair2.getKeyPair().getPrivate());
+    Assert.assertNotNull(proxyCA.getCaCert());
+    Assert.assertNotNull(proxyCA.getCaKeyPair());
+    Assert.assertNotNull(proxyCA.getX509KeyManager());
+    Assert.assertNotNull(proxyCA.getHostnameVerifier());
+    Assert.assertNotEquals(proxyCA.getCaCert(), pair1.getCert());
+    Assert.assertNotEquals(proxyCA.getCaKeyPair().getPrivate(),
+        pair2.getKeyPair().getPrivate());
+    Assert.assertNotEquals(proxyCA.getCaKeyPair().getPublic(),
+        pair2.getKeyPair().getPublic());
+  }
+
+  @Test
+  public void testInit2Invalid() throws Exception {
+    ProxyCA proxyCA = new ProxyCA();
+    Assert.assertNull(proxyCA.getCaCert());
+    Assert.assertNull(proxyCA.getCaKeyPair());
+    Assert.assertNull(proxyCA.getX509KeyManager());
+    Assert.assertNull(proxyCA.getHostnameVerifier());
+
+    // Invalid key - fail the verification
+    X509Certificate certificate = Mockito.mock(X509Certificate.class);
+    PrivateKey privateKey = Mockito.mock(PrivateKey.class);
+    try {
+      proxyCA.init(certificate, privateKey);
+      Assert.fail("Expected InvalidKeyException");
+    } catch (InvalidKeyException e) {
+      // expected
+    }
+  }
+
+  @Test
+  public void testInit2() throws Exception {
+    ProxyCA proxyCA = new ProxyCA();
+    Assert.assertNull(proxyCA.getCaCert());
+    Assert.assertNull(proxyCA.getCaKeyPair());
+    Assert.assertNull(proxyCA.getX509KeyManager());
+    Assert.assertNull(proxyCA.getHostnameVerifier());
+
+    // certificate and private key do match
+    CertKeyPair pair = createCertAndKeyPair();
+    proxyCA.init(pair.getCert(), pair.getKeyPair().getPrivate());
+    Assert.assertEquals(pair.getCert(), proxyCA.getCaCert());
+    Assert.assertEquals(pair.getKeyPair().getPrivate(),
+        proxyCA.getCaKeyPair().getPrivate());
+    Assert.assertEquals(pair.getKeyPair().getPublic(),
+        proxyCA.getCaKeyPair().getPublic());
+    Assert.assertNotNull(proxyCA.getX509KeyManager());
+    Assert.assertNotNull(proxyCA.getHostnameVerifier());
+  }
+
+  @Test
   public void testCreateChildKeyStore() throws Exception {
     ProxyCA proxyCA = new ProxyCA();
     proxyCA.init();
@@ -515,4 +599,29 @@ public class TestProxyCA {
       Certificate[] certs) {
     return Arrays.copyOf(certs, certs.length, X509Certificate[].class);
   }
+
+  private static class CertKeyPair {
+    private X509Certificate cert;
+    private KeyPair keyPair;
+
+    public CertKeyPair(X509Certificate cert, KeyPair keyPair) {
+      this.cert = cert;
+      this.keyPair = keyPair;
+    }
+
+    public X509Certificate getCert() {
+      return cert;
+    }
+
+    public KeyPair getKeyPair() {
+      return keyPair;
+    }
+  }
+
+  private CertKeyPair createCertAndKeyPair() throws Exception {
+    // Re-use a ProxyCA to generate a valid Certificate and KeyPair
+    ProxyCA proxyCA = new ProxyCA();
+    proxyCA.init();
+    return new CertKeyPair(proxyCA.getCaCert(), proxyCA.getCaKeyPair());
+  }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: common-commits-unsubscr...@hadoop.apache.org
For additional commands, e-mail: common-commits-h...@hadoop.apache.org

Reply via email to