blob: 06fc071f32dde12cc766a503a67f29e04ac34978 [file] [log] [blame]
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.ozone.security;
import com.google.common.base.Preconditions;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.hdds.security.x509.SecurityConfig;
import org.apache.hadoop.hdds.security.x509.certificate.client.CertificateClient;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.security.AccessControlException;
import org.apache.hadoop.security.token.SecretManager;
import org.apache.hadoop.security.token.Token;
import org.apache.hadoop.security.token.TokenIdentifier;
import org.slf4j.Logger;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import java.util.concurrent.atomic.AtomicInteger;
/**
* SecretManager for Ozone Master. Responsible for signing identifiers with
* private key,
*/
@InterfaceAudience.Private
@InterfaceStability.Unstable
public abstract class OzoneSecretManager<T extends TokenIdentifier>
extends SecretManager<T> {
private final Logger logger;
/**
* The name of the Private/Public Key based hashing algorithm.
*/
private final SecurityConfig securityConfig;
private final long tokenMaxLifetime;
private final long tokenRenewInterval;
private final Text service;
private CertificateClient certClient;
private volatile boolean running;
private OzoneSecretKey currentKey;
private AtomicInteger currentKeyId;
private AtomicInteger tokenSequenceNumber;
/**
* Create a secret manager.
*
* @param secureConf configuration.
* @param tokenMaxLifetime the maximum lifetime of the delegation tokens in
* milliseconds
* @param tokenRenewInterval how often the tokens must be renewed in
* milliseconds
* @param service name of service
* @param logger logger for the secret manager
*/
public OzoneSecretManager(SecurityConfig secureConf, long tokenMaxLifetime,
long tokenRenewInterval, Text service, Logger logger) {
this.securityConfig = secureConf;
this.tokenMaxLifetime = tokenMaxLifetime;
this.tokenRenewInterval = tokenRenewInterval;
currentKeyId = new AtomicInteger();
tokenSequenceNumber = new AtomicInteger();
this.service = service;
this.logger = logger;
}
/**
* Compute HMAC of the identifier using the private key and return the output
* as password.
*
* @param identifier
* @param privateKey
* @return byte[] signed byte array
*/
public byte[] createPassword(byte[] identifier, PrivateKey privateKey)
throws OzoneSecurityException {
try {
Signature rsaSignature = Signature.getInstance(
getDefaultSignatureAlgorithm());
rsaSignature.initSign(privateKey);
rsaSignature.update(identifier);
return rsaSignature.sign();
} catch (InvalidKeyException | NoSuchAlgorithmException |
SignatureException ex) {
throw new OzoneSecurityException("Error while creating HMAC hash for " +
"token.", ex, OzoneSecurityException.ResultCodes
.SECRET_MANAGER_HMAC_ERROR);
}
}
@Override
public byte[] createPassword(T identifier) {
if (logger.isDebugEnabled()) {
logger.debug("Creating password for identifier: {}, currentKey: {}",
formatTokenId(identifier), currentKey.getKeyId());
}
byte[] password = null;
try {
password = createPassword(identifier.getBytes(),
currentKey.getPrivateKey());
} catch (IOException ioe) {
logger.error("Could not store token {}!!", formatTokenId(identifier),
ioe);
}
return password;
}
/**
* Renew a delegation token.
*
* @param token the token to renew
* @param renewer the full principal name of the user doing the renewal
* @return the new expiration time
* @throws InvalidToken if the token is invalid
* @throws AccessControlException if the user can't renew token
*/
public abstract long renewToken(Token<T> token, String renewer)
throws IOException;
/**
* Cancel a token by removing it from store and cache.
*
* @return Identifier of the canceled token
* @throws InvalidToken for invalid token
* @throws AccessControlException if the user isn't allowed to cancel
*/
public abstract T cancelToken(Token<T> token, String canceller)
throws IOException;
public int incrementCurrentKeyId() {
return currentKeyId.incrementAndGet();
}
public int getDelegationTokenSeqNum() {
return tokenSequenceNumber.get();
}
public void setDelegationTokenSeqNum(int seqNum) {
tokenSequenceNumber.set(seqNum);
}
public int incrementDelegationTokenSeqNum() {
return tokenSequenceNumber.incrementAndGet();
}
/**
* Update the current master key. This is called once by start method before
* tokenRemoverThread is created,
*/
private OzoneSecretKey updateCurrentKey(KeyPair keyPair) throws IOException {
logger.info("Updating the current master key for generating tokens");
// TODO: fix me based on the certificate expire time to set the key
// expire time.
int newCurrentId = incrementCurrentKeyId();
OzoneSecretKey newKey = new OzoneSecretKey(newCurrentId, -1,
keyPair);
currentKey = newKey;
return currentKey;
}
public String formatTokenId(T id) {
return "(" + id + ")";
}
/**
* Should be called before this object is used.
*
* @param client
* @throws IOException
*/
public synchronized void start(CertificateClient client)
throws IOException {
Preconditions.checkState(!isRunning());
setCertClient(client);
updateCurrentKey(new KeyPair(certClient.getPublicKey(),
certClient.getPrivateKey()));
setIsRunning(true);
}
/**
* Stops the OzoneDelegationTokenSecretManager.
*
* @throws IOException
*/
public synchronized void stop() throws IOException {
setIsRunning(false);
}
public String getDefaultSignatureAlgorithm() {
return securityConfig.getSignatureAlgo();
}
public long getTokenMaxLifetime() {
return tokenMaxLifetime;
}
public long getTokenRenewInterval() {
return tokenRenewInterval;
}
public Text getService() {
return service;
}
/**
* Is Secret Manager running.
*
* @return true if secret mgr is running
*/
public synchronized boolean isRunning() {
return running;
}
public void setIsRunning(boolean val) {
running = val;
}
public OzoneSecretKey getCurrentKey() {
return currentKey;
}
public AtomicInteger getCurrentKeyId() {
return currentKeyId;
}
public AtomicInteger getTokenSequenceNumber() {
return tokenSequenceNumber;
}
public CertificateClient getCertClient() {
return certClient;
}
public void setCertClient(CertificateClient client) {
this.certClient = client;
}
}