| /* |
| * 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.hdds.security.x509; |
| |
| import com.google.common.base.Preconditions; |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.ozone.OzoneConfigKeys; |
| import org.apache.ratis.thirdparty.io.netty.handler.ssl.SslProvider; |
| import org.bouncycastle.jce.provider.BouncyCastleProvider; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.security.Provider; |
| import java.security.Security; |
| import java.time.Duration; |
| import java.util.concurrent.TimeUnit; |
| |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_DEFAULT_KEY_ALGORITHM; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_DEFAULT_KEY_LEN; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_DEFAULT_SECURITY_PROVIDER; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_BLOCK_TOKEN_ENABLED; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_BLOCK_TOKEN_ENABLED_DEFAULT; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_GRPC_TLS_ENABLED; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_GRPC_TLS_ENABLED_DEFAULT; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_GRPC_TLS_PROVIDER; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_GRPC_TLS_PROVIDER_DEFAULT; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_GRPC_TLS_TEST_CERT; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_GRPC_TLS_TEST_CERT_DEFAULT; |
| |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_KEY_ALGORITHM; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_KEY_DIR_NAME; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_KEY_DIR_NAME_DEFAULT; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_KEY_LEN; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_METADATA_DIR_NAME; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_PRIVATE_KEY_FILE_NAME; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_PRIVATE_KEY_FILE_NAME_DEFAULT; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_PUBLIC_KEY_FILE_NAME; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_PUBLIC_KEY_FILE_NAME_DEFAULT; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_SECURITY_PROVIDER; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_DEFAULT_DURATION_DEFAULT; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_DEFAULT_DURATION; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_DIR_NAME; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_DIR_NAME_DEFAULT; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_FILE_NAME; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_FILE_NAME_DEFAULT; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_MAX_DURATION; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_MAX_DURATION_DEFAULT; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_SIGNATURE_ALGO; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_SIGNATURE_ALGO_DEFAULT; |
| import static org.apache.hadoop.hdds.HddsConfigKeys.OZONE_METADATA_DIRS; |
| import static org.apache.hadoop.hdds.scm.ScmConfigKeys.HDDS_DATANODE_DIR_KEY; |
| import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_SECURITY_ENABLED_DEFAULT; |
| import static org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_SECURITY_ENABLED_KEY; |
| |
| /** |
| * A class that deals with all Security related configs in HDDS. |
| * <p> |
| * This class allows security configs to be read and used consistently across |
| * all of security related code base. |
| */ |
| public class SecurityConfig { |
| private static final Logger LOG = |
| LoggerFactory.getLogger(SecurityConfig.class); |
| private static volatile Provider provider; |
| private final Configuration configuration; |
| private final int size; |
| private final String keyAlgo; |
| private final String providerString; |
| private final String metadatDir; |
| private final String keyDir; |
| private final String privateKeyFileName; |
| private final String publicKeyFileName; |
| private final Duration certDuration; |
| private final String x509SignatureAlgo; |
| private final boolean blockTokenEnabled; |
| private final String certificateDir; |
| private final String certificateFileName; |
| private final boolean grpcTlsEnabled; |
| private boolean grpcTlsUseTestCert; |
| private final Duration defaultCertDuration; |
| private final boolean isSecurityEnabled; |
| |
| /** |
| * Constructs a SecurityConfig. |
| * |
| * @param configuration - HDDS Configuration |
| */ |
| public SecurityConfig(Configuration configuration) { |
| Preconditions.checkNotNull(configuration, "Configuration cannot be null"); |
| this.configuration = configuration; |
| this.size = this.configuration.getInt(HDDS_KEY_LEN, HDDS_DEFAULT_KEY_LEN); |
| this.keyAlgo = this.configuration.get(HDDS_KEY_ALGORITHM, |
| HDDS_DEFAULT_KEY_ALGORITHM); |
| this.providerString = this.configuration.get(HDDS_SECURITY_PROVIDER, |
| HDDS_DEFAULT_SECURITY_PROVIDER); |
| |
| // Please Note: To make it easy for our customers we will attempt to read |
| // HDDS metadata dir and if that is not set, we will use Ozone directory. |
| // TODO: We might want to fix this later. |
| this.metadatDir = this.configuration.get(HDDS_METADATA_DIR_NAME, |
| configuration.get(OZONE_METADATA_DIRS, |
| configuration.get(HDDS_DATANODE_DIR_KEY))); |
| this.keyDir = this.configuration.get(HDDS_KEY_DIR_NAME, |
| HDDS_KEY_DIR_NAME_DEFAULT); |
| this.privateKeyFileName = this.configuration.get(HDDS_PRIVATE_KEY_FILE_NAME, |
| HDDS_PRIVATE_KEY_FILE_NAME_DEFAULT); |
| this.publicKeyFileName = this.configuration.get(HDDS_PUBLIC_KEY_FILE_NAME, |
| HDDS_PUBLIC_KEY_FILE_NAME_DEFAULT); |
| |
| String durationString = this.configuration.get(HDDS_X509_MAX_DURATION, |
| HDDS_X509_MAX_DURATION_DEFAULT); |
| this.certDuration = Duration.parse(durationString); |
| this.x509SignatureAlgo = this.configuration.get(HDDS_X509_SIGNATURE_ALGO, |
| HDDS_X509_SIGNATURE_ALGO_DEFAULT); |
| this.certificateDir = this.configuration.get(HDDS_X509_DIR_NAME, |
| HDDS_X509_DIR_NAME_DEFAULT); |
| this.certificateFileName = this.configuration.get(HDDS_X509_FILE_NAME, |
| HDDS_X509_FILE_NAME_DEFAULT); |
| |
| this.blockTokenEnabled = this.configuration.getBoolean( |
| HDDS_BLOCK_TOKEN_ENABLED, |
| HDDS_BLOCK_TOKEN_ENABLED_DEFAULT); |
| |
| this.grpcTlsEnabled = this.configuration.getBoolean(HDDS_GRPC_TLS_ENABLED, |
| HDDS_GRPC_TLS_ENABLED_DEFAULT); |
| |
| if (grpcTlsEnabled) { |
| this.grpcTlsUseTestCert = this.configuration.getBoolean( |
| HDDS_GRPC_TLS_TEST_CERT, HDDS_GRPC_TLS_TEST_CERT_DEFAULT); |
| } |
| |
| this.isSecurityEnabled = this.configuration.getBoolean( |
| OZONE_SECURITY_ENABLED_KEY, |
| OZONE_SECURITY_ENABLED_DEFAULT); |
| |
| String certDurationString = |
| this.configuration.get(HDDS_X509_DEFAULT_DURATION, |
| HDDS_X509_DEFAULT_DURATION_DEFAULT); |
| defaultCertDuration = Duration.parse(certDurationString); |
| |
| |
| // First Startup -- if the provider is null, check for the provider. |
| if (SecurityConfig.provider == null) { |
| synchronized (SecurityConfig.class) { |
| provider = Security.getProvider(this.providerString); |
| if (SecurityConfig.provider == null) { |
| // Provider not found, let us try to Dynamically initialize the |
| // provider. |
| provider = initSecurityProvider(this.providerString); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Returns true if security is enabled for OzoneCluster. This is determined |
| * by value of OZONE_SECURITY_ENABLED_KEY. |
| * |
| * @return true if security is enabled for OzoneCluster. |
| */ |
| public boolean isSecurityEnabled() { |
| return isSecurityEnabled; |
| } |
| |
| /** |
| * Returns the Default Certificate Duration. |
| * |
| * @return Duration for the default certificate issue. |
| */ |
| public Duration getDefaultCertDuration() { |
| return defaultCertDuration; |
| } |
| |
| /** |
| * Returns the Standard Certificate file name. |
| * |
| * @return String - Name of the Certificate File. |
| */ |
| public String getCertificateFileName() { |
| return certificateFileName; |
| } |
| |
| /** |
| * Returns the public key file name, This is used for storing the public keys |
| * on disk. |
| * |
| * @return String, File name used for public keys. |
| */ |
| public String getPublicKeyFileName() { |
| return publicKeyFileName; |
| } |
| |
| /** |
| * Returns the private key file name.This is used for storing the private keys |
| * on disk. |
| * |
| * @return String, File name used for private keys. |
| */ |
| public String getPrivateKeyFileName() { |
| return privateKeyFileName; |
| } |
| |
| /** |
| * Returns the File path to where keys are stored with an additional component |
| * name inserted in between. |
| * |
| * @param component - Component Name - String. |
| * @return Path Key location. |
| */ |
| public Path getKeyLocation(String component) { |
| Preconditions.checkNotNull(this.metadatDir, "Metadata directory can't be" |
| + " null. Please check configs."); |
| return Paths.get(metadatDir, component, keyDir); |
| } |
| |
| /** |
| * Returns the File path to where certificates are stored with an addition |
| * component |
| * name inserted in between. |
| * |
| * @param component - Component Name - String. |
| * @return Path location. |
| */ |
| public Path getCertificateLocation(String component) { |
| Preconditions.checkNotNull(this.metadatDir, "Metadata directory can't be" |
| + " null. Please check configs."); |
| return Paths.get(metadatDir, component, certificateDir); |
| } |
| |
| /** |
| * Gets the Key Size, The default key size is 2048, since the default |
| * algorithm used is RSA. User can change this by setting the "hdds.key.len" |
| * in configuration. |
| * |
| * @return key size. |
| */ |
| public int getSize() { |
| return size; |
| } |
| |
| /** |
| * Returns the Provider name. SCM defaults to using Bouncy Castle and will |
| * return "BC". |
| * |
| * @return String Provider name. |
| */ |
| public String getProvider() { |
| return providerString; |
| } |
| |
| /** |
| * Returns the Key generation Algorithm used. User can change this by setting |
| * the "hdds.key.algo" in configuration. |
| * |
| * @return String Algo. |
| */ |
| public String getKeyAlgo() { |
| return keyAlgo; |
| } |
| |
| /** |
| * Returns the X.509 Signature Algorithm used. This can be changed by setting |
| * "hdds.x509.signature.algorithm" to the new name. The default algorithm is |
| * SHA256withRSA. |
| * |
| * @return String |
| */ |
| public String getSignatureAlgo() { |
| return x509SignatureAlgo; |
| } |
| |
| /** |
| * Returns the Configuration used for initializing this SecurityConfig. |
| * |
| * @return Configuration |
| */ |
| public Configuration getConfiguration() { |
| return configuration; |
| } |
| |
| /** |
| * Returns the maximum length a certificate can be valid in SCM. The default |
| * value is 5 years. This can be changed by setting "hdds.x509.max.duration" |
| * in configuration. The formats accepted are based on the ISO-8601 duration |
| * format PnDTnHnMn.nS |
| * <p> |
| * Default value is 5 years and written as P1865D. |
| * |
| * @return Duration. |
| */ |
| public Duration getMaxCertificateDuration() { |
| return this.certDuration; |
| } |
| |
| public boolean isBlockTokenEnabled() { |
| return this.blockTokenEnabled; |
| } |
| |
| /** |
| * Returns true if TLS is enabled for gRPC services. |
| * @return true if TLS is enabled for gRPC services. |
| */ |
| public boolean isGrpcTlsEnabled() { |
| return this.grpcTlsEnabled; |
| } |
| |
| /** |
| * Get the gRPC TLS provider. |
| * @return the gRPC TLS Provider. |
| */ |
| public SslProvider getGrpcSslProvider() { |
| return SslProvider.valueOf(configuration.get(HDDS_GRPC_TLS_PROVIDER, |
| HDDS_GRPC_TLS_PROVIDER_DEFAULT)); |
| } |
| |
| /** |
| * Return true if using test certificates with authority as localhost. |
| * This should be used only for unit test where certificates are generated |
| * by openssl with localhost as DN and should never use for production as it |
| * will bypass the hostname/ip matching verification. |
| * @return true if using test certificates. |
| */ |
| public boolean useTestCert() { |
| return grpcTlsUseTestCert; |
| } |
| |
| /** |
| * Adds a security provider dynamically if it is not loaded already. |
| * |
| * @param providerName - name of the provider. |
| */ |
| private Provider initSecurityProvider(String providerName) { |
| switch (providerName) { |
| case "BC": |
| Security.addProvider(new BouncyCastleProvider()); |
| return Security.getProvider(providerName); |
| default: |
| LOG.error("Security Provider:{} is unknown", provider); |
| throw new SecurityException("Unknown security provider:" + provider); |
| } |
| } |
| |
| /** |
| * Returns max date for which S3 tokens will be valid. |
| */ |
| public long getS3TokenMaxDate() { |
| return getConfiguration().getTimeDuration( |
| OzoneConfigKeys.OZONE_S3_TOKEN_MAX_LIFETIME_KEY, |
| OzoneConfigKeys.OZONE_S3_TOKEN_MAX_LIFETIME_KEY_DEFAULT, |
| TimeUnit.MICROSECONDS); |
| } |
| } |