001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.hadoop.hbase.security.token; 020 021import java.io.IOException; 022import java.util.List; 023import org.apache.hadoop.conf.Configuration; 024import org.apache.hadoop.hbase.log.HBaseMarkers; 025import org.apache.hadoop.hbase.util.Writables; 026import org.apache.hadoop.hbase.zookeeper.ZKListener; 027import org.apache.hadoop.hbase.zookeeper.ZKUtil; 028import org.apache.hadoop.hbase.zookeeper.ZKWatcher; 029import org.apache.hadoop.hbase.zookeeper.ZNodePaths; 030import org.apache.yetus.audience.InterfaceAudience; 031import org.apache.zookeeper.KeeperException; 032import org.slf4j.Logger; 033import org.slf4j.LoggerFactory; 034 035/** 036 * Synchronizes token encryption keys across cluster nodes. 037 */ 038@InterfaceAudience.Private 039public class ZKSecretWatcher extends ZKListener { 040 private static final String DEFAULT_ROOT_NODE = "tokenauth"; 041 private static final String DEFAULT_KEYS_PARENT = "keys"; 042 private static final Logger LOG = LoggerFactory.getLogger(ZKSecretWatcher.class); 043 044 private AuthenticationTokenSecretManager secretManager; 045 private String baseKeyZNode; 046 private String keysParentZNode; 047 048 public ZKSecretWatcher(Configuration conf, 049 ZKWatcher watcher, 050 AuthenticationTokenSecretManager secretManager) { 051 super(watcher); 052 this.secretManager = secretManager; 053 String keyZNodeParent = conf.get("zookeeper.znode.tokenauth.parent", DEFAULT_ROOT_NODE); 054 this.baseKeyZNode = ZNodePaths.joinZNode(watcher.getZNodePaths().baseZNode, keyZNodeParent); 055 this.keysParentZNode = ZNodePaths.joinZNode(baseKeyZNode, DEFAULT_KEYS_PARENT); 056 } 057 058 public void start() throws KeeperException { 059 watcher.registerListener(this); 060 // make sure the base node exists 061 ZKUtil.createWithParents(watcher, keysParentZNode); 062 063 if (ZKUtil.watchAndCheckExists(watcher, keysParentZNode)) { 064 List<ZKUtil.NodeAndData> nodes = 065 ZKUtil.getChildDataAndWatchForNewChildren(watcher, keysParentZNode); 066 refreshNodes(nodes); 067 } 068 } 069 070 @Override 071 public void nodeCreated(String path) { 072 if (path.equals(keysParentZNode)) { 073 try { 074 List<ZKUtil.NodeAndData> nodes = 075 ZKUtil.getChildDataAndWatchForNewChildren(watcher, keysParentZNode); 076 refreshNodes(nodes); 077 } catch (KeeperException ke) { 078 LOG.error(HBaseMarkers.FATAL, "Error reading data from zookeeper", ke); 079 watcher.abort("Error reading new key znode "+path, ke); 080 } 081 } 082 } 083 084 @Override 085 public void nodeDeleted(String path) { 086 if (keysParentZNode.equals(ZKUtil.getParent(path))) { 087 String keyId = ZKUtil.getNodeName(path); 088 try { 089 Integer id = Integer.valueOf(keyId); 090 secretManager.removeKey(id); 091 LOG.info("Node deleted id={}", id); 092 } catch (NumberFormatException nfe) { 093 LOG.error("Invalid znode name for key ID '"+keyId+"'", nfe); 094 } 095 } 096 } 097 098 @Override 099 public void nodeDataChanged(String path) { 100 if (keysParentZNode.equals(ZKUtil.getParent(path))) { 101 try { 102 byte[] data = ZKUtil.getDataAndWatch(watcher, path); 103 if (data == null || data.length == 0) { 104 LOG.debug("Ignoring empty node "+path); 105 return; 106 } 107 108 AuthenticationKey key = (AuthenticationKey)Writables.getWritable(data, 109 new AuthenticationKey()); 110 secretManager.addKey(key); 111 } catch (KeeperException ke) { 112 LOG.error(HBaseMarkers.FATAL, "Error reading data from zookeeper", ke); 113 watcher.abort("Error reading updated key znode "+path, ke); 114 } catch (IOException ioe) { 115 LOG.error(HBaseMarkers.FATAL, "Error reading key writables", ioe); 116 watcher.abort("Error reading key writables from znode "+path, ioe); 117 } 118 } 119 } 120 121 @Override 122 public void nodeChildrenChanged(String path) { 123 if (path.equals(keysParentZNode)) { 124 // keys changed 125 try { 126 List<ZKUtil.NodeAndData> nodes = 127 ZKUtil.getChildDataAndWatchForNewChildren(watcher, keysParentZNode); 128 refreshNodes(nodes); 129 } catch (KeeperException ke) { 130 LOG.error(HBaseMarkers.FATAL, "Error reading data from zookeeper", ke); 131 watcher.abort("Error reading changed keys from zookeeper", ke); 132 } 133 } 134 } 135 136 public String getRootKeyZNode() { 137 return baseKeyZNode; 138 } 139 140 private void refreshNodes(List<ZKUtil.NodeAndData> nodes) { 141 for (ZKUtil.NodeAndData n : nodes) { 142 String path = n.getNode(); 143 String keyId = ZKUtil.getNodeName(path); 144 try { 145 byte[] data = n.getData(); 146 if (data == null || data.length == 0) { 147 LOG.debug("Ignoring empty node "+path); 148 continue; 149 } 150 AuthenticationKey key = (AuthenticationKey)Writables.getWritable( 151 data, new AuthenticationKey()); 152 secretManager.addKey(key); 153 } catch (IOException ioe) { 154 LOG.error(HBaseMarkers.FATAL, "Failed reading new secret key for id '" + 155 keyId + "' from zk", ioe); 156 watcher.abort("Error deserializing key from znode "+path, ioe); 157 } 158 } 159 } 160 161 private String getKeyNode(int keyId) { 162 return ZNodePaths.joinZNode(keysParentZNode, Integer.toString(keyId)); 163 } 164 165 public void removeKeyFromZK(AuthenticationKey key) { 166 String keyZNode = getKeyNode(key.getKeyId()); 167 try { 168 ZKUtil.deleteNode(watcher, keyZNode); 169 } catch (KeeperException.NoNodeException nne) { 170 LOG.error("Non-existent znode "+keyZNode+" for key "+key.getKeyId(), nne); 171 } catch (KeeperException ke) { 172 LOG.error(HBaseMarkers.FATAL, "Failed removing znode "+keyZNode+" for key "+ 173 key.getKeyId(), ke); 174 watcher.abort("Unhandled zookeeper error removing znode "+keyZNode+ 175 " for key "+key.getKeyId(), ke); 176 } 177 } 178 179 public void addKeyToZK(AuthenticationKey key) { 180 String keyZNode = getKeyNode(key.getKeyId()); 181 try { 182 byte[] keyData = Writables.getBytes(key); 183 // TODO: is there any point in retrying beyond what ZK client does? 184 ZKUtil.createSetData(watcher, keyZNode, keyData); 185 } catch (KeeperException ke) { 186 LOG.error(HBaseMarkers.FATAL, "Unable to synchronize master key "+key.getKeyId()+ 187 " to znode "+keyZNode, ke); 188 watcher.abort("Unable to synchronize secret key "+ 189 key.getKeyId()+" in zookeeper", ke); 190 } catch (IOException ioe) { 191 // this can only happen from an error serializing the key 192 watcher.abort("Failed serializing key "+key.getKeyId(), ioe); 193 } 194 } 195 196 public void updateKeyInZK(AuthenticationKey key) { 197 String keyZNode = getKeyNode(key.getKeyId()); 198 try { 199 byte[] keyData = Writables.getBytes(key); 200 try { 201 ZKUtil.updateExistingNodeData(watcher, keyZNode, keyData, -1); 202 } catch (KeeperException.NoNodeException ne) { 203 // node was somehow removed, try adding it back 204 ZKUtil.createSetData(watcher, keyZNode, keyData); 205 } 206 } catch (KeeperException ke) { 207 LOG.error(HBaseMarkers.FATAL, "Unable to update master key "+key.getKeyId()+ 208 " in znode "+keyZNode); 209 watcher.abort("Unable to synchronize secret key "+ 210 key.getKeyId()+" in zookeeper", ke); 211 } catch (IOException ioe) { 212 // this can only happen from an error serializing the key 213 watcher.abort("Failed serializing key "+key.getKeyId(), ioe); 214 } 215 } 216 217 /** 218 * refresh keys 219 */ 220 synchronized void refreshKeys() { 221 try { 222 List<ZKUtil.NodeAndData> nodes = 223 ZKUtil.getChildDataAndWatchForNewChildren(watcher, keysParentZNode); 224 refreshNodes(nodes); 225 } catch (KeeperException ke) { 226 LOG.error(HBaseMarkers.FATAL, "Error reading data from zookeeper", ke); 227 watcher.abort("Error reading changed keys from zookeeper", ke); 228 } 229 } 230 231 /** 232 * get token keys parent node 233 * @return token keys parent node 234 */ 235 String getKeysParentZNode() { 236 return keysParentZNode; 237 } 238}