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  import java.util.ArrayList;
25  import java.util.List;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.hadoop.fs.FSDataInputStream;
30  import org.apache.hadoop.hbase.HConstants;
31  import org.apache.hadoop.hbase.io.crypto.Cipher;
32  import org.apache.hadoop.hbase.io.crypto.Decryptor;
33  import org.apache.hadoop.hbase.io.crypto.Encryption;
34  import org.apache.hadoop.hbase.protobuf.generated.WALProtos.WALHeader;
35  import org.apache.hadoop.hbase.regionserver.wal.ProtobufLogReader.WALHdrResult;
36  import org.apache.hadoop.hbase.security.EncryptionUtil;
37  import org.apache.hadoop.hbase.security.User;
38  
39  public class SecureProtobufLogReader extends ProtobufLogReader {
40  
41    private static final Log LOG = LogFactory.getLog(SecureProtobufLogReader.class);
42  
43    private Decryptor decryptor = null;
44    private static List<String> writerClsNames = new ArrayList<String>();
45    static {
46      writerClsNames.add(ProtobufLogWriter.class.getSimpleName());
47      writerClsNames.add(SecureProtobufLogWriter.class.getSimpleName());
48    }
49  
50    @Override
51    protected List<String> getWriterClsNames() {
52      return writerClsNames;
53    }
54  
55    @Override
56    protected WALHdrContext readHeader(WALHeader.Builder builder, FSDataInputStream stream)
57        throws IOException {
58      WALHdrContext hdrCtxt = super.readHeader(builder, stream);
59      WALHdrResult result = hdrCtxt.getResult();
60      // We need to unconditionally handle the case where the WAL has a key in
61      // the header, meaning it is encrypted, even if ENABLE_WAL_ENCRYPTION is
62      // no longer set in the site configuration.
63      if (result == WALHdrResult.SUCCESS && builder.hasEncryptionKey()) {
64        // Serialized header data has been merged into the builder from the
65        // stream.
66  
67        // Retrieve a usable key
68  
69        byte[] keyBytes = builder.getEncryptionKey().toByteArray();
70        Key key = null;
71        String walKeyName = conf.get(HConstants.CRYPTO_WAL_KEY_NAME_CONF_KEY);
72        // First try the WAL key, if one is configured
73        if (walKeyName != null) {
74          try {
75            key = EncryptionUtil.unwrapKey(conf, walKeyName, keyBytes);
76          } catch (KeyException e) {
77            if (LOG.isDebugEnabled()) {
78              LOG.debug("Unable to unwrap key with WAL key '" + walKeyName + "'");
79            }
80            key = null;
81          }
82        }
83        if (key == null) {
84          String masterKeyName = conf.get(HConstants.CRYPTO_MASTERKEY_NAME_CONF_KEY,
85            User.getCurrent().getShortName());
86          try {
87            // Then, try the cluster master key
88            key = EncryptionUtil.unwrapKey(conf, masterKeyName, keyBytes);
89          } catch (KeyException e) {
90            // If the current master key fails to unwrap, try the alternate, if
91            // one is configured
92            if (LOG.isDebugEnabled()) {
93              LOG.debug("Unable to unwrap key with current master key '" + masterKeyName + "'");
94            }
95            String alternateKeyName =
96              conf.get(HConstants.CRYPTO_MASTERKEY_ALTERNATE_NAME_CONF_KEY);
97            if (alternateKeyName != null) {
98              try {
99                key = EncryptionUtil.unwrapKey(conf, alternateKeyName, keyBytes);
100             } catch (KeyException ex) {
101               throw new IOException(ex);
102             }
103           } else {
104             throw new IOException(e);
105           }
106         }
107       }
108 
109       // Use the algorithm the key wants
110 
111       Cipher cipher = Encryption.getCipher(conf, key.getAlgorithm());
112       if (cipher == null) {
113         throw new IOException("Cipher '" + key.getAlgorithm() + "' is not available");
114       }
115 
116       // Set up the decryptor for this WAL
117 
118       decryptor = cipher.getDecryptor();
119       decryptor.setKey(key);
120 
121       if (LOG.isTraceEnabled()) {
122         LOG.trace("Initialized secure protobuf WAL: cipher=" + cipher.getName());
123       }
124     }
125 
126     return hdrCtxt;
127   }
128 
129   @Override
130   protected void initAfterCompression(String cellCodecClsName) throws IOException {
131     if (decryptor != null && cellCodecClsName.equals(SecureWALCellCodec.class.getName())) {
132       WALCellCodec codec = SecureWALCellCodec.getCodec(this.conf, decryptor);
133       this.cellDecoder = codec.getDecoder(this.inputStream);
134       // We do not support compression with WAL encryption
135       this.compressionContext = null;
136       this.hasCompression = false;
137     } else {
138       super.initAfterCompression(cellCodecClsName);
139     }
140   }
141 
142 }