Skip to content

Commit b2aea16

Browse files
committed
Add KMS framework
1 parent c79b33c commit b2aea16

File tree

44 files changed

+4969
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+4969
-1
lines changed

api/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@
7171
<artifactId>cloud-framework-direct-download</artifactId>
7272
<version>${project.version}</version>
7373
</dependency>
74+
<dependency>
75+
<groupId>org.apache.cloudstack</groupId>
76+
<artifactId>cloud-framework-kms</artifactId>
77+
<version>${project.version}</version>
78+
</dependency>
7479
</dependencies>
7580
<build>
7681
<plugins>

api/src/main/java/com/cloud/event/EventTypes.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,14 @@ public class EventTypes {
271271
public static final String EVENT_CA_CERTIFICATE_REVOKE = "CA.CERTIFICATE.REVOKE";
272272
public static final String EVENT_CA_CERTIFICATE_PROVISION = "CA.CERTIFICATE.PROVISION";
273273

274+
// KMS (Key Management Service) events
275+
public static final String EVENT_KMS_KEY_WRAP = "KMS.KEY.WRAP";
276+
public static final String EVENT_KMS_KEY_UNWRAP = "KMS.KEY.UNWRAP";
277+
public static final String EVENT_KMS_KEK_CREATE = "KMS.KEK.CREATE";
278+
public static final String EVENT_KMS_KEK_ROTATE = "KMS.KEK.ROTATE";
279+
public static final String EVENT_KMS_KEK_DELETE = "KMS.KEK.DELETE";
280+
public static final String EVENT_KMS_HEALTH_CHECK = "KMS.HEALTH.CHECK";
281+
274282
// Account events
275283
public static final String EVENT_ACCOUNT_ENABLE = "ACCOUNT.ENABLE";
276284
public static final String EVENT_ACCOUNT_DISABLE = "ACCOUNT.DISABLE";

api/src/main/java/org/apache/cloudstack/api/ApiCommandResourceType.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ public enum ApiCommandResourceType {
8989
KubernetesSupportedVersion(null),
9090
SharedFS(org.apache.cloudstack.storage.sharedfs.SharedFS.class),
9191
Extension(org.apache.cloudstack.extension.Extension.class),
92-
ExtensionCustomAction(org.apache.cloudstack.extension.ExtensionCustomAction.class);
92+
ExtensionCustomAction(org.apache.cloudstack.extension.ExtensionCustomAction.class),
93+
KmsKey(org.apache.cloudstack.kms.KMSKey.class);
9394

9495
private final Class<?> clazz;
9596

api/src/main/java/org/apache/cloudstack/api/ApiConstants.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,9 @@ public class ApiConstants {
862862
public static final String SORT_BY = "sortby";
863863
public static final String CHANGE_CIDR = "changecidr";
864864
public static final String PURPOSE = "purpose";
865+
public static final String KMS_KEY_ID = "kmskeyid";
866+
public static final String KEK_LABEL = "keklabel";
867+
public static final String KEY_BITS = "keybits";
865868
public static final String IS_TAGGED = "istagged";
866869
public static final String INSTANCE_NAME = "instancename";
867870
public static final String CONSIDER_LAST_HOST = "considerlasthost";

api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@
7373
import org.apache.cloudstack.api.response.IPAddressResponse;
7474
import org.apache.cloudstack.api.response.ImageStoreResponse;
7575
import org.apache.cloudstack.api.response.InstanceGroupResponse;
76+
import org.apache.cloudstack.api.response.KMSKeyResponse;
77+
import org.apache.cloudstack.kms.KMSKey;
7678
import org.apache.cloudstack.api.response.InternalLoadBalancerElementResponse;
7779
import org.apache.cloudstack.api.response.IpForwardingRuleResponse;
7880
import org.apache.cloudstack.api.response.IpQuarantineResponse;
@@ -583,4 +585,6 @@ List<TemplateResponse> createTemplateResponses(ResponseView view, VirtualMachine
583585
GuiThemeResponse createGuiThemeResponse(GuiThemeJoin guiThemeJoin);
584586

585587
ConsoleSessionResponse createConsoleSessionResponse(ConsoleSession consoleSession, ResponseView responseView);
588+
589+
KMSKeyResponse createKMSKeyResponse(KMSKey kmsKey);
586590
}
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.cloudstack.api.command.user.kms;
21+
22+
import com.cloud.exception.ResourceAllocationException;
23+
import com.cloud.user.Account;
24+
import org.apache.cloudstack.acl.RoleType;
25+
import org.apache.cloudstack.api.APICommand;
26+
import org.apache.cloudstack.api.ApiCommandResourceType;
27+
import org.apache.cloudstack.api.ApiConstants;
28+
import org.apache.cloudstack.api.ApiErrorCode;
29+
import org.apache.cloudstack.api.BaseCmd;
30+
import org.apache.cloudstack.api.Parameter;
31+
import org.apache.cloudstack.api.ServerApiException;
32+
import org.apache.cloudstack.api.command.user.UserCmd;
33+
import org.apache.cloudstack.api.response.DomainResponse;
34+
import org.apache.cloudstack.api.response.KMSKeyResponse;
35+
import org.apache.cloudstack.api.response.ZoneResponse;
36+
import org.apache.cloudstack.context.CallContext;
37+
import org.apache.cloudstack.framework.kms.KMSException;
38+
import org.apache.cloudstack.kms.KMSManager;
39+
40+
import javax.inject.Inject;
41+
42+
@APICommand(name = "createKMSKey",
43+
description = "Creates a new KMS key (Key Encryption Key) for encryption",
44+
responseObject = KMSKeyResponse.class,
45+
since = "4.23.0",
46+
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
47+
requestHasSensitiveInfo = false,
48+
responseHasSensitiveInfo = false)
49+
public class CreateKMSKeyCmd extends BaseCmd implements UserCmd {
50+
private static final String s_name = "createkmskeyresponse";
51+
52+
@Inject
53+
private KMSManager kmsManager;
54+
55+
/////////////////////////////////////////////////////
56+
//////////////// API parameters /////////////////////
57+
/////////////////////////////////////////////////////
58+
59+
@Parameter(name = ApiConstants.NAME,
60+
required = true,
61+
type = CommandType.STRING,
62+
description = "Name of the KMS key")
63+
private String name;
64+
65+
@Parameter(name = ApiConstants.DESCRIPTION,
66+
type = CommandType.STRING,
67+
description = "Description of the KMS key")
68+
private String description;
69+
70+
@Parameter(name = ApiConstants.PURPOSE,
71+
required = true,
72+
type = CommandType.STRING,
73+
description = "Purpose of the key: VOLUME_ENCRYPTION, TLS_CERT, CONFIG_SECRET")
74+
private String purpose;
75+
76+
@Parameter(name = ApiConstants.ZONE_ID,
77+
required = true,
78+
type = CommandType.UUID,
79+
entityType = ZoneResponse.class,
80+
description = "Zone ID where the key will be valid")
81+
private Long zoneId;
82+
83+
@Parameter(name = ApiConstants.ACCOUNT,
84+
type = CommandType.STRING,
85+
description = "Account name (for creating keys for child accounts - requires domain admin or admin)")
86+
private String accountName;
87+
88+
@Parameter(name = ApiConstants.DOMAIN_ID,
89+
type = CommandType.UUID,
90+
entityType = DomainResponse.class,
91+
description = "Domain ID (for creating keys for child accounts - requires domain admin or admin)")
92+
private Long domainId;
93+
94+
@Parameter(name = ApiConstants.KEY_BITS,
95+
type = CommandType.INTEGER,
96+
description = "Key size in bits: 128, 192, or 256 (default: 256)")
97+
private Integer keyBits;
98+
99+
/////////////////////////////////////////////////////
100+
/////////////////// Accessors ///////////////////////
101+
/////////////////////////////////////////////////////
102+
103+
public String getName() {
104+
return name;
105+
}
106+
107+
public String getDescription() {
108+
return description;
109+
}
110+
111+
public String getPurpose() {
112+
return purpose;
113+
}
114+
115+
public Long getZoneId() {
116+
return zoneId;
117+
}
118+
119+
public String getAccountName() {
120+
return accountName;
121+
}
122+
123+
public Long getDomainId() {
124+
return domainId;
125+
}
126+
127+
public Integer getKeyBits() {
128+
return keyBits != null ? keyBits : 256; // Default to 256 bits
129+
}
130+
131+
/////////////////////////////////////////////////////
132+
/////////////// API Implementation///////////////////
133+
/////////////////////////////////////////////////////
134+
135+
@Override
136+
public void execute() throws ResourceAllocationException {
137+
try {
138+
KMSKeyResponse response = kmsManager.createKMSKey(this);
139+
response.setResponseName(getCommandName());
140+
setResponseObject(response);
141+
} catch (KMSException e) {
142+
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR,
143+
"Failed to create KMS key: " + e.getMessage());
144+
}
145+
}
146+
147+
@Override
148+
public String getCommandName() {
149+
return s_name;
150+
}
151+
152+
@Override
153+
public long getEntityOwnerId() {
154+
Account caller = CallContext.current().getCallingAccount();
155+
if (accountName != null || domainId != null) {
156+
return _accountService.finalyzeAccountId(accountName, domainId, null, true);
157+
}
158+
return caller.getId();
159+
}
160+
161+
@Override
162+
public ApiCommandResourceType getApiResourceType() {
163+
return ApiCommandResourceType.KmsKey;
164+
}
165+
}
166+
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.cloudstack.api.command.user.kms;
21+
22+
import com.cloud.event.EventTypes;
23+
import org.apache.cloudstack.acl.RoleType;
24+
import org.apache.cloudstack.api.APICommand;
25+
import org.apache.cloudstack.api.ApiCommandResourceType;
26+
import org.apache.cloudstack.api.ApiConstants;
27+
import org.apache.cloudstack.api.ApiErrorCode;
28+
import org.apache.cloudstack.api.BaseAsyncCmd;
29+
import org.apache.cloudstack.api.Parameter;
30+
import org.apache.cloudstack.api.ServerApiException;
31+
import org.apache.cloudstack.api.command.user.UserCmd;
32+
import org.apache.cloudstack.api.response.KMSKeyResponse;
33+
import org.apache.cloudstack.api.response.SuccessResponse;
34+
import org.apache.cloudstack.context.CallContext;
35+
import org.apache.cloudstack.framework.kms.KMSException;
36+
import org.apache.cloudstack.kms.KMSManager;
37+
38+
import javax.inject.Inject;
39+
40+
@APICommand(name = "deleteKMSKey",
41+
description = "Deletes a KMS key (only if not in use)",
42+
responseObject = SuccessResponse.class,
43+
since = "4.23.0",
44+
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
45+
requestHasSensitiveInfo = false,
46+
responseHasSensitiveInfo = false)
47+
public class DeleteKMSKeyCmd extends BaseAsyncCmd implements UserCmd {
48+
private static final String s_name = "deletekmskeyresponse";
49+
50+
@Inject
51+
private KMSManager kmsManager;
52+
53+
/////////////////////////////////////////////////////
54+
//////////////// API parameters /////////////////////
55+
/////////////////////////////////////////////////////
56+
57+
@Parameter(name = ApiConstants.ID,
58+
required = true,
59+
type = CommandType.UUID,
60+
entityType = KMSKeyResponse.class,
61+
description = "The UUID of the KMS key to delete")
62+
private Long id;
63+
64+
/////////////////////////////////////////////////////
65+
/////////////////// Accessors ///////////////////////
66+
/////////////////////////////////////////////////////
67+
68+
public Long getId() {
69+
return id;
70+
}
71+
72+
/////////////////////////////////////////////////////
73+
/////////////// API Implementation///////////////////
74+
/////////////////////////////////////////////////////
75+
76+
@Override
77+
public void execute() {
78+
try {
79+
SuccessResponse response = kmsManager.deleteKMSKey(this);
80+
response.setResponseName(getCommandName());
81+
setResponseObject(response);
82+
} catch (KMSException e) {
83+
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR,
84+
"Failed to delete KMS key: " + e.getMessage());
85+
}
86+
}
87+
88+
@Override
89+
public String getCommandName() {
90+
return s_name;
91+
}
92+
93+
@Override
94+
public long getEntityOwnerId() {
95+
return CallContext.current().getCallingAccount().getId();
96+
}
97+
98+
@Override
99+
public String getEventType() {
100+
return EventTypes.EVENT_KMS_KEK_DELETE;
101+
}
102+
103+
@Override
104+
public String getEventDescription() {
105+
return "deleting KMS key: " + getId();
106+
}
107+
108+
@Override
109+
public ApiCommandResourceType getApiResourceType() {
110+
return ApiCommandResourceType.KmsKey;
111+
}
112+
}
113+

0 commit comments

Comments
 (0)