diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/CryptoCodec.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/CryptoCodec.java index 9bd1846552d..c5ac2ae242d 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/CryptoCodec.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/CryptoCodec.java @@ -82,12 +82,7 @@ public abstract class CryptoCodec implements Configurable { } } - if (codec != null) { - return codec; - } - - throw new RuntimeException("No available crypto codec which meets " + - "the cipher suite " + cipherSuite.getName() + "."); + return codec; } /** diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/CryptoProtocolVersion.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/CryptoProtocolVersion.java new file mode 100644 index 00000000000..fed8289317c --- /dev/null +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/CryptoProtocolVersion.java @@ -0,0 +1,90 @@ +/** + * 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.crypto; + +import org.apache.hadoop.classification.InterfaceAudience; + +/** + * Versions of the client/server protocol used for HDFS encryption. + */ +@InterfaceAudience.Private +public enum CryptoProtocolVersion { + UNKNOWN("Unknown", 1), + ENCRYPTION_ZONES("Encryption zones", 2); + + private final String description; + private final int version; + private Integer unknownValue = null; + + private static CryptoProtocolVersion[] supported = {ENCRYPTION_ZONES}; + + /** + * @return Array of supported protocol versions. + */ + public static CryptoProtocolVersion[] supported() { + return supported; + } + + CryptoProtocolVersion(String description, int version) { + this.description = description; + this.version = version; + } + + /** + * Returns if a given protocol version is supported. + * + * @param version version number + * @return true if the version is supported, else false + */ + public static boolean supports(CryptoProtocolVersion version) { + if (version.getVersion() == UNKNOWN.getVersion()) { + return false; + } + for (CryptoProtocolVersion v : CryptoProtocolVersion.values()) { + if (v.getVersion() == version.getVersion()) { + return true; + } + } + return false; + } + + public void setUnknownValue(int unknown) { + this.unknownValue = unknown; + } + + public int getUnknownValue() { + return unknownValue; + } + + public String getDescription() { + return description; + } + + public int getVersion() { + return version; + } + + @Override + public String toString() { + return "CryptoProtocolVersion{" + + "description='" + description + '\'' + + ", version=" + version + + ", unknownValue=" + unknownValue + + '}'; + } +} diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileEncryptionInfo.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileEncryptionInfo.java index 641709d98a3..27e0c852923 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileEncryptionInfo.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileEncryptionInfo.java @@ -20,6 +20,7 @@ package org.apache.hadoop.fs; import org.apache.commons.codec.binary.Hex; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.crypto.CipherSuite; +import org.apache.hadoop.crypto.CryptoProtocolVersion; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; @@ -32,6 +33,7 @@ import static com.google.common.base.Preconditions.checkNotNull; public class FileEncryptionInfo { private final CipherSuite cipherSuite; + private final CryptoProtocolVersion version; private final byte[] edek; private final byte[] iv; private final String keyName; @@ -47,9 +49,11 @@ public class FileEncryptionInfo { * @param ezKeyVersionName name of the KeyVersion used to encrypt the * encrypted data encryption key. */ - public FileEncryptionInfo(final CipherSuite suite, final byte[] edek, + public FileEncryptionInfo(final CipherSuite suite, + final CryptoProtocolVersion version, final byte[] edek, final byte[] iv, final String keyName, final String ezKeyVersionName) { checkNotNull(suite); + checkNotNull(version); checkNotNull(edek); checkNotNull(iv); checkNotNull(keyName); @@ -59,6 +63,7 @@ public class FileEncryptionInfo { checkArgument(iv.length == suite.getAlgorithmBlockSize(), "Unexpected IV length"); this.cipherSuite = suite; + this.version = version; this.edek = edek; this.iv = iv; this.keyName = keyName; @@ -73,6 +78,14 @@ public class FileEncryptionInfo { return cipherSuite; } + /** + * @return {@link org.apache.hadoop.crypto.CryptoProtocolVersion} to use + * to access the file. + */ + public CryptoProtocolVersion getCryptoProtocolVersion() { + return version; + } + /** * @return encrypted data encryption key (EDEK) for the file */ @@ -102,6 +115,7 @@ public class FileEncryptionInfo { public String toString() { StringBuilder builder = new StringBuilder("{"); builder.append("cipherSuite: " + cipherSuite); + builder.append(", cryptoProtocolVersion: " + version); builder.append(", edek: " + Hex.encodeHexString(edek)); builder.append(", iv: " + Hex.encodeHexString(iv)); builder.append(", keyName: " + keyName); diff --git a/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt b/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt index 2c39cddedea..35efea79c7f 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt +++ b/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt @@ -249,6 +249,8 @@ Release 2.6.0 - UNRELEASED HDFS-7119. Split error checks in AtomicFileOutputStream#close into separate conditions to improve diagnostics. (cnauroth) + HDFS-7077. Separate CipherSuite from crypto protocol version. (wang) + OPTIMIZATIONS HDFS-6690. Deduplicate xattr names in memory. (wang) diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSClient.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSClient.java index e9fe06f507d..bdfea127477 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSClient.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSClient.java @@ -21,7 +21,6 @@ import static org.apache.hadoop.crypto.key.KeyProvider.KeyVersion; import static org.apache.hadoop.crypto.key.KeyProviderCryptoExtension .EncryptedKeyVersion; import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_CRYPTO_CODEC_CLASSES_KEY_PREFIX; -import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_CRYPTO_CIPHER_SUITE_KEY; import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_BLOCK_SIZE_DEFAULT; import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_BLOCK_SIZE_KEY; import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_BYTES_PER_CHECKSUM_DEFAULT; @@ -104,6 +103,7 @@ import org.apache.hadoop.crypto.CipherSuite; import org.apache.hadoop.crypto.CryptoCodec; import org.apache.hadoop.crypto.CryptoInputStream; import org.apache.hadoop.crypto.CryptoOutputStream; +import org.apache.hadoop.crypto.CryptoProtocolVersion; import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension; import org.apache.hadoop.fs.BlockLocation; import org.apache.hadoop.fs.BlockStorageLocation; @@ -263,9 +263,6 @@ public class DFSClient implements java.io.Closeable, RemotePeerFactory, private static final DFSHedgedReadMetrics HEDGED_READ_METRIC = new DFSHedgedReadMetrics(); private static ThreadPoolExecutor HEDGED_READ_THREAD_POOL; - private final CryptoCodec codec; - @VisibleForTesting - List cipherSuites; @VisibleForTesting KeyProviderCryptoExtension provider; /** @@ -599,11 +596,6 @@ public class DFSClient implements java.io.Closeable, RemotePeerFactory, this.authority = nameNodeUri == null? "null": nameNodeUri.getAuthority(); this.clientName = "DFSClient_" + dfsClientConf.taskId + "_" + DFSUtil.getRandom().nextInt() + "_" + Thread.currentThread().getId(); - this.codec = CryptoCodec.getInstance(conf); - this.cipherSuites = Lists.newArrayListWithCapacity(1); - if (codec != null) { - cipherSuites.add(codec.getCipherSuite()); - } provider = DFSUtil.createKeyProviderCryptoExtension(conf); if (LOG.isDebugEnabled()) { if (provider == null) { @@ -1329,6 +1321,55 @@ public class DFSClient implements java.io.Closeable, RemotePeerFactory, } } + /** + * Obtain the crypto protocol version from the provided FileEncryptionInfo, + * checking to see if this version is supported by. + * + * @param feInfo FileEncryptionInfo + * @return CryptoProtocolVersion from the feInfo + * @throws IOException if the protocol version is unsupported. + */ + private static CryptoProtocolVersion getCryptoProtocolVersion + (FileEncryptionInfo feInfo) throws IOException { + final CryptoProtocolVersion version = feInfo.getCryptoProtocolVersion(); + if (!CryptoProtocolVersion.supports(version)) { + throw new IOException("Client does not support specified " + + "CryptoProtocolVersion " + version.getDescription() + " version " + + "number" + version.getVersion()); + } + return version; + } + + /** + * Obtain a CryptoCodec based on the CipherSuite set in a FileEncryptionInfo + * and the available CryptoCodecs configured in the Configuration. + * + * @param conf Configuration + * @param feInfo FileEncryptionInfo + * @return CryptoCodec + * @throws IOException if no suitable CryptoCodec for the CipherSuite is + * available. + */ + private static CryptoCodec getCryptoCodec(Configuration conf, + FileEncryptionInfo feInfo) throws IOException { + final CipherSuite suite = feInfo.getCipherSuite(); + if (suite.equals(CipherSuite.UNKNOWN)) { + throw new IOException("NameNode specified unknown CipherSuite with ID " + + suite.getUnknownValue() + ", cannot instantiate CryptoCodec."); + } + final CryptoCodec codec = CryptoCodec.getInstance(conf, suite); + if (codec == null) { + throw new UnknownCipherSuiteException( + "No configuration found for the cipher suite " + + suite.getConfigSuffix() + " prefixed with " + + HADOOP_SECURITY_CRYPTO_CODEC_CLASSES_KEY_PREFIX + + ". Please see the example configuration " + + "hadoop.security.crypto.codec.classes.EXAMPLECIPHERSUITE " + + "at core-default.xml for details."); + } + return codec; + } + /** * Wraps the stream in a CryptoInputStream if the underlying file is * encrypted. @@ -1338,17 +1379,10 @@ public class DFSClient implements java.io.Closeable, RemotePeerFactory, final FileEncryptionInfo feInfo = dfsis.getFileEncryptionInfo(); if (feInfo != null) { // File is encrypted, wrap the stream in a crypto stream. - KeyVersion decrypted = decryptEncryptedDataEncryptionKey(feInfo); - CryptoCodec codec = CryptoCodec - .getInstance(conf, feInfo.getCipherSuite()); - if (codec == null) { - throw new IOException("No configuration found for the cipher suite " - + feInfo.getCipherSuite().getConfigSuffix() + " prefixed with " - + HADOOP_SECURITY_CRYPTO_CODEC_CLASSES_KEY_PREFIX - + ". Please see the example configuration " - + "hadoop.security.crypto.codec.classes.EXAMPLECIPHERSUITE " - + "at core-default.xml for details."); - } + // Currently only one version, so no special logic based on the version # + getCryptoProtocolVersion(feInfo); + final CryptoCodec codec = getCryptoCodec(conf, feInfo); + final KeyVersion decrypted = decryptEncryptedDataEncryptionKey(feInfo); final CryptoInputStream cryptoIn = new CryptoInputStream(dfsis, codec, decrypted.getMaterial(), feInfo.getIV()); @@ -1376,15 +1410,10 @@ public class DFSClient implements java.io.Closeable, RemotePeerFactory, FileSystem.Statistics statistics, long startPos) throws IOException { final FileEncryptionInfo feInfo = dfsos.getFileEncryptionInfo(); if (feInfo != null) { - if (codec == null) { - throw new IOException("No configuration found for the cipher suite " - + HADOOP_SECURITY_CRYPTO_CIPHER_SUITE_KEY + " value prefixed with " - + HADOOP_SECURITY_CRYPTO_CODEC_CLASSES_KEY_PREFIX - + ". Please see the example configuration " - + "hadoop.security.crypto.codec.classes.EXAMPLECIPHERSUITE " - + "at core-default.xml for details."); - } // File is encrypted, wrap the stream in a crypto stream. + // Currently only one version, so no special logic based on the version # + getCryptoProtocolVersion(feInfo); + final CryptoCodec codec = getCryptoCodec(conf, feInfo); KeyVersion decrypted = decryptEncryptedDataEncryptionKey(feInfo); final CryptoOutputStream cryptoOut = new CryptoOutputStream(dfsos, codec, @@ -1599,7 +1628,7 @@ public class DFSClient implements java.io.Closeable, RemotePeerFactory, final DFSOutputStream result = DFSOutputStream.newStreamForCreate(this, src, masked, flag, createParent, replication, blockSize, progress, buffersize, dfsClientConf.createChecksum(checksumOpt), - favoredNodeStrs, cipherSuites); + favoredNodeStrs); beginFileLease(result.getFileId(), result); return result; } @@ -1646,7 +1675,7 @@ public class DFSClient implements java.io.Closeable, RemotePeerFactory, DataChecksum checksum = dfsClientConf.createChecksum(checksumOpt); result = DFSOutputStream.newStreamForCreate(this, src, absPermission, flag, createParent, replication, blockSize, progress, buffersize, - checksum, null, cipherSuites); + checksum, null); } beginFileLease(result.getFileId(), result); return result; diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSOutputStream.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSOutputStream.java index 1cb071091a3..fbe81712fb9 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSOutputStream.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSOutputStream.java @@ -43,7 +43,6 @@ import java.util.concurrent.atomic.AtomicReference; import com.google.common.base.Preconditions; import org.apache.hadoop.classification.InterfaceAudience; -import org.apache.hadoop.crypto.CipherSuite; import org.apache.hadoop.fs.CanSetDropBehind; import org.apache.hadoop.fs.CreateFlag; import org.apache.hadoop.fs.FSOutputSummer; @@ -54,6 +53,7 @@ import org.apache.hadoop.fs.Syncable; import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.hdfs.client.HdfsDataOutputStream; import org.apache.hadoop.hdfs.client.HdfsDataOutputStream.SyncFlag; +import org.apache.hadoop.crypto.CryptoProtocolVersion; import org.apache.hadoop.hdfs.protocol.DSQuotaExceededException; import org.apache.hadoop.hdfs.protocol.DatanodeInfo; import org.apache.hadoop.hdfs.protocol.ExtendedBlock; @@ -134,7 +134,10 @@ public class DFSOutputStream extends FSOutputSummer * errors (typically related to encryption zones and KeyProvider operations). */ @VisibleForTesting - public static final int CREATE_RETRY_COUNT = 10; + static final int CREATE_RETRY_COUNT = 10; + @VisibleForTesting + static CryptoProtocolVersion[] SUPPORTED_CRYPTO_VERSIONS = + CryptoProtocolVersion.supported(); private final DFSClient dfsClient; private Socket s; @@ -1655,8 +1658,7 @@ public class DFSOutputStream extends FSOutputSummer static DFSOutputStream newStreamForCreate(DFSClient dfsClient, String src, FsPermission masked, EnumSet flag, boolean createParent, short replication, long blockSize, Progressable progress, int buffersize, - DataChecksum checksum, String[] favoredNodes, - List cipherSuites) throws IOException { + DataChecksum checksum, String[] favoredNodes) throws IOException { HdfsFileStatus stat = null; // Retry the create if we get a RetryStartFileException up to a maximum @@ -1668,7 +1670,7 @@ public class DFSOutputStream extends FSOutputSummer try { stat = dfsClient.namenode.create(src, masked, dfsClient.clientName, new EnumSetWritable(flag), createParent, replication, - blockSize, cipherSuites); + blockSize, SUPPORTED_CRYPTO_VERSIONS); break; } catch (RemoteException re) { IOException e = re.unwrapRemoteException( @@ -1682,7 +1684,7 @@ public class DFSOutputStream extends FSOutputSummer SafeModeException.class, UnresolvedPathException.class, SnapshotAccessControlException.class, - UnknownCipherSuiteException.class); + UnknownCryptoProtocolVersionException.class); if (e instanceof RetryStartFileException) { if (retryCount > 0) { shouldRetry = true; diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/UnknownCipherSuiteException.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/UnknownCipherSuiteException.java index b85edf69c48..ec17bb8141f 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/UnknownCipherSuiteException.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/UnknownCipherSuiteException.java @@ -23,15 +23,12 @@ import java.io.IOException; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; +/** + * Thrown when an unknown cipher suite is encountered. + */ @InterfaceAudience.Public -@InterfaceStability.Evolving +@InterfaceStability.Stable public class UnknownCipherSuiteException extends IOException { - private static final long serialVersionUID = 8957192l; - - public UnknownCipherSuiteException() { - super(); - } - public UnknownCipherSuiteException(String msg) { super(msg); } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/UnknownCryptoProtocolVersionException.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/UnknownCryptoProtocolVersionException.java new file mode 100644 index 00000000000..0aac8c81f34 --- /dev/null +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/UnknownCryptoProtocolVersionException.java @@ -0,0 +1,38 @@ +/** + * 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.hdfs; + +import java.io.IOException; + +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.classification.InterfaceStability; + +@InterfaceAudience.Public +@InterfaceStability.Evolving +public class UnknownCryptoProtocolVersionException extends IOException { + private static final long serialVersionUID = 8957192l; + + public UnknownCryptoProtocolVersionException() { + super(); + } + + public UnknownCryptoProtocolVersionException(String msg) { + super(msg); + } +} diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/protocol/ClientProtocol.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/protocol/ClientProtocol.java index df67db65017..d29d2ebdccf 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/protocol/ClientProtocol.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/protocol/ClientProtocol.java @@ -24,7 +24,7 @@ import java.util.List; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; -import org.apache.hadoop.crypto.CipherSuite; +import org.apache.hadoop.crypto.CryptoProtocolVersion; import org.apache.hadoop.fs.BatchedRemoteIterator.BatchedEntries; import org.apache.hadoop.fs.CacheFlag; import org.apache.hadoop.fs.ContentSummary; @@ -163,6 +163,7 @@ public interface ClientProtocol { * @param createParent create missing parent directory if true * @param replication block replication factor. * @param blockSize maximum block size. + * @param supportedVersions CryptoProtocolVersions supported by the client * * @return the status of the created file, it could be null if the server * doesn't support returning the file status @@ -191,7 +192,7 @@ public interface ClientProtocol { public HdfsFileStatus create(String src, FsPermission masked, String clientName, EnumSetWritable flag, boolean createParent, short replication, long blockSize, - List cipherSuites) + CryptoProtocolVersion[] supportedVersions) throws AccessControlException, AlreadyBeingCreatedException, DSQuotaExceededException, FileAlreadyExistsException, FileNotFoundException, NSQuotaExceededException, diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/protocol/EncryptionZone.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/protocol/EncryptionZone.java index 58e9ebad173..f1441b5727f 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/protocol/EncryptionZone.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/protocol/EncryptionZone.java @@ -22,6 +22,7 @@ import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.crypto.CipherSuite; +import org.apache.hadoop.crypto.CryptoProtocolVersion; /** * A simple class for representing an encryption zone. Presently an encryption @@ -35,13 +36,15 @@ public class EncryptionZone { private final long id; private final String path; private final CipherSuite suite; + private final CryptoProtocolVersion version; private final String keyName; - public EncryptionZone(long id, String path, - CipherSuite suite, String keyName) { + public EncryptionZone(long id, String path, CipherSuite suite, + CryptoProtocolVersion version, String keyName) { this.id = id; this.path = path; this.suite = suite; + this.version = version; this.keyName = keyName; } @@ -57,15 +60,20 @@ public class EncryptionZone { return suite; } + public CryptoProtocolVersion getVersion() { return version; } + public String getKeyName() { return keyName; } @Override public int hashCode() { - return new HashCodeBuilder(13, 31). - append(id).append(path). - append(suite).append(keyName). + return new HashCodeBuilder(13, 31) + .append(id) + .append(path) + .append(suite) + .append(version) + .append(keyName). toHashCode(); } @@ -86,6 +94,7 @@ public class EncryptionZone { append(id, rhs.id). append(path, rhs.path). append(suite, rhs.suite). + append(version, rhs.version). append(keyName, rhs.keyName). isEquals(); } @@ -95,6 +104,7 @@ public class EncryptionZone { return "EncryptionZone [id=" + id + ", path=" + path + ", suite=" + suite + + ", version=" + version + ", keyName=" + keyName + "]"; } } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/protocolPB/ClientNamenodeProtocolServerSideTranslatorPB.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/protocolPB/ClientNamenodeProtocolServerSideTranslatorPB.java index adad3b81b87..27904e1087a 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/protocolPB/ClientNamenodeProtocolServerSideTranslatorPB.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/protocolPB/ClientNamenodeProtocolServerSideTranslatorPB.java @@ -394,8 +394,9 @@ public class ClientNamenodeProtocolServerSideTranslatorPB implements HdfsFileStatus result = server.create(req.getSrc(), PBHelper.convert(req.getMasked()), req.getClientName(), PBHelper.convertCreateFlag(req.getCreateFlag()), req.getCreateParent(), - (short) req.getReplication(), req.getBlockSize(), - PBHelper.convertCipherSuiteProtos(req.getCipherSuitesList())); + (short) req.getReplication(), req.getBlockSize(), + PBHelper.convertCryptoProtocolVersions( + req.getCryptoProtocolVersionList())); if (result != null) { return CreateResponseProto.newBuilder().setFs(PBHelper.convert(result)) diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/protocolPB/ClientNamenodeProtocolTranslatorPB.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/protocolPB/ClientNamenodeProtocolTranslatorPB.java index 90b52e0963e..df8965cf4d4 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/protocolPB/ClientNamenodeProtocolTranslatorPB.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/protocolPB/ClientNamenodeProtocolTranslatorPB.java @@ -28,6 +28,7 @@ import com.google.common.collect.Lists; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.crypto.CipherSuite; +import org.apache.hadoop.crypto.CryptoProtocolVersion; import org.apache.hadoop.fs.BatchedRemoteIterator.BatchedEntries; import org.apache.hadoop.fs.CacheFlag; import org.apache.hadoop.fs.ContentSummary; @@ -274,7 +275,7 @@ public class ClientNamenodeProtocolTranslatorPB implements public HdfsFileStatus create(String src, FsPermission masked, String clientName, EnumSetWritable flag, boolean createParent, short replication, long blockSize, - List cipherSuites) + CryptoProtocolVersion[] supportedVersions) throws AccessControlException, AlreadyBeingCreatedException, DSQuotaExceededException, FileAlreadyExistsException, FileNotFoundException, NSQuotaExceededException, @@ -288,9 +289,7 @@ public class ClientNamenodeProtocolTranslatorPB implements .setCreateParent(createParent) .setReplication(replication) .setBlockSize(blockSize); - if (cipherSuites != null) { - builder.addAllCipherSuites(PBHelper.convertCipherSuites(cipherSuites)); - } + builder.addAllCryptoProtocolVersion(PBHelper.convert(supportedVersions)); CreateRequestProto req = builder.build(); try { CreateResponseProto res = rpcProxy.create(null, req); diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/protocolPB/PBHelper.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/protocolPB/PBHelper.java index 140b47a937c..d26e1474cd6 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/protocolPB/PBHelper.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/protocolPB/PBHelper.java @@ -20,6 +20,8 @@ package org.apache.hadoop.hdfs.protocolPB; import static com.google.common.base.Preconditions.checkNotNull; import static org.apache.hadoop.hdfs.protocol.proto.EncryptionZonesProtos .EncryptionZoneProto; +import static org.apache.hadoop.hdfs.protocol.proto.HdfsProtos.CipherSuiteProto; +import static org.apache.hadoop.hdfs.protocol.proto.HdfsProtos.CryptoProtocolVersionProto; import java.io.EOFException; import java.io.IOException; @@ -59,6 +61,7 @@ import org.apache.hadoop.hdfs.protocol.CachePoolStats; import org.apache.hadoop.crypto.CipherSuite; import org.apache.hadoop.hdfs.protocol.ClientProtocol; import org.apache.hadoop.hdfs.protocol.CorruptFileBlocks; +import org.apache.hadoop.crypto.CryptoProtocolVersion; import org.apache.hadoop.hdfs.protocol.DatanodeID; import org.apache.hadoop.hdfs.protocol.DatanodeInfo; import org.apache.hadoop.hdfs.protocol.DatanodeInfo.AdminStates; @@ -2392,15 +2395,17 @@ public class PBHelper { public static EncryptionZoneProto convert(EncryptionZone zone) { return EncryptionZoneProto.newBuilder() .setId(zone.getId()) - .setKeyName(zone.getKeyName()) .setPath(zone.getPath()) .setSuite(convert(zone.getSuite())) + .setCryptoProtocolVersion(convert(zone.getVersion())) + .setKeyName(zone.getKeyName()) .build(); } public static EncryptionZone convert(EncryptionZoneProto proto) { - return new EncryptionZone(proto.getId(), proto.getPath(), - convert(proto.getSuite()), proto.getKeyName()); + return new EncryptionZone(proto.getId(), proto.getPath(), + convert(proto.getSuite()), convert(proto.getCryptoProtocolVersion()), + proto.getKeyName()); } public static ShortCircuitShmSlotProto convert(SlotId slotId) { @@ -2670,18 +2675,18 @@ public class PBHelper { builder.build()).build(); } - public static HdfsProtos.CipherSuite convert(CipherSuite suite) { + public static CipherSuiteProto convert(CipherSuite suite) { switch (suite) { case UNKNOWN: - return HdfsProtos.CipherSuite.UNKNOWN; + return CipherSuiteProto.UNKNOWN; case AES_CTR_NOPADDING: - return HdfsProtos.CipherSuite.AES_CTR_NOPADDING; + return CipherSuiteProto.AES_CTR_NOPADDING; default: return null; } } - public static CipherSuite convert(HdfsProtos.CipherSuite proto) { + public static CipherSuite convert(CipherSuiteProto proto) { switch (proto) { case AES_CTR_NOPADDING: return CipherSuite.AES_CTR_NOPADDING; @@ -2693,26 +2698,49 @@ public class PBHelper { } } - public static List convertCipherSuites - (List suites) { - if (suites == null) { - return null; - } - List protos = - Lists.newArrayListWithCapacity(suites.size()); - for (CipherSuite suite : suites) { - protos.add(convert(suite)); + public static List convert( + CryptoProtocolVersion[] versions) { + List protos = + Lists.newArrayListWithCapacity(versions.length); + for (CryptoProtocolVersion v: versions) { + protos.add(convert(v)); } return protos; } - public static List convertCipherSuiteProtos( - List protos) { - List suites = Lists.newArrayListWithCapacity(protos.size()); - for (HdfsProtos.CipherSuite proto : protos) { - suites.add(convert(proto)); + public static CryptoProtocolVersion[] convertCryptoProtocolVersions( + List protos) { + List versions = + Lists.newArrayListWithCapacity(protos.size()); + for (CryptoProtocolVersionProto p: protos) { + versions.add(convert(p)); + } + return versions.toArray(new CryptoProtocolVersion[] {}); + } + + public static CryptoProtocolVersion convert(CryptoProtocolVersionProto + proto) { + switch(proto) { + case ENCRYPTION_ZONES: + return CryptoProtocolVersion.ENCRYPTION_ZONES; + default: + // Set to UNKNOWN and stash the unknown enum value + CryptoProtocolVersion version = CryptoProtocolVersion.UNKNOWN; + version.setUnknownValue(proto.getNumber()); + return version; + } + } + + public static CryptoProtocolVersionProto convert(CryptoProtocolVersion + version) { + switch(version) { + case UNKNOWN: + return CryptoProtocolVersionProto.UNKNOWN_PROTOCOL_VERSION; + case ENCRYPTION_ZONES: + return CryptoProtocolVersionProto.ENCRYPTION_ZONES; + default: + return null; } - return suites; } public static HdfsProtos.FileEncryptionInfoProto convert( @@ -2722,6 +2750,7 @@ public class PBHelper { } return HdfsProtos.FileEncryptionInfoProto.newBuilder() .setSuite(convert(info.getCipherSuite())) + .setCryptoProtocolVersion(convert(info.getCryptoProtocolVersion())) .setKey(getByteString(info.getEncryptedDataEncryptionKey())) .setIv(getByteString(info.getIV())) .setEzKeyVersionName(info.getEzKeyVersionName()) @@ -2742,12 +2771,13 @@ public class PBHelper { } public static HdfsProtos.ZoneEncryptionInfoProto convert( - CipherSuite suite, String keyName) { - if (suite == null || keyName == null) { + CipherSuite suite, CryptoProtocolVersion version, String keyName) { + if (suite == null || version == null || keyName == null) { return null; } return HdfsProtos.ZoneEncryptionInfoProto.newBuilder() .setSuite(convert(suite)) + .setCryptoProtocolVersion(convert(version)) .setKeyName(keyName) .build(); } @@ -2758,23 +2788,27 @@ public class PBHelper { return null; } CipherSuite suite = convert(proto.getSuite()); + CryptoProtocolVersion version = convert(proto.getCryptoProtocolVersion()); byte[] key = proto.getKey().toByteArray(); byte[] iv = proto.getIv().toByteArray(); String ezKeyVersionName = proto.getEzKeyVersionName(); String keyName = proto.getKeyName(); - return new FileEncryptionInfo(suite, key, iv, keyName, ezKeyVersionName); + return new FileEncryptionInfo(suite, version, key, iv, keyName, + ezKeyVersionName); } public static FileEncryptionInfo convert( HdfsProtos.PerFileEncryptionInfoProto fileProto, - CipherSuite suite, String keyName) { - if (fileProto == null || suite == null || keyName == null) { + CipherSuite suite, CryptoProtocolVersion version, String keyName) { + if (fileProto == null || suite == null || version == null || + keyName == null) { return null; } byte[] key = fileProto.getKey().toByteArray(); byte[] iv = fileProto.getIv().toByteArray(); String ezKeyVersionName = fileProto.getEzKeyVersionName(); - return new FileEncryptionInfo(suite, key, iv, keyName, ezKeyVersionName); + return new FileEncryptionInfo(suite, version, key, iv, keyName, + ezKeyVersionName); } } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/EncryptionZoneManager.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/EncryptionZoneManager.java index e22f8b97423..0e83583fb35 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/EncryptionZoneManager.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/EncryptionZoneManager.java @@ -27,6 +27,7 @@ import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.crypto.CipherSuite; +import org.apache.hadoop.crypto.CryptoProtocolVersion; import org.apache.hadoop.fs.UnresolvedLinkException; import org.apache.hadoop.fs.XAttr; import org.apache.hadoop.fs.XAttrSetFlag; @@ -57,7 +58,8 @@ public class EncryptionZoneManager { .class); public static final EncryptionZone NULL_EZ = - new EncryptionZone(-1, "", CipherSuite.UNKNOWN, ""); + new EncryptionZone(-1, "", CipherSuite.UNKNOWN, + CryptoProtocolVersion.UNKNOWN, ""); /** * EncryptionZoneInt is the internal representation of an encryption zone. The @@ -67,11 +69,16 @@ public class EncryptionZoneManager { private static class EncryptionZoneInt { private final long inodeId; private final CipherSuite suite; + private final CryptoProtocolVersion version; private final String keyName; - EncryptionZoneInt(long inodeId, CipherSuite suite, String keyName) { + EncryptionZoneInt(long inodeId, CipherSuite suite, + CryptoProtocolVersion version, String keyName) { + Preconditions.checkArgument(suite != CipherSuite.UNKNOWN); + Preconditions.checkArgument(version != CryptoProtocolVersion.UNKNOWN); this.inodeId = inodeId; this.suite = suite; + this.version = version; this.keyName = keyName; } @@ -83,6 +90,8 @@ public class EncryptionZoneManager { return suite; } + CryptoProtocolVersion getVersion() { return version; } + String getKeyName() { return keyName; } @@ -118,9 +127,10 @@ public class EncryptionZoneManager { * @param inodeId of the encryption zone * @param keyName encryption zone key name */ - void addEncryptionZone(Long inodeId, CipherSuite suite, String keyName) { + void addEncryptionZone(Long inodeId, CipherSuite suite, + CryptoProtocolVersion version, String keyName) { assert dir.hasWriteLock(); - unprotectedAddEncryptionZone(inodeId, suite, keyName); + unprotectedAddEncryptionZone(inodeId, suite, version, keyName); } /** @@ -132,9 +142,9 @@ public class EncryptionZoneManager { * @param keyName encryption zone key name */ void unprotectedAddEncryptionZone(Long inodeId, - CipherSuite suite, String keyName) { + CipherSuite suite, CryptoProtocolVersion version, String keyName) { final EncryptionZoneInt ez = new EncryptionZoneInt( - inodeId, suite, keyName); + inodeId, suite, version, keyName); encryptionZones.put(inodeId, ez); } @@ -219,7 +229,7 @@ public class EncryptionZoneManager { return NULL_EZ; } else { return new EncryptionZone(ezi.getINodeId(), getFullPathName(ezi), - ezi.getSuite(), ezi.getKeyName()); + ezi.getSuite(), ezi.getVersion(), ezi.getKeyName()); } } @@ -275,7 +285,8 @@ public class EncryptionZoneManager { *

* Called while holding the FSDirectory lock. */ - XAttr createEncryptionZone(String src, CipherSuite suite, String keyName) + XAttr createEncryptionZone(String src, CipherSuite suite, + CryptoProtocolVersion version, String keyName) throws IOException { assert dir.hasWriteLock(); if (dir.isNonEmptyDirectory(src)) { @@ -296,7 +307,7 @@ public class EncryptionZoneManager { } final HdfsProtos.ZoneEncryptionInfoProto proto = - PBHelper.convert(suite, keyName); + PBHelper.convert(suite, version, keyName); final XAttr ezXAttr = XAttrHelper .buildXAttr(CRYPTO_XATTR_ENCRYPTION_ZONE, proto.toByteArray()); @@ -341,7 +352,7 @@ public class EncryptionZoneManager { } // Add the EZ to the result list zones.add(new EncryptionZone(ezi.getINodeId(), pathName, - ezi.getSuite(), ezi.getKeyName())); + ezi.getSuite(), ezi.getVersion(), ezi.getKeyName())); count++; if (count >= numResponses) { break; diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSDirectory.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSDirectory.java index 4f35846c381..cb97ba4f3ea 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSDirectory.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSDirectory.java @@ -38,6 +38,7 @@ import org.apache.hadoop.HadoopIllegalArgumentException; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.crypto.CipherSuite; +import org.apache.hadoop.crypto.CryptoProtocolVersion; import org.apache.hadoop.fs.ContentSummary; import org.apache.hadoop.fs.FileAlreadyExistsException; import org.apache.hadoop.fs.FileEncryptionInfo; @@ -2173,6 +2174,7 @@ public class FSDirectory implements Closeable { xattr.getValue()); ezManager.unprotectedAddEncryptionZone(inode.getId(), PBHelper.convert(ezProto.getSuite()), + PBHelper.convert(ezProto.getCryptoProtocolVersion()), ezProto.getKeyName()); } catch (InvalidProtocolBufferException e) { NameNode.LOG.warn("Error parsing protocol buffer of " + @@ -2766,11 +2768,12 @@ public class FSDirectory implements Closeable { } } - XAttr createEncryptionZone(String src, CipherSuite suite, String keyName) + XAttr createEncryptionZone(String src, CipherSuite suite, + CryptoProtocolVersion version, String keyName) throws IOException { writeLock(); try { - return ezManager.createEncryptionZone(src, suite, keyName); + return ezManager.createEncryptionZone(src, suite, version, keyName); } finally { writeUnlock(); } @@ -2854,8 +2857,9 @@ public class FSDirectory implements Closeable { } } - CipherSuite suite = encryptionZone.getSuite(); - String keyName = encryptionZone.getKeyName(); + final CryptoProtocolVersion version = encryptionZone.getVersion(); + final CipherSuite suite = encryptionZone.getSuite(); + final String keyName = encryptionZone.getKeyName(); XAttr fileXAttr = unprotectedGetXAttrByName(inode, snapshotId, CRYPTO_XATTR_FILE_ENCRYPTION_INFO); @@ -2871,7 +2875,7 @@ public class FSDirectory implements Closeable { HdfsProtos.PerFileEncryptionInfoProto fileProto = HdfsProtos.PerFileEncryptionInfoProto.parseFrom( fileXAttr.getValue()); - return PBHelper.convert(fileProto, suite, keyName); + return PBHelper.convert(fileProto, suite, version, keyName); } catch (InvalidProtocolBufferException e) { throw new IOException("Could not parse file encryption info for " + "inode " + inode, e); @@ -2914,6 +2918,7 @@ public class FSDirectory implements Closeable { HdfsProtos.ZoneEncryptionInfoProto.parseFrom(xattr.getValue()); ezManager.addEncryptionZone(inode.getId(), PBHelper.convert(ezProto.getSuite()), + PBHelper.convert(ezProto.getCryptoProtocolVersion()), ezProto.getKeyName()); } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSNamesystem.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSNamesystem.java index 41484746728..6916c0d484a 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSNamesystem.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSNamesystem.java @@ -136,6 +136,7 @@ import org.apache.hadoop.HadoopIllegalArgumentException; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.crypto.CipherSuite; +import org.apache.hadoop.crypto.CryptoProtocolVersion; import org.apache.hadoop.crypto.key.KeyProvider; import org.apache.hadoop.crypto.CryptoCodec; import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension; @@ -170,9 +171,8 @@ import org.apache.hadoop.hdfs.DFSConfigKeys; import org.apache.hadoop.hdfs.DFSUtil; import org.apache.hadoop.hdfs.HAUtil; import org.apache.hadoop.hdfs.HdfsConfiguration; -import org.apache.hadoop.hdfs.StorageType; +import org.apache.hadoop.hdfs.UnknownCryptoProtocolVersionException; import org.apache.hadoop.hdfs.XAttrHelper; -import org.apache.hadoop.hdfs.UnknownCipherSuiteException; import org.apache.hadoop.hdfs.protocol.AclException; import org.apache.hadoop.hdfs.protocol.AlreadyBeingCreatedException; import org.apache.hadoop.hdfs.protocol.Block; @@ -2358,46 +2358,41 @@ public class FSNamesystem implements Namesystem, FSClusterStats, /** * If the file is within an encryption zone, select the appropriate - * CipherSuite from the list provided by the client. Since the client may - * be newer, need to handle unknown CipherSuites. + * CryptoProtocolVersion from the list provided by the client. Since the + * client may be newer, we need to handle unknown versions. * - * @param srcIIP path of the file - * @param cipherSuites client-provided list of supported CipherSuites, - * in desired order. - * @return chosen CipherSuite, or null if file is not in an EncryptionZone + * @param zone EncryptionZone of the file + * @param supportedVersions List of supported protocol versions + * @return chosen protocol version * @throws IOException */ - private CipherSuite chooseCipherSuite(INodesInPath srcIIP, List - cipherSuites) - throws UnknownCipherSuiteException, UnresolvedLinkException, + private CryptoProtocolVersion chooseProtocolVersion(EncryptionZone zone, + CryptoProtocolVersion[] supportedVersions) + throws UnknownCryptoProtocolVersionException, UnresolvedLinkException, SnapshotAccessControlException { - // Not in an EZ - if (!dir.isInAnEZ(srcIIP)) { - return null; - } - CipherSuite chosen = null; - for (CipherSuite c : cipherSuites) { - if (c.equals(CipherSuite.UNKNOWN)) { + Preconditions.checkNotNull(zone); + Preconditions.checkNotNull(supportedVersions); + // Right now, we only support a single protocol version, + // so simply look for it in the list of provided options + final CryptoProtocolVersion required = zone.getVersion(); + + for (CryptoProtocolVersion c : supportedVersions) { + if (c.equals(CryptoProtocolVersion.UNKNOWN)) { if (LOG.isDebugEnabled()) { - LOG.debug("Ignoring unknown CipherSuite provided by client: " - + c.getUnknownValue()); + LOG.debug("Ignoring unknown CryptoProtocolVersion provided by " + + "client: " + c.getUnknownValue()); } continue; } - for (CipherSuite supported : CipherSuite.values()) { - if (supported.equals(c)) { - chosen = c; - break; - } + if (c.equals(required)) { + return c; } } - if (chosen == null) { - throw new UnknownCipherSuiteException( - "No cipher suites provided by the client are supported." - + " Client provided: " + Arrays.toString(cipherSuites.toArray()) - + " NameNode supports: " + Arrays.toString(CipherSuite.values())); - } - return chosen; + throw new UnknownCryptoProtocolVersionException( + "No crypto protocol versions provided by the client are supported." + + " Client provided: " + Arrays.toString(supportedVersions) + + " NameNode supports: " + Arrays.toString(CryptoProtocolVersion + .values())); } /** @@ -2433,7 +2428,7 @@ public class FSNamesystem implements Namesystem, FSClusterStats, HdfsFileStatus startFile(String src, PermissionStatus permissions, String holder, String clientMachine, EnumSet flag, boolean createParent, short replication, long blockSize, - List cipherSuites) + CryptoProtocolVersion[] supportedVersions) throws AccessControlException, SafeModeException, FileAlreadyExistsException, UnresolvedLinkException, FileNotFoundException, ParentNotDirectoryException, IOException { @@ -2446,7 +2441,7 @@ public class FSNamesystem implements Namesystem, FSClusterStats, try { status = startFileInt(src, permissions, holder, clientMachine, flag, - createParent, replication, blockSize, cipherSuites, + createParent, replication, blockSize, supportedVersions, cacheEntry != null); } catch (AccessControlException e) { logAuditEvent(false, "create", src); @@ -2460,7 +2455,8 @@ public class FSNamesystem implements Namesystem, FSClusterStats, private HdfsFileStatus startFileInt(final String srcArg, PermissionStatus permissions, String holder, String clientMachine, EnumSet flag, boolean createParent, short replication, - long blockSize, List cipherSuites, boolean logRetryCache) + long blockSize, CryptoProtocolVersion[] supportedVersions, + boolean logRetryCache) throws AccessControlException, SafeModeException, FileAlreadyExistsException, UnresolvedLinkException, FileNotFoundException, ParentNotDirectoryException, IOException { @@ -2474,9 +2470,9 @@ public class FSNamesystem implements Namesystem, FSClusterStats, + ", replication=" + replication + ", createFlag=" + flag.toString() + ", blockSize=" + blockSize); - builder.append(", cipherSuites="); - if (cipherSuites != null) { - builder.append(Arrays.toString(cipherSuites.toArray())); + builder.append(", supportedVersions="); + if (supportedVersions != null) { + builder.append(Arrays.toString(supportedVersions)); } else { builder.append("null"); } @@ -2514,6 +2510,7 @@ public class FSNamesystem implements Namesystem, FSClusterStats, * special RetryStartFileException to ask the DFSClient to try the create * again later. */ + CryptoProtocolVersion protocolVersion = null; CipherSuite suite = null; String ezKeyName = null; readLock(); @@ -2522,13 +2519,16 @@ public class FSNamesystem implements Namesystem, FSClusterStats, INodesInPath iip = dir.getINodesInPath4Write(src); // Nothing to do if the path is not within an EZ if (dir.isInAnEZ(iip)) { - suite = chooseCipherSuite(iip, cipherSuites); - if (suite != null) { - Preconditions.checkArgument(!suite.equals(CipherSuite.UNKNOWN), - "Chose an UNKNOWN CipherSuite!"); - } + EncryptionZone zone = dir.getEZForPath(iip); + protocolVersion = chooseProtocolVersion(zone, supportedVersions); + suite = zone.getSuite(); ezKeyName = dir.getKeyName(iip); - Preconditions.checkState(ezKeyName != null); + + Preconditions.checkNotNull(protocolVersion); + Preconditions.checkNotNull(suite); + Preconditions.checkArgument(!suite.equals(CipherSuite.UNKNOWN), + "Chose an UNKNOWN CipherSuite!"); + Preconditions.checkNotNull(ezKeyName); } } finally { readUnlock(); @@ -2554,7 +2554,7 @@ public class FSNamesystem implements Namesystem, FSClusterStats, src = resolvePath(src, pathComponents); toRemoveBlocks = startFileInternal(pc, src, permissions, holder, clientMachine, create, overwrite, createParent, replication, - blockSize, suite, edek, logRetryCache); + blockSize, suite, protocolVersion, edek, logRetryCache); stat = dir.getFileInfo(src, false, FSDirectory.isReservedRawName(srcArg), false); } catch (StandbyException se) { @@ -2590,7 +2590,8 @@ public class FSNamesystem implements Namesystem, FSClusterStats, String src, PermissionStatus permissions, String holder, String clientMachine, boolean create, boolean overwrite, boolean createParent, short replication, long blockSize, - CipherSuite suite, EncryptedKeyVersion edek, boolean logRetryEntry) + CipherSuite suite, CryptoProtocolVersion version, + EncryptedKeyVersion edek, boolean logRetryEntry) throws FileAlreadyExistsException, AccessControlException, UnresolvedLinkException, FileNotFoundException, ParentNotDirectoryException, RetryStartFileException, IOException { @@ -2615,7 +2616,7 @@ public class FSNamesystem implements Namesystem, FSClusterStats, if (!ezKeyName.equals(edek.getEncryptionKeyName())) { throw new RetryStartFileException(); } - feInfo = new FileEncryptionInfo(suite, + feInfo = new FileEncryptionInfo(suite, version, edek.getEncryptedKeyVersion().getMaterial(), edek.getEncryptedKeyIv(), ezKeyName, edek.getEncryptionKeyVersionName()); @@ -8683,7 +8684,11 @@ public class FSNamesystem implements Namesystem, FSClusterStats, src = resolvePath(src, pathComponents); final CipherSuite suite = CipherSuite.convert(cipher); - final XAttr ezXAttr = dir.createEncryptionZone(src, suite, keyName); + // For now this is hardcoded, as we only support one method. + final CryptoProtocolVersion version = + CryptoProtocolVersion.ENCRYPTION_ZONES; + final XAttr ezXAttr = dir.createEncryptionZone(src, suite, + version, keyName); List xAttrs = Lists.newArrayListWithCapacity(1); xAttrs.add(ezXAttr); getEditLog().logSetXAttrs(src, xAttrs, logRetryCache); diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NameNodeRpcServer.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NameNodeRpcServer.java index b6be8778744..e50cc45ad58 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NameNodeRpcServer.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NameNodeRpcServer.java @@ -38,7 +38,7 @@ import com.google.common.collect.Lists; import org.apache.commons.logging.Log; import org.apache.hadoop.HadoopIllegalArgumentException; import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.crypto.CipherSuite; +import org.apache.hadoop.crypto.CryptoProtocolVersion; import org.apache.hadoop.fs.BatchedRemoteIterator.BatchedEntries; import org.apache.hadoop.fs.CacheFlag; import org.apache.hadoop.fs.CommonConfigurationKeys; @@ -537,7 +537,7 @@ class NameNodeRpcServer implements NamenodeProtocols { public HdfsFileStatus create(String src, FsPermission masked, String clientName, EnumSetWritable flag, boolean createParent, short replication, long blockSize, - List cipherSuites) + CryptoProtocolVersion[] supportedVersions) throws IOException { String clientMachine = getClientMachine(); if (stateChangeLog.isDebugEnabled()) { @@ -551,7 +551,7 @@ class NameNodeRpcServer implements NamenodeProtocols { HdfsFileStatus fileStatus = namesystem.startFile(src, new PermissionStatus( getRemoteUser().getShortUserName(), null, masked), clientName, clientMachine, flag.get(), createParent, replication, - blockSize, cipherSuites); + blockSize, supportedVersions); metrics.incrFilesCreated(); metrics.incrCreateFileOps(); return fileStatus; diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/proto/ClientNamenodeProtocol.proto b/hadoop-hdfs-project/hadoop-hdfs/src/main/proto/ClientNamenodeProtocol.proto index e09f142a688..14e8c68fd9c 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/proto/ClientNamenodeProtocol.proto +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/proto/ClientNamenodeProtocol.proto @@ -75,7 +75,7 @@ message CreateRequestProto { required bool createParent = 5; required uint32 replication = 6; // Short: Only 16 bits used required uint64 blockSize = 7; - repeated CipherSuite cipherSuites = 8; + repeated CryptoProtocolVersionProto cryptoProtocolVersion = 8; } message CreateResponseProto { diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/proto/encryption.proto b/hadoop-hdfs-project/hadoop-hdfs/src/main/proto/encryption.proto index c4b7009105c..bb291eaff23 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/proto/encryption.proto +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/proto/encryption.proto @@ -48,8 +48,9 @@ message ListEncryptionZonesRequestProto { message EncryptionZoneProto { required int64 id = 1; required string path = 2; - required CipherSuite suite = 3; - required string keyName = 4; + required CipherSuiteProto suite = 3; + required CryptoProtocolVersionProto cryptoProtocolVersion = 4; + required string keyName = 5; } message ListEncryptionZonesResponseProto { diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/proto/hdfs.proto b/hadoop-hdfs-project/hadoop-hdfs/src/main/proto/hdfs.proto index c57e308bf2b..38e649edbc2 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/proto/hdfs.proto +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/proto/hdfs.proto @@ -218,20 +218,29 @@ message DataEncryptionKeyProto { /** * Cipher suite. */ -enum CipherSuite { +enum CipherSuiteProto { UNKNOWN = 1; AES_CTR_NOPADDING = 2; } +/** + * Crypto protocol version used to access encrypted files. + */ +enum CryptoProtocolVersionProto { + UNKNOWN_PROTOCOL_VERSION = 1; + ENCRYPTION_ZONES = 2; +} + /** * Encryption information for a file. */ message FileEncryptionInfoProto { - required CipherSuite suite = 1; - required bytes key = 2; - required bytes iv = 3; - required string keyName = 4; - required string ezKeyVersionName = 5; + required CipherSuiteProto suite = 1; + required CryptoProtocolVersionProto cryptoProtocolVersion = 2; + required bytes key = 3; + required bytes iv = 4; + required string keyName = 5; + required string ezKeyVersionName = 6; } /** @@ -249,8 +258,9 @@ message PerFileEncryptionInfoProto { * zone */ message ZoneEncryptionInfoProto { - required CipherSuite suite = 1; - required string keyName = 2; + required CipherSuiteProto suite = 1; + required CryptoProtocolVersionProto cryptoProtocolVersion = 2; + required string keyName = 3; } /** diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSClientRetries.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSClientRetries.java index de392e31073..382ad4809ef 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSClientRetries.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSClientRetries.java @@ -53,6 +53,7 @@ import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.impl.Log4JLogger; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.crypto.CipherSuite; +import org.apache.hadoop.crypto.CryptoProtocolVersion; import org.apache.hadoop.fs.CommonConfigurationKeys; import org.apache.hadoop.fs.CreateFlag; import org.apache.hadoop.fs.FSDataInputStream; @@ -264,7 +265,7 @@ public class TestDFSClientRetries { .when(mockNN) .create(anyString(), (FsPermission) anyObject(), anyString(), (EnumSetWritable) anyObject(), anyBoolean(), - anyShort(), anyLong(), (List) anyList()); + anyShort(), anyLong(), (CryptoProtocolVersion[]) anyObject()); final DFSClient client = new DFSClient(null, mockNN, conf, null); OutputStream os = client.create("testfile", true); diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestEncryptionZones.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestEncryptionZones.java index 05b39c3342c..df1864c7e76 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestEncryptionZones.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestEncryptionZones.java @@ -40,10 +40,13 @@ import java.util.concurrent.Future; import com.google.common.collect.Lists; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.crypto.CipherSuite; +import org.apache.hadoop.crypto.CryptoProtocolVersion; import org.apache.hadoop.crypto.key.JavaKeyStoreProvider; import org.apache.hadoop.crypto.key.KeyProvider; import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension; import org.apache.hadoop.crypto.key.KeyProviderFactory; +import org.apache.hadoop.fs.CommonConfigurationKeysPublic; +import org.apache.hadoop.fs.CreateFlag; import org.apache.hadoop.fs.FSDataOutputStream; import org.apache.hadoop.fs.FSTestWrapper; import org.apache.hadoop.fs.FileContext; @@ -57,7 +60,9 @@ import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.RemoteIterator; import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.hdfs.client.HdfsAdmin; +import org.apache.hadoop.hdfs.protocol.ClientProtocol; import org.apache.hadoop.hdfs.protocol.EncryptionZone; +import org.apache.hadoop.hdfs.protocol.HdfsFileStatus; import org.apache.hadoop.hdfs.protocol.LocatedBlocks; import org.apache.hadoop.hdfs.protocol.HdfsConstants.SafeModeAction; import org.apache.hadoop.hdfs.server.namenode.EncryptionFaultInjector; @@ -68,6 +73,7 @@ import org.apache.hadoop.hdfs.tools.DFSck; import org.apache.hadoop.hdfs.tools.offlineImageViewer.PBImageXmlWriter; import org.apache.hadoop.hdfs.web.WebHdfsFileSystem; import org.apache.hadoop.hdfs.web.WebHdfsTestUtil; +import org.apache.hadoop.io.EnumSetWritable; import org.apache.hadoop.security.AccessControlException; import org.apache.hadoop.security.Credentials; import org.apache.hadoop.security.UserGroupInformation; @@ -83,6 +89,11 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; + +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyShort; import static org.mockito.Mockito.withSettings; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyString; @@ -628,7 +639,7 @@ public class TestEncryptionZones { } @Test(timeout = 60000) - public void testCipherSuiteNegotiation() throws Exception { + public void testVersionAndSuiteNegotiation() throws Exception { final HdfsAdmin dfsAdmin = new HdfsAdmin(FileSystem.getDefaultUri(conf), conf); final Path zone = new Path("/zone"); @@ -637,43 +648,44 @@ public class TestEncryptionZones { // Create a file in an EZ, which should succeed DFSTestUtil .createFile(fs, new Path(zone, "success1"), 0, (short) 1, 0xFEED); - // Pass no cipherSuites, fail - fs.getClient().cipherSuites = Lists.newArrayListWithCapacity(0); + // Pass no supported versions, fail + DFSOutputStream.SUPPORTED_CRYPTO_VERSIONS = new CryptoProtocolVersion[] {}; try { DFSTestUtil.createFile(fs, new Path(zone, "fail"), 0, (short) 1, 0xFEED); - fail("Created a file without specifying a CipherSuite!"); - } catch (UnknownCipherSuiteException e) { - assertExceptionContains("No cipher suites", e); + fail("Created a file without specifying a crypto protocol version"); + } catch (UnknownCryptoProtocolVersionException e) { + assertExceptionContains("No crypto protocol versions", e); } - // Pass some unknown cipherSuites, fail - fs.getClient().cipherSuites = Lists.newArrayListWithCapacity(3); - fs.getClient().cipherSuites.add(CipherSuite.UNKNOWN); - fs.getClient().cipherSuites.add(CipherSuite.UNKNOWN); - fs.getClient().cipherSuites.add(CipherSuite.UNKNOWN); + // Pass some unknown versions, fail + DFSOutputStream.SUPPORTED_CRYPTO_VERSIONS = new CryptoProtocolVersion[] + { CryptoProtocolVersion.UNKNOWN, CryptoProtocolVersion.UNKNOWN }; try { DFSTestUtil.createFile(fs, new Path(zone, "fail"), 0, (short) 1, 0xFEED); - fail("Created a file without specifying a CipherSuite!"); - } catch (UnknownCipherSuiteException e) { - assertExceptionContains("No cipher suites", e); + fail("Created a file without specifying a known crypto protocol version"); + } catch (UnknownCryptoProtocolVersionException e) { + assertExceptionContains("No crypto protocol versions", e); } // Pass some unknown and a good cipherSuites, success - fs.getClient().cipherSuites = Lists.newArrayListWithCapacity(3); - fs.getClient().cipherSuites.add(CipherSuite.AES_CTR_NOPADDING); - fs.getClient().cipherSuites.add(CipherSuite.UNKNOWN); - fs.getClient().cipherSuites.add(CipherSuite.UNKNOWN); + DFSOutputStream.SUPPORTED_CRYPTO_VERSIONS = + new CryptoProtocolVersion[] { + CryptoProtocolVersion.UNKNOWN, + CryptoProtocolVersion.UNKNOWN, + CryptoProtocolVersion.ENCRYPTION_ZONES }; DFSTestUtil .createFile(fs, new Path(zone, "success2"), 0, (short) 1, 0xFEED); - fs.getClient().cipherSuites = Lists.newArrayListWithCapacity(3); - fs.getClient().cipherSuites.add(CipherSuite.UNKNOWN); - fs.getClient().cipherSuites.add(CipherSuite.UNKNOWN); - fs.getClient().cipherSuites.add(CipherSuite.AES_CTR_NOPADDING); + DFSOutputStream.SUPPORTED_CRYPTO_VERSIONS = + new CryptoProtocolVersion[] { + CryptoProtocolVersion.ENCRYPTION_ZONES, + CryptoProtocolVersion.UNKNOWN, + CryptoProtocolVersion.UNKNOWN} ; DFSTestUtil .createFile(fs, new Path(zone, "success3"), 4096, (short) 1, 0xFEED); // Check KeyProvider state // Flushing the KP on the NN, since it caches, and init a test one cluster.getNamesystem().getProvider().flush(); KeyProvider provider = KeyProviderFactory - .get(new URI(conf.get(DFSConfigKeys.DFS_ENCRYPTION_KEY_PROVIDER_URI)), conf); + .get(new URI(conf.get(DFSConfigKeys.DFS_ENCRYPTION_KEY_PROVIDER_URI)), + conf); List keys = provider.getKeys(); assertEquals("Expected NN to have created one key per zone", 1, keys.size()); @@ -691,6 +703,66 @@ public class TestEncryptionZones { "/success" + i)); assertEquals(feInfo.getCipherSuite(), CipherSuite.AES_CTR_NOPADDING); } + + DFSClient old = fs.dfs; + try { + testCipherSuiteNegotiation(fs, conf); + } finally { + fs.dfs = old; + } + } + + @SuppressWarnings("unchecked") + private static void mockCreate(ClientProtocol mcp, + CipherSuite suite, CryptoProtocolVersion version) throws Exception { + Mockito.doReturn( + new HdfsFileStatus(0, false, 1, 1024, 0, 0, new FsPermission( + (short) 777), "owner", "group", new byte[0], new byte[0], + 1010, 0, new FileEncryptionInfo(suite, + version, new byte[suite.getAlgorithmBlockSize()], + new byte[suite.getAlgorithmBlockSize()], + "fakeKey", "fakeVersion"), + (byte) 0)) + .when(mcp) + .create(anyString(), (FsPermission) anyObject(), anyString(), + (EnumSetWritable) anyObject(), anyBoolean(), + anyShort(), anyLong(), (CryptoProtocolVersion[]) anyObject()); + } + + // This test only uses mocks. Called from the end of an existing test to + // avoid an extra mini cluster. + private static void testCipherSuiteNegotiation(DistributedFileSystem fs, + Configuration conf) throws Exception { + // Set up mock ClientProtocol to test client-side CipherSuite negotiation + final ClientProtocol mcp = Mockito.mock(ClientProtocol.class); + + // Try with an empty conf + final Configuration noCodecConf = new Configuration(conf); + final CipherSuite suite = CipherSuite.AES_CTR_NOPADDING; + final String confKey = CommonConfigurationKeysPublic + .HADOOP_SECURITY_CRYPTO_CODEC_CLASSES_KEY_PREFIX + suite + .getConfigSuffix(); + noCodecConf.set(confKey, ""); + fs.dfs = new DFSClient(null, mcp, noCodecConf, null); + mockCreate(mcp, suite, CryptoProtocolVersion.ENCRYPTION_ZONES); + try { + fs.create(new Path("/mock")); + fail("Created with no configured codecs!"); + } catch (UnknownCipherSuiteException e) { + assertExceptionContains("No configuration found for the cipher", e); + } + + // Try create with an UNKNOWN CipherSuite + fs.dfs = new DFSClient(null, mcp, conf, null); + CipherSuite unknown = CipherSuite.UNKNOWN; + unknown.setUnknownValue(989); + mockCreate(mcp, unknown, CryptoProtocolVersion.ENCRYPTION_ZONES); + try { + fs.create(new Path("/mock")); + fail("Created with unknown cipher!"); + } catch (IOException e) { + assertExceptionContains("unknown CipherSuite with ID 989", e); + } } @Test(timeout = 120000) diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestLease.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestLease.java index 6119b6e09b7..5d93db4e47a 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestLease.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestLease.java @@ -38,6 +38,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.crypto.CipherSuite; +import org.apache.hadoop.crypto.CryptoProtocolVersion; import org.apache.hadoop.fs.CreateFlag; import org.apache.hadoop.fs.FSDataOutputStream; import org.apache.hadoop.fs.FileSystem; @@ -352,7 +353,7 @@ public class TestLease { .when(mcp) .create(anyString(), (FsPermission) anyObject(), anyString(), (EnumSetWritable) anyObject(), anyBoolean(), - anyShort(), anyLong(), (List) anyList()); + anyShort(), anyLong(), (CryptoProtocolVersion[]) anyObject()); final Configuration conf = new Configuration(); final DFSClient c1 = createDFSClientAs(ugi[0], conf); diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestRetryCacheWithHA.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestRetryCacheWithHA.java index 8f7d11ae7e6..bf889f3c81b 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestRetryCacheWithHA.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestRetryCacheWithHA.java @@ -39,6 +39,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.crypto.CryptoProtocolVersion; import org.apache.hadoop.fs.CacheFlag; import org.apache.hadoop.fs.CreateFlag; import org.apache.hadoop.fs.FSDataOutputStream; @@ -395,7 +396,8 @@ public class TestRetryCacheWithHA { this.status = client.getNamenode().create(fileName, FsPermission.getFileDefault(), client.getClientName(), new EnumSetWritable(createFlag), false, DataNodes, - BlockSize, null); + BlockSize, + new CryptoProtocolVersion[] {CryptoProtocolVersion.ENCRYPTION_ZONES}); } @Override