Skip to content

Commit e508976

Browse files
foldikoleewere
authored andcommitted
CB-18258 Generate ECDSA based access key if it is enabled in UMS and packages on image support it.
1 parent ea15fdf commit e508976

File tree

29 files changed

+1863
-1545
lines changed

29 files changed

+1863
-1545
lines changed

auth-connector/src/generated/main/java/com/cloudera/thunderhead/service/usermanagement/UserManagementProto.java

+1,422-1,403
Large diffs are not rendered by default.

auth-connector/src/main/java/com/sequenceiq/cloudbreak/auth/altus/EntitlementService.java

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.sequenceiq.cloudbreak.auth.altus;
22

3+
import static com.sequenceiq.cloudbreak.auth.altus.model.Entitlement.ACCESS_KEY_ECDSA;
34
import static com.sequenceiq.cloudbreak.auth.altus.model.Entitlement.AUDIT_ARCHIVING_GCP;
45
import static com.sequenceiq.cloudbreak.auth.altus.model.Entitlement.CDP_ALLOW_DIFFERENT_DATAHUB_VERSION_THAN_DATALAKE;
56
import static com.sequenceiq.cloudbreak.auth.altus.model.Entitlement.CDP_ALLOW_HA_REPAIR;
@@ -558,6 +559,10 @@ public boolean isLongTimeBackupEnabled(String accountId) {
558559
return isEntitlementRegistered(accountId, CDP_DATALAKE_BACKUP_LONG_TIMEOUT);
559560
}
560561

