Skip to content

Commit

Permalink
[yugabyte#9455] Platform: Migrate clientRootCA support for TlsToggle …
Browse files Browse the repository at this point in the history
…to new Upgrade Refactor class

Summary:
Original diff: https://phabricator.dev.yugabyte.com/D12183
Original commit: 2369b7b

Update TlsToggle upgrade task to support the recently introduced support for clientRootCA

Test Plan:
- Migrated unit tests from old class to new class and verified
- Manually tested common flows and verified

Reviewers: sanketh, arnav

Reviewed By: arnav

Subscribers: jenkins-bot, yugaware

Differential Revision: https://phabricator.dev.yugabyte.com/D12383
  • Loading branch information
hkandala committed Jul 27, 2021
1 parent 1bafe17 commit 652d3cd
Show file tree
Hide file tree
Showing 13 changed files with 517 additions and 181 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import com.yugabyte.yw.commissioner.tasks.subtasks.PrecheckNode;
import com.yugabyte.yw.commissioner.tasks.subtasks.WaitForMasterLeader;
import com.yugabyte.yw.commissioner.tasks.subtasks.WaitForTServerHeartBeats;
import com.yugabyte.yw.common.CertificateHelper;
import com.yugabyte.yw.common.NodeManager;
import com.yugabyte.yw.common.PlacementInfoUtil;
import com.yugabyte.yw.forms.UniverseDefinitionTaskParams;
Expand Down Expand Up @@ -126,49 +127,51 @@ public Universe writeUserIntentToUniverse(boolean isReadOnlyCreate) {
public Universe writeUserIntentToUniverse(boolean isReadOnlyCreate, boolean updateOnpremNodes) {
// Create the update lambda.
UniverseUpdater updater =
new UniverseUpdater() {
@Override
public void run(Universe universe) {
// Persist the updated information about the universe.
// It should have been marked as being edited in lockUniverseForUpdate().
UniverseDefinitionTaskParams universeDetails = universe.getUniverseDetails();
if (!universeDetails.updateInProgress) {
String msg =
"Universe "
+ taskParams().universeUUID
+ " has not been marked as being updated.";
log.error(msg);
throw new RuntimeException(msg);
}
if (!isReadOnlyCreate) {
universeDetails.nodeDetailsSet = taskParams().nodeDetailsSet;
universeDetails.nodePrefix = taskParams().nodePrefix;
universeDetails.universeUUID = taskParams().universeUUID;
universe -> {
// Persist the updated information about the universe.
// It should have been marked as being edited in lockUniverseForUpdate().
UniverseDefinitionTaskParams universeDetails = universe.getUniverseDetails();
if (!universeDetails.updateInProgress) {
String msg =
"Universe " + taskParams().universeUUID + " has not been marked as being updated.";
log.error(msg);
throw new RuntimeException(msg);
}
if (!isReadOnlyCreate) {
universeDetails.nodeDetailsSet = taskParams().nodeDetailsSet;
universeDetails.nodePrefix = taskParams().nodePrefix;
universeDetails.universeUUID = taskParams().universeUUID;
universeDetails.allowInsecure = taskParams().allowInsecure;
universeDetails.rootAndClientRootCASame = taskParams().rootAndClientRootCASame;
universeDetails.rootCA = null;
universeDetails.clientRootCA = null;
if (CertificateHelper.isRootCARequired(taskParams())) {
universeDetails.rootCA = taskParams().rootCA;
}
if (CertificateHelper.isClientRootCARequired(taskParams())) {
universeDetails.clientRootCA = taskParams().clientRootCA;
universeDetails.allowInsecure = taskParams().allowInsecure;
Cluster cluster = taskParams().getPrimaryCluster();
if (cluster != null) {
universeDetails.upsertPrimaryCluster(cluster.userIntent, cluster.placementInfo);
} // else read only cluster edit mode.
} else {
// Combine the existing nodes with new read only cluster nodes.
universeDetails.nodeDetailsSet.addAll(taskParams().nodeDetailsSet);
}
taskParams()
.getReadOnlyClusters()
.forEach(
(async) -> {
// Update read replica cluster TLS params to be same as primary cluster
async.userIntent.enableNodeToNodeEncrypt =
universeDetails.getPrimaryCluster().userIntent.enableNodeToNodeEncrypt;
async.userIntent.enableClientToNodeEncrypt =
universeDetails.getPrimaryCluster().userIntent.enableClientToNodeEncrypt;
universeDetails.upsertCluster(
async.userIntent, async.placementInfo, async.uuid);
});
universe.setUniverseDetails(universeDetails);
Cluster cluster = taskParams().getPrimaryCluster();
if (cluster != null) {
universeDetails.upsertPrimaryCluster(cluster.userIntent, cluster.placementInfo);
} // else read only cluster edit mode.
} else {
// Combine the existing nodes with new read only cluster nodes.
universeDetails.nodeDetailsSet.addAll(taskParams().nodeDetailsSet);
}
taskParams()
.getReadOnlyClusters()
.forEach(
(async) -> {
// Update read replica cluster TLS params to be same as primary cluster
async.userIntent.enableNodeToNodeEncrypt =
universeDetails.getPrimaryCluster().userIntent.enableNodeToNodeEncrypt;
async.userIntent.enableClientToNodeEncrypt =
universeDetails.getPrimaryCluster().userIntent.enableClientToNodeEncrypt;
universeDetails.upsertCluster(
async.userIntent, async.placementInfo, async.uuid);
});
universe.setUniverseDetails(universeDetails);
};
// Perform the update. If unsuccessful, this will throw a runtime exception which we do not
// catch as we want to fail.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public void run() {
if (CertificateHelper.isRootCARequired(taskParams())) {
universeDetails.rootCA = taskParams().rootCA;
}
if (taskParams().enableClientToNodeEncrypt) {
if (CertificateHelper.isClientRootCARequired(taskParams())) {
universeDetails.clientRootCA = taskParams().clientRootCA;
}
universe.setUniverseDetails(universeDetails);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.yugabyte.yw.commissioner.UserTaskDetails.SubTaskGroupType;
import com.yugabyte.yw.commissioner.tasks.subtasks.AnsibleConfigureServers;
import com.yugabyte.yw.commissioner.tasks.subtasks.UniverseSetTlsParams;
import com.yugabyte.yw.common.CertificateHelper;
import com.yugabyte.yw.forms.TlsToggleParams;
import com.yugabyte.yw.forms.UpgradeTaskParams.UpgradeOption;
import com.yugabyte.yw.forms.UpgradeTaskParams.UpgradeTaskSubType;
Expand Down Expand Up @@ -63,9 +64,13 @@ public void run() {
private void verifyParams() {
taskParams().verifyParams(getUniverse());

if ((taskParams().enableNodeToNodeEncrypt || taskParams().enableClientToNodeEncrypt)
&& taskParams().rootCA == null) {
throw new IllegalArgumentException("Root certificate cannot be null when enabling TLS");
if (CertificateHelper.isRootCARequired(taskParams()) && taskParams().rootCA == null) {
throw new IllegalArgumentException("Root certificate is null");
}

if (CertificateHelper.isClientRootCARequired(taskParams())
&& taskParams().clientRootCA == null) {
throw new IllegalArgumentException("Client root certificate is null");
}
}

Expand Down Expand Up @@ -178,6 +183,8 @@ private void createUniverseSetTlsParamsTask() {
params.enableClientToNodeEncrypt = taskParams().enableClientToNodeEncrypt;
params.allowInsecure = taskParams().allowInsecure;
params.rootCA = taskParams().rootCA;
params.clientRootCA = taskParams().clientRootCA;
params.rootAndClientRootCASame = taskParams().rootAndClientRootCASame;

UniverseSetTlsParams task = createTask(UniverseSetTlsParams.class);
task.initialize(params);
Expand All @@ -195,6 +202,8 @@ private AnsibleConfigureServers getAnsibleConfigureServerTask(
params.enableClientToNodeEncrypt = taskParams().enableClientToNodeEncrypt;
params.allowInsecure = taskParams().allowInsecure;
params.rootCA = taskParams().rootCA;
params.clientRootCA = taskParams().clientRootCA;
params.rootAndClientRootCASame = taskParams().rootAndClientRootCASame;
params.nodeToNodeChange = getNodeToNodeChange();
AnsibleConfigureServers task = createTask(AnsibleConfigureServers.class);
task.initialize(params);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
import com.yugabyte.yw.commissioner.tasks.subtasks.AnsibleConfigureServers;
import com.yugabyte.yw.commissioner.tasks.subtasks.UniverseSetTlsParams;
import com.yugabyte.yw.forms.CertificateParams;
import com.yugabyte.yw.forms.TlsToggleParams;
import com.yugabyte.yw.forms.UniverseDefinitionTaskParams;
import com.yugabyte.yw.forms.UniverseDefinitionTaskParams.UserIntent;
import com.yugabyte.yw.models.CertificateInfo;
import java.io.ByteArrayInputStream;
import java.io.File;
Expand Down Expand Up @@ -548,22 +551,76 @@ public static PrivateKey getPrivateKey(String keyContent) {
}
}

public static boolean isRootCARequired(UniverseDefinitionTaskParams taskParams) {
UserIntent userIntent = taskParams.getPrimaryCluster().userIntent;
return isRootCARequired(
userIntent.enableNodeToNodeEncrypt,
userIntent.enableClientToNodeEncrypt,
taskParams.rootAndClientRootCASame);
}

public static boolean isRootCARequired(AnsibleConfigureServers.Params taskParams) {
return taskParams.enableNodeToNodeEncrypt
|| (taskParams.rootAndClientRootCASame && taskParams.enableClientToNodeEncrypt);
return isRootCARequired(
taskParams.enableNodeToNodeEncrypt,
taskParams.enableClientToNodeEncrypt,
taskParams.rootAndClientRootCASame);
}

public static boolean isRootCARequired(UniverseSetTlsParams.Params taskParams) {
return taskParams.enableNodeToNodeEncrypt
|| (taskParams.rootAndClientRootCASame && taskParams.enableClientToNodeEncrypt);
return isRootCARequired(
taskParams.enableNodeToNodeEncrypt,
taskParams.enableClientToNodeEncrypt,
taskParams.rootAndClientRootCASame);
}

public static boolean isRootCARequired(TlsToggleParams taskParams) {
return isRootCARequired(
taskParams.enableNodeToNodeEncrypt,
taskParams.enableClientToNodeEncrypt,
taskParams.rootAndClientRootCASame);
}

public static boolean isRootCARequired(
boolean enableNodeToNodeEncrypt,
boolean enableClientToNodeEncrypt,
boolean rootAndClientRootCASame) {
return enableNodeToNodeEncrypt || (rootAndClientRootCASame && enableClientToNodeEncrypt);
}

public static boolean isClientRootCARequired(UniverseDefinitionTaskParams taskParams) {
UserIntent userIntent = taskParams.getPrimaryCluster().userIntent;
return isClientRootCARequired(
userIntent.enableNodeToNodeEncrypt,
userIntent.enableClientToNodeEncrypt,
taskParams.rootAndClientRootCASame);
}

public static boolean isClientRootCARequired(AnsibleConfigureServers.Params taskParams) {
return !taskParams.rootAndClientRootCASame && taskParams.enableClientToNodeEncrypt;
return isClientRootCARequired(
taskParams.enableNodeToNodeEncrypt,
taskParams.enableClientToNodeEncrypt,
taskParams.rootAndClientRootCASame);
}

public static boolean isClientRootCARequired(UniverseSetTlsParams.Params taskParams) {
return !taskParams.rootAndClientRootCASame && taskParams.enableClientToNodeEncrypt;
return isClientRootCARequired(
taskParams.enableNodeToNodeEncrypt,
taskParams.enableClientToNodeEncrypt,
taskParams.rootAndClientRootCASame);
}

public static boolean isClientRootCARequired(TlsToggleParams taskParams) {
return isClientRootCARequired(
taskParams.enableNodeToNodeEncrypt,
taskParams.enableClientToNodeEncrypt,
taskParams.rootAndClientRootCASame);
}

public static boolean isClientRootCARequired(
boolean enableNodeToNodeEncrypt,
boolean enableClientToNodeEncrypt,
boolean rootAndClientRootCASame) {
return !rootAndClientRootCASame && enableClientToNodeEncrypt;
}

public static void writeKeyFileContentToKeyPath(PrivateKey keyContent, String keyPath) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,31 +219,36 @@ public UniverseResp createUniverse(Customer customer, UniverseDefinitionTaskPara
BAD_REQUEST, "IPV6 not supported for platform deployed VMs.");
}
}

if (primaryCluster.userIntent.enableNodeToNodeEncrypt) {
// create self signed rootCA in case it is not provided by the user.
if (taskParams.rootCA == null) {
taskParams.rootCA =
CertificateHelper.createRootCA(
taskParams.nodePrefix, customer.uuid, appConfig.getString("yb.storage.path"));
}

CertificateInfo cert = CertificateInfo.get(taskParams.rootCA);
if (cert.certType == CertificateInfo.Type.CustomServerCert) {
throw new YWServiceException(
BAD_REQUEST,
"CustomServerCert are only supported for Client to Server Communication.");
}
if (cert.certType != CertificateInfo.Type.SelfSigned) {

if (cert.certType == CertificateInfo.Type.CustomCertHostPath) {
if (!taskParams
.getPrimaryCluster()
.userIntent
.providerType
.equals(Common.CloudType.onprem)) {
throw new YWServiceException(
BAD_REQUEST, "Custom certificates are only supported for onprem providers.");
BAD_REQUEST,
"CustomCertHostPath certificates are only supported for onprem providers.");
}
checkValidRootCA(taskParams.rootCA);
}
}

if (primaryCluster.userIntent.enableClientToNodeEncrypt) {
if (taskParams.clientRootCA == null) {
if (taskParams.rootCA != null && taskParams.rootAndClientRootCASame) {
Expand All @@ -258,45 +263,43 @@ public UniverseResp createUniverse(Customer customer, UniverseDefinitionTaskPara
}
}

CertificateInfo cert = CertificateInfo.get(taskParams.clientRootCA);
if (cert.certType == CertificateInfo.Type.CustomCertHostPath) {
if (!taskParams
.getPrimaryCluster()
.userIntent
.providerType
.equals(Common.CloudType.onprem)) {
throw new YWServiceException(
BAD_REQUEST,
"CustomCertHostPath certificates are only supported for onprem providers.");
}
checkValidRootCA(taskParams.rootCA);
}

// Setting rootCA to ClientRootCA in case node to node encryption is disabled.
// This is necessary to set to ensure backward compatibity as existing parts of
// codebase (kubernetes) uses rootCA for Client to Node Encryption
if (taskParams.rootCA == null && taskParams.rootAndClientRootCASame) {
taskParams.rootCA = taskParams.clientRootCA;
}

// If client encryption is enabled, generate the client cert file for each node.
CertificateInfo cert = CertificateInfo.get(taskParams.clientRootCA);
if (cert.certType == CertificateInfo.Type.SelfSigned) {
CertificateHelper.createClientCertificate(
taskParams.clientRootCA,
String.format(
CertificateHelper.CERT_PATH,
appConfig.getString("yb.storage.path"),
customer.uuid.toString(),
taskParams.clientRootCA.toString()),
CertificateHelper.DEFAULT_CLIENT,
null,
null);
} else {
if (cert.certType == CertificateInfo.Type.CustomCertHostPath
&& !taskParams
.getPrimaryCluster()
.userIntent
.providerType
.equals(Common.CloudType.onprem)) {
throw new YWServiceException(
BAD_REQUEST,
"CustomCertHostPath certificates are only supported for onprem providers.");
// Generate client certs if rootAndClientRootCASame is true and rootCA is self-signed.
// This is there only for legacy support, no need if rootCA and clientRootCA are different.
if (taskParams.rootAndClientRootCASame) {
CertificateInfo rootCert = CertificateInfo.get(taskParams.rootCA);
if (rootCert.certType == CertificateInfo.Type.SelfSigned) {
CertificateHelper.createClientCertificate(
taskParams.rootCA,
String.format(
CertificateHelper.CERT_PATH,
appConfig.getString("yb.storage.path"),
customer.uuid.toString(),
taskParams.rootCA.toString()),
CertificateHelper.DEFAULT_CLIENT,
null,
null);
}
checkValidRootCA(taskParams.clientRootCA);
LOG.info(
"Skipping client certificate creation for universe {} ({}) "
+ "because cert {} (type {})is not a self-signed cert.",
universe.name,
universe.universeUUID,
taskParams.clientRootCA,
cert.certType);
}
}

Expand Down
Loading

0 comments on commit 652d3cd

Please sign in to comment.