View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.regionserver.wal;
20  
21  import java.io.IOException;
22  import java.security.Key;
23  import java.security.KeyException;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  import org.apache.hadoop.fs.FSDataInputStream;
28  import org.apache.hadoop.hbase.HConstants;
29  import org.apache.hadoop.hbase.io.crypto.Cipher;
30  import org.apache.hadoop.hbase.io.crypto.Decryptor;
31  import org.apache.hadoop.hbase.io.crypto.Encryption;
32  import org.apache.hadoop.hbase.protobuf.generated.WALProtos.WALHeader;
33  import org.apache.hadoop.hbase.security.EncryptionUtil;
34  import org.apache.hadoop.hbase.security.User;
35  
36  public class SecureProtobufLogReader extends ProtobufLogReader {
37  
38    private static final Log LOG = LogFactory.getLog(SecureProtobufLogReader.class);
39  
40    private Decryptor decryptor = null;
41  
42    @Override
43    protected boolean readHeader(WALHeader.Builder builder, FSDataInputStream stream)
44        throws IOException {
45      boolean result = super.readHeader(builder, stream);
46      // We need to unconditionally handle the case where the WAL has a key in
47      // the header, meaning it is encrypted, even if ENABLE_WAL_ENCRYPTION is
48      // no longer set in the site configuration.
49      if (result && builder.hasEncryptionKey()) {
50        // Serialized header data has been merged into the builder from the
51        // stream.
52  
53        // Retrieve a usable key
54  
55        byte[] keyBytes = builder.getEncryptionKey().toByteArray();
56        Key key = null;
57        String walKeyName = conf.get(HConstants.CRYPTO_WAL_KEY_NAME_CONF_KEY);
58        // First try the WAL key, if one is configured
59        if (walKeyName != null) {
60          try {
61            key = EncryptionUtil.unwrapKey(conf, walKeyName, keyBytes);
62          } catch (KeyException e) {
63            if (LOG.isDebugEnabled()) {
64              LOG.debug("Unable to unwrap key with WAL key '" + walKeyName + "'");
65            }
66            key = null;
67          }
68        }
69        if (key == null) {
70          String masterKeyName = conf.get(HConstants.CRYPTO_MASTERKEY_NAME_CONF_KEY,
71            User.getCurrent().getShortName());
72          try {
73            // Then, try the cluster master key
74            key = EncryptionUtil.unwrapKey(conf, masterKeyName, keyBytes);
75          } catch (KeyException e) {
76            // If the current master key fails to unwrap, try the alternate, if
77            // one is configured
78            if (LOG.isDebugEnabled()) {
79              LOG.debug("Unable to unwrap key with current master key '" + masterKeyName + "'");
80            }
81            String alternateKeyName =
82              conf.get(HConstants.CRYPTO_MASTERKEY_ALTERNATE_NAME_CONF_KEY);
83            if (alternateKeyName != null) {
84              try {
85                key = EncryptionUtil.unwrapKey(conf, alternateKeyName, keyBytes);
86              } catch (KeyException ex) {
87                throw new IOException(ex);
88              }
89            } else {
90              throw new IOException(e);
91            }
92          }
93        }
94  
95        // Use the algorithm the key wants
96  
97        Cipher cipher = Encryption.getCipher(conf, key.getAlgorithm());
98        if (cipher == null) {
99          throw new IOException("Cipher '" + key.getAlgorithm() + "' is not available");
100       }
101 
102       // Set up the decryptor for this WAL
103 
104       decryptor = cipher.getDecryptor();
105       decryptor.setKey(key);
106 
107       if (LOG.isTraceEnabled()) {
108         LOG.trace("Initialized secure protobuf WAL: cipher=" + cipher.getName());
109       }
110     }
111 
112     return result;
113   }
114 
115   @Override
116   protected void initAfterCompression() throws IOException {
117     if (decryptor != null) {
118       WALCellCodec codec = SecureWALCellCodec.getCodec(this.conf, decryptor);
119       this.cellDecoder = codec.getDecoder(this.inputStream);
120       // We do not support compression with WAL encryption
121       this.compressionContext = null;
122       this.hasCompression = false;
123     } else {
124       super.initAfterCompression();
125     }
126   }
127 
128 }