562+
public boolean isECDSABasedAccessKeyEnabled(String accountId) {
563+
return isEntitlementRegistered(accountId, ACCESS_KEY_ECDSA);
564+
}
565+
561566
public List<String> getEntitlements(String accountId) {
562567
Account accountDetails = umsClient.getAccountDetails(
563568
accountId,

common-model/src/main/java/com/sequenceiq/common/api/telemetry/model/CdpAccessKeyType.java auth-connector/src/main/java/com/sequenceiq/cloudbreak/auth/altus/model/CdpAccessKeyType.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.sequenceiq.common.api.telemetry.model;
1+
package com.sequenceiq.cloudbreak.auth.altus.model;
22

33
public enum CdpAccessKeyType {
44
ED25519("Ed25519"), ECDSA("ECDSA"), RSA("RSA");

auth-connector/src/main/java/com/sequenceiq/cloudbreak/auth/altus/model/Entitlement.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -114,5 +114,6 @@ public enum Entitlement {
114114
CDP_POSTGRES_UPGRADE_EMBEDDED,
115115
CDP_POSTGRES_UPGRADE_EXCEPTION,
116116
CDP_USERSYNC_SPLIT_FREEIPA_USER_RETRIEVAL,
117-
CDP_DATALAKE_BACKUP_LONG_TIMEOUT
117+
CDP_DATALAKE_BACKUP_LONG_TIMEOUT,
118+
ACCESS_KEY_ECDSA
118119
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.sequenceiq.cloudbreak.auth.altus.model;
2+
3+
public class MachineUserRequest {
4+
5+
private String name;
6+
7+
private String actorCrn;
8+
9+
private String accountId;
10+
11+
private CdpAccessKeyType cdpAccessKeyType;
12+
13+
public String getName() {
14+
return name;
15+
}
16+
17+
public MachineUserRequest setName(String name) {
18+
this.name = name;
19+
return this;
20+
}
21+
22+
public String getActorCrn() {
23+
return actorCrn;
24+
}
25+
26+
public MachineUserRequest setActorCrn(String actorCrn) {
27+
this.actorCrn = actorCrn;
28+
return this;
29+
}
30+
31+
public String getAccountId() {
32+
return accountId;
33+
}
34+
35+
public MachineUserRequest setAccountId(String accountId) {
36+
this.accountId = accountId;
37+
return this;
38+
}
39+
40+
public CdpAccessKeyType getCdpAccessKeyType() {
41+
return cdpAccessKeyType;
42+
}
43+
44+
public MachineUserRequest setCdpAccessKeyType(CdpAccessKeyType cdpAccessKeyType) {
45+
this.cdpAccessKeyType = cdpAccessKeyType;
46+
return this;
47+
}
48+
}

auth-connector/src/main/java/com/sequenceiq/cloudbreak/auth/altus/service/AltusIAMService.java

+27-14
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import com.cloudera.thunderhead.service.usermanagement.UserManagementProto;
1313
import com.sequenceiq.cloudbreak.auth.altus.GrpcUmsClient;
1414
import com.sequenceiq.cloudbreak.auth.altus.model.AltusCredential;
15+
import com.sequenceiq.cloudbreak.auth.altus.model.CdpAccessKeyType;
16+
import com.sequenceiq.cloudbreak.auth.altus.model.MachineUserRequest;
1517
import com.sequenceiq.cloudbreak.auth.crn.RegionAwareInternalCrnGeneratorFactory;
1618
import com.sequenceiq.common.api.telemetry.model.AnonymizationRule;
1719

@@ -55,32 +57,30 @@ public AltusCredential generateMachineUserWithAccessKeyForLegacyCm(String machin
5557
/**
5658
* Generate databus machine user with access keys
5759
*/
58-
public Optional<AltusCredential> generateDatabusMachineUserWithAccessKey(String machineUserName, String actorCrn, String accountId,
59-
boolean useSharedCredential) {
60+
public Optional<AltusCredential> generateDatabusMachineUserWithAccessKey(MachineUserRequest machineUserRequest, boolean useSharedCredential) {
6061
return Optional.ofNullable(sharedAltusCredentialProvider.getSharedCredentialIfConfigured(useSharedCredential)
6162
.orElse(umsClient.createMachineUserAndGenerateKeys(
62-
machineUserName,
63-
actorCrn,
64-
accountId,
65-
roleCrnGenerator.getBuiltInDatabusRoleCrn(accountId),
63+
machineUserRequest.getName(),
64+
machineUserRequest.getActorCrn(),
65+
machineUserRequest.getAccountId(),
66+
roleCrnGenerator.getBuiltInDatabusRoleCrn(machineUserRequest.getAccountId()),
6667
Collections.emptyMap(),
67-
UserManagementProto.AccessKeyType.Value.ED25519,
68+
mapToAccessKeyType(machineUserRequest.getCdpAccessKeyType()),
6869
regionAwareInternalCrnGeneratorFactory)));
6970
}
7071

7172
/**
7273
* Generate monitoring machine user with access keys
7374
*/
74-
public Optional<AltusCredential> generateMonitoringMachineUserWithAccessKey(String machineUserName, String actorCrn, String accountId,
75-
boolean useSharedCredential) {
75+
public Optional<AltusCredential> generateMonitoringMachineUserWithAccessKey(MachineUserRequest machineUserRequest, boolean useSharedCredential) {
7676
return Optional.ofNullable(sharedAltusCredentialProvider.getSharedCredentialIfConfigured(useSharedCredential)
7777
.orElse(umsClient.createMachineUserAndGenerateKeys(
78-
machineUserName,
79-
actorCrn,
80-
accountId,
81-
roleCrnGenerator.getBuiltInDatabusRoleCrn(accountId),
78+
machineUserRequest.getName(),
79+
machineUserRequest.getActorCrn(),
80+
machineUserRequest.getAccountId(),
81+
roleCrnGenerator.getBuiltInDatabusRoleCrn(machineUserRequest.getAccountId()),
8282
Collections.emptyMap(),
83-
UserManagementProto.AccessKeyType.Value.ED25519,
83+
mapToAccessKeyType(machineUserRequest.getCdpAccessKeyType()),
8484
regionAwareInternalCrnGeneratorFactory)));
8585
}
8686

@@ -141,4 +141,17 @@ public void clearMachineUser(String machineUserName, String actorCrn, String acc
141141
public List<UserManagementProto.MachineUser> getAllMachineUsersForAccount(String accountId) {
142142
return umsClient.listAllMachineUsers(accountId, true, true, regionAwareInternalCrnGeneratorFactory);
143143
}
144+
145+
private UserManagementProto.AccessKeyType.Value mapToAccessKeyType(CdpAccessKeyType cdpAccessKeyType) {
146+
switch (cdpAccessKeyType) {
147+
case ED25519:
148+
return UserManagementProto.AccessKeyType.Value.ED25519;
149+
case RSA:
150+
return UserManagementProto.AccessKeyType.Value.RSA;
151+
case ECDSA:
152+
return UserManagementProto.AccessKeyType.Value.ECDSA;
153+
default:
154+
return UserManagementProto.AccessKeyType.Value.ED25519;
155+
}
156+
}
144157
}

auth-connector/src/main/proto/usermanagement.proto

+3
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,9 @@ message AccessKeyType {
732732
// Private keys are 32 bytes encoded in base64 (44 bytes)
733733
// Public keys are 32 bytes
734734
ED25519 = 2;
735+
// Private keys are encoded in PKCS#8
736+
// Public keys are encoded in X.509
737+
ECDSA = 3;
735738
}
736739
}
737740

common-model/src/main/java/com/sequenceiq/common/api/telemetry/model/CdpCredential.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public abstract class CdpCredential implements Serializable {
2323
private String privateKey;
2424

2525
@JsonProperty("accessKeyType")
26-
private String accessKeyType;
26+
private String accessKeyType = "Ed25519";
2727

2828
public String getMachineUserName() {
2929
return machineUserName;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.sequenceiq.cloudbreak.telemetry;
2+
3+
import java.util.List;
4+
import java.util.Map;
5+
6+
import org.apache.commons.lang3.tuple.Pair;
7+
import org.slf4j.Logger;
8+
import org.slf4j.LoggerFactory;
9+
import org.springframework.stereotype.Component;
10+
11+
import com.sequenceiq.cloudbreak.common.type.Versioned;
12+
import com.sequenceiq.cloudbreak.util.VersionComparator;
13+
14+
@Component
15+
public class TelemetryFeatureService {
16+
17+
private static final Logger LOGGER = LoggerFactory.getLogger(TelemetryFeatureService.class);
18+
19+
private static final List<Pair<String, Versioned>> ECDSA_PACKAGE_VERSION_REQUIREMENTS = List.of(
20+
Pair.of("cdp-logging-agent", () -> "0.3.3"),
21+
Pair.of("cdp-request-signer", () -> "0.2.3"),
22+
Pair.of("cdp-telemetry", () -> "0.4.30"));
23+
24+
public boolean isECDSAAccessKeyTypeSupported(Map<String, String> packages) {
25+
if (packages == null) {
26+
return false;
27+
}
28+
for (Pair<String, Versioned> packageWithVersion : ECDSA_PACKAGE_VERSION_REQUIREMENTS) {
29+
String packageName = packageWithVersion.getKey();
30+
Versioned minimumVersion = packageWithVersion.getValue();
31+
if (!packages.containsKey(packageName)) {
32+
LOGGER.info("Image doesn't contain {} package which is required for ECDSA.", packageName);
33+
return false;
34+
}
35+
String packageVersion = packages.get(packageName);
36+
if (new VersionComparator().compare(() -> packageVersion, minimumVersion) < 0) {
37+
LOGGER.info("{} package's version {} is smaller than {}. Therefore ECDSA based access key is not enabled.",
38+
packageName, packageVersion, minimumVersion.getVersion());
39+
return false;
40+
}
41+
}
42+
LOGGER.info("ECDSA based access key type is enabled by package versions.");
43+
return true;
44+
}
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.sequenceiq.cloudbreak.telemetry;
2+
3+
import java.util.stream.Collectors;
4+
5+
public class UMSSecretKeyFormatter {
6+
7+
private UMSSecretKeyFormatter() {
8+
}
9+
10+
public static String formatSecretKey(String keyType, String secretKey) {
11+
if ("ECDSA".equals(keyType)) {
12+
return secretKey.trim().lines().collect(Collectors.joining("\\n"));
13+
} else {
14+
return secretKey;
15+
}
16+
}
17+
}

common/src/main/java/com/sequenceiq/cloudbreak/telemetry/databus/DatabusConfigService.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,31 @@
66
import org.springframework.stereotype.Service;
77

88
import com.sequenceiq.cloudbreak.telemetry.TelemetryPillarConfigGenerator;
9+
import com.sequenceiq.cloudbreak.telemetry.UMSSecretKeyFormatter;
910
import com.sequenceiq.cloudbreak.telemetry.context.DatabusContext;
1011
import com.sequenceiq.cloudbreak.telemetry.context.TelemetryContext;
11-
import com.sequenceiq.common.api.telemetry.model.CdpAccessKeyType;
1212
import com.sequenceiq.common.api.telemetry.model.DataBusCredential;
1313

1414
@Service
1515
public class DatabusConfigService implements TelemetryPillarConfigGenerator<DatabusConfigView> {
1616

1717
private static final Logger LOGGER = LoggerFactory.getLogger(DatabusConfigService.class);
1818

19+
private static final String ED25519 = "Ed25519";
20+
1921
private static final String SALT_STATE = "databus";
2022

2123
@Override
2224
public DatabusConfigView createConfigs(TelemetryContext context) {
2325
final DatabusContext databusContext = context.getDatabusContext();
2426
final DataBusCredential dataBusCredential = databusContext.getCredential();
27+
String accessKeySecretAlgorithm = StringUtils.defaultIfBlank(dataBusCredential.getAccessKeyType(), ED25519);
2528
return new DatabusConfigView.Builder()
2629
.withEnabled(databusContext.isEnabled())
2730
.withEndpoint(databusContext.getEndpoint())
2831
.withAccessKeyId(dataBusCredential.getAccessKey())
29-
.withAccessKeySecret(dataBusCredential.getPrivateKey().toCharArray())
30-
.withAccessKeySecretAlgorithm(StringUtils.defaultIfBlank(dataBusCredential.getAccessKeyType(), CdpAccessKeyType.ED25519.getValue()))
32+
.withAccessKeySecret(UMSSecretKeyFormatter.formatSecretKey(accessKeySecretAlgorithm, dataBusCredential.getPrivateKey()).toCharArray())
33+
.withAccessKeySecretAlgorithm(accessKeySecretAlgorithm)
3134
.build();
3235
}
3336

common/src/main/java/com/sequenceiq/cloudbreak/telemetry/monitoring/MonitoringConfigService.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@
66
import org.springframework.stereotype.Service;
77

88
import com.sequenceiq.cloudbreak.telemetry.TelemetryPillarConfigGenerator;
9+
import com.sequenceiq.cloudbreak.telemetry.UMSSecretKeyFormatter;
910
import com.sequenceiq.cloudbreak.telemetry.context.MonitoringContext;
1011
import com.sequenceiq.cloudbreak.telemetry.context.TelemetryContext;
11-
import com.sequenceiq.common.api.telemetry.model.CdpAccessKeyType;
1212

1313
@Service
1414
public class MonitoringConfigService implements TelemetryPillarConfigGenerator<MonitoringConfigView> {
1515

1616
private static final Logger LOGGER = LoggerFactory.getLogger(MonitoringConfigService.class);
1717

18+
private static final String ED25519 = "Ed25519";
19+
1820
private static final String SALT_STATE = "monitoring";
1921

2022
private final MonitoringConfiguration monitoringConfiguration;
@@ -50,9 +52,10 @@ public MonitoringConfigView createConfigs(TelemetryContext context) {
5052
builder.withMinBackoff(monitoringConfiguration.getAgent().getMinBackoff());
5153
builder.withMaxBackoff(monitoringConfiguration.getAgent().getMaxBackoff());
5254
if (monitoringContext.getCredential() != null) {
55+
String accessKeyType = StringUtils.defaultIfBlank(monitoringContext.getCredential().getAccessKeyType(), ED25519);
5356
builder.withAccessKeyId(monitoringContext.getCredential().getAccessKey())
54-
.withPrivateKey(monitoringContext.getCredential().getPrivateKey().toCharArray())
55-
.withAccessKeyType(StringUtils.defaultIfBlank(monitoringContext.getCredential().getAccessKeyType(), CdpAccessKeyType.ED25519.getValue()));
57+
.withPrivateKey(UMSSecretKeyFormatter.formatSecretKey(accessKeyType, monitoringContext.getCredential().getPrivateKey()).toCharArray())
58+
.withAccessKeyType(accessKeyType);
5659
}
5760
fillExporterConfigs(builder, monitoringContext.getSharedPassword());
5861
fillRequestSignerConfigs(monitoringConfiguration.getRequestSigner(), builder);

common/src/main/java/com/sequenceiq/cloudbreak/telemetry/monitoring/MonitoringConfigView.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ public Map<String, Object> toMap() {
283283
map.put("token", this.token != null ? new String(this.token) : EMPTY_CONFIG_DEFAULT);
284284
map.put("monitoringAccessKeyId", defaultIfNull(this.accessKeyId, EMPTY_CONFIG_DEFAULT));
285285
map.put("monitoringPrivateKey", this.privateKey != null ? new String(this.privateKey) : EMPTY_CONFIG_DEFAULT);
286-
map.put("accessKeyType", defaultIfNull(this.accessKeyType, EMPTY_CONFIG_DEFAULT));
286+
map.put("monitoringAccessKeyType", defaultIfNull(this.accessKeyType, EMPTY_CONFIG_DEFAULT));
287287
if (this.clusterDetails != null) {
288288
map.putAll(clusterDetails.toMap());
289289
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.sequenceiq.cloudbreak.telemetry;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
class UMSSecretKeyFormatterTest {
8+
9+
@Test
10+
public void testECDSAKeyIsFormattedInOneLine() {
11+
String secretKey = "-----BEGIN PRIVATE KEY-----\n" +
12+
"MIH3AgEAMBAGByqGSM49AgEGBSuBBAAjBIHfMIHcAgEBBEIA/aEOb3cox2HlI8OV\n" +
13+
"citV0heZE3++uAu2HmkwNEMBMRDOGuSw+9YaoAc+k0nioxJZ/IRCt7KGkT2Zm5rO\n" +
14+
"j6KS67agBwYFK4EEACOhgYkDgYYABAA798mhJb0V24eOfLmSpo4Odp+dgdc6DqlE\n" +
15+
"piZXdHME1CvU96nPax8KUYG776GrqQsmSk36SjdiqYyNVnUGqHTOYgBPhaOX8JU0\n" +
16+
"HlP8C4GzBCBoIJGE+ItGil6gO44Xzd3Phs8gnUOb2uhJKJ9niim1UCgT3UKk5HaY\n" +
17+
"PDIxdZ0cCY/W0Q==\n" +
18+
"-----END PRIVATE KEY-----";
19+
assertEquals("-----BEGIN PRIVATE KEY-----\\n" +
20+
"MIH3AgEAMBAGByqGSM49AgEGBSuBBAAjBIHfMIHcAgEBBEIA/aEOb3cox2HlI8OV\\n" +
21+
"citV0heZE3++uAu2HmkwNEMBMRDOGuSw+9YaoAc+k0nioxJZ/IRCt7KGkT2Zm5rO\\n" +
22+
"j6KS67agBwYFK4EEACOhgYkDgYYABAA798mhJb0V24eOfLmSpo4Odp+dgdc6DqlE\\n" +
23+
"piZXdHME1CvU96nPax8KUYG776GrqQsmSk36SjdiqYyNVnUGqHTOYgBPhaOX8JU0\\n" +
24+
"HlP8C4GzBCBoIJGE+ItGil6gO44Xzd3Phs8gnUOb2uhJKJ9niim1UCgT3UKk5HaY\\n" +
25+
"PDIxdZ0cCY/W0Q==\\n" +
26+
"-----END PRIVATE KEY-----", UMSSecretKeyFormatter.formatSecretKey("ECDSA", secretKey));
27+
}
28+
29+
}

common/src/test/java/com/sequenceiq/cloudbreak/telemetry/monitoring/MonitoringConfigServiceTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ public void testCreateConfigs() {
105105
// THEN
106106
assertEquals("myendpoint", result.get("remoteWriteUrl"));
107107
assertEquals(true, result.get("cmAutoTls"));
108-
assertEquals(ACCESS_KEY_TYPE, result.get("accessKeyType"));
108+
assertEquals(ACCESS_KEY_TYPE, result.get("monitoringAccessKeyType"));
109109
assertEquals(true, result.get("enabled"));
110110
assertEquals("user", result.get("cmUsername"));
111111
assertEquals(1000, result.get("blackboxExporterClouderaIntervalSeconds"));

core/src/main/java/com/sequenceiq/cloudbreak/core/bootstrap/service/host/ClusterHostServiceRunner.java

-11
Original file line numberDiff line numberDiff line change
@@ -548,17 +548,6 @@ private void addClouderaManagerConfig(StackDto stackDto, Map<String, SaltPillarP
548548
servicePillar.putAll(createPillarWithClouderaManagerSettings(clouderaManagerRepo, stackDto, primaryGatewayConfig));
549549
}
550550

551-
private <T> T convertOrReturnNull(String value, Class<T> type) {
552-
if (StringUtils.isNotBlank(value)) {
553-
try {
554-
return new Json(value).get(type);
555-
} catch (IOException e) {
556-
LOGGER.error("Cannot read {} from cluster entity. Continue without value.", type.getSimpleName(), e);
557-
}
558-
}
559-
return null;
560-
}
561-
562551
private VirtualGroupRequest getVirtualGroupRequest(String virtualGroupsEnvironmentCrn, Optional<LdapView> ldapView) {
563552
String adminGroup = ldapView.isPresent() ? ldapView.get().getAdminGroup() : "";
564553
return new VirtualGroupRequest(virtualGroupsEnvironmentCrn, adminGroup);

0 commit comments

Comments
 (0)