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