Skip to content

Commit 7afdd31

Browse files
committed
integrate volume encryption with kms
1 parent b2aea16 commit 7afdd31

File tree

46 files changed

+1671
-793
lines changed

Some content is hidden

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

46 files changed

+1671
-793
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ public class EventTypes {
278278
public static final String EVENT_KMS_KEK_ROTATE = "KMS.KEK.ROTATE";
279279
public static final String EVENT_KMS_KEK_DELETE = "KMS.KEK.DELETE";
280280
public static final String EVENT_KMS_HEALTH_CHECK = "KMS.HEALTH.CHECK";
281+
public static final String EVENT_VOLUME_MIGRATE_TO_KMS = "VOLUME.MIGRATE.TO.KMS";
281282

282283
// Account events
283284
public static final String EVENT_ACCOUNT_ENABLE = "ACCOUNT.ENABLE";

api/src/main/java/com/cloud/storage/Volume.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,14 @@ enum Event {
275275

276276
void setPassphraseId(Long id);
277277

278+
Long getKmsKeyId();
279+
280+
void setKmsKeyId(Long id);
281+
282+
Long getKmsWrappedKeyId();
283+
284+
void setKmsWrappedKeyId(Long id);
285+
278286
String getEncryptFormat();
279287

280288
void setEncryptFormat(String encryptFormat);
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package org.apache.cloudstack.api.command.admin.kms;
18+
19+
import com.cloud.user.Account;
20+
import org.apache.cloudstack.acl.RoleType;
21+
import org.apache.cloudstack.api.APICommand;
22+
import org.apache.cloudstack.api.ApiCommandResourceType;
23+
import org.apache.cloudstack.api.ApiConstants;
24+
import org.apache.cloudstack.api.ApiErrorCode;
25+
import org.apache.cloudstack.api.BaseAsyncCmd;
26+
import org.apache.cloudstack.api.Parameter;
27+
import org.apache.cloudstack.api.ServerApiException;
28+
import org.apache.cloudstack.api.response.AsyncJobResponse;
29+
import org.apache.cloudstack.api.response.DomainResponse;
30+
import org.apache.cloudstack.api.response.ZoneResponse;
31+
import org.apache.cloudstack.framework.kms.KMSException;
32+
import org.apache.cloudstack.kms.KMSManager;
33+
34+
import javax.inject.Inject;
35+
36+
@APICommand(name = "migrateVolumesToKMS",
37+
description = "Migrates passphrase-based volumes to KMS (admin only)",
38+
responseObject = AsyncJobResponse.class,
39+
since = "4.23.0",
40+
authorized = {RoleType.Admin},
41+
requestHasSensitiveInfo = false,
42+
responseHasSensitiveInfo = false)
43+
public class MigrateVolumesToKMSCmd extends BaseAsyncCmd {
44+
private static final String s_name = "migratevolumestokmsresponse";
45+
46+
@Inject
47+
private KMSManager kmsManager;
48+
49+
/////////////////////////////////////////////////////
50+
//////////////// API parameters /////////////////////
51+
/////////////////////////////////////////////////////
52+
53+
@Parameter(name = ApiConstants.ZONE_ID,
54+
required = true,
55+
type = CommandType.UUID,
56+
entityType = ZoneResponse.class,
57+
description = "Zone ID")
58+
private Long zoneId;
59+
60+
@Parameter(name = ApiConstants.ACCOUNT,
61+
type = CommandType.STRING,
62+
description = "Migrate volumes for specific account")
63+
private String accountName;
64+
65+
@Parameter(name = ApiConstants.DOMAIN_ID,
66+
type = CommandType.UUID,
67+
entityType = DomainResponse.class,
68+
description = "Domain ID")
69+
private Long domainId;
70+
71+
/////////////////////////////////////////////////////
72+
/////////////////// Accessors ///////////////////////
73+
/////////////////////////////////////////////////////
74+
75+
public Long getZoneId() {
76+
return zoneId;
77+
}
78+
79+
public String getAccountName() {
80+
return accountName;
81+
}
82+
83+
public Long getDomainId() {
84+
return domainId;
85+
}
86+
87+
/////////////////////////////////////////////////////
88+
/////////////// API Implementation///////////////////
89+
/////////////////////////////////////////////////////
90+
91+
@Override
92+
public void execute() {
93+
try {
94+
kmsManager.migrateVolumesToKMS(this);
95+
} catch (KMSException e) {
96+
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR,
97+
"Failed to migrate volumes to KMS: " + e.getMessage());
98+
}
99+
}
100+
101+
@Override
102+
public String getCommandName() {
103+
return s_name;
104+
}
105+
106+
@Override
107+
public long getEntityOwnerId() {
108+
return Account.ACCOUNT_ID_SYSTEM;
109+
}
110+
111+
@Override
112+
public String getEventType() {
113+
return com.cloud.event.EventTypes.EVENT_VOLUME_MIGRATE_TO_KMS;
114+
}
115+
116+
@Override
117+
public String getEventDescription() {
118+
return "Migrating volumes to KMS for zone: " + _uuidMgr.getUuid(ZoneResponse.class, zoneId);
119+
}
120+
121+
@Override
122+
public ApiCommandResourceType getApiResourceType() {
123+
return ApiCommandResourceType.Zone;
124+
}
125+
126+
@Override
127+
public Long getApiResourceId() {
128+
return zoneId;
129+
}
130+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package org.apache.cloudstack.api.command.admin.kms;
18+
19+
import com.cloud.user.Account;
20+
import org.apache.cloudstack.acl.RoleType;
21+
import org.apache.cloudstack.api.APICommand;
22+
import org.apache.cloudstack.api.ApiCommandResourceType;
23+
import org.apache.cloudstack.api.ApiConstants;
24+
import org.apache.cloudstack.api.ApiErrorCode;
25+
import org.apache.cloudstack.api.BaseAsyncCmd;
26+
import org.apache.cloudstack.api.Parameter;
27+
import org.apache.cloudstack.api.ServerApiException;
28+
import org.apache.cloudstack.api.response.AsyncJobResponse;
29+
import org.apache.cloudstack.api.response.KMSKeyResponse;
30+
import org.apache.cloudstack.framework.kms.KMSException;
31+
import org.apache.cloudstack.kms.KMSManager;
32+
33+
import javax.inject.Inject;
34+
35+
@APICommand(name = "rotateKMSKey",
36+
description = "Rotates KEK by creating new version and scheduling gradual re-encryption (admin only)",
37+
responseObject = AsyncJobResponse.class,
38+
since = "4.23.0",
39+
authorized = {RoleType.Admin},
40+
requestHasSensitiveInfo = false,
41+
responseHasSensitiveInfo = false)
42+
public class RotateKMSKeyCmd extends BaseAsyncCmd {
43+
private static final String s_name = "rotatekmskeyresponse";
44+
45+
@Inject
46+
private KMSManager kmsManager;
47+
48+
/////////////////////////////////////////////////////
49+
//////////////// API parameters /////////////////////
50+
/////////////////////////////////////////////////////
51+
52+
@Parameter(name = ApiConstants.ID,
53+
required = true,
54+
type = CommandType.UUID,
55+
entityType = KMSKeyResponse.class,
56+
description = "KMS Key UUID to rotate")
57+
private Long id;
58+
59+
@Parameter(name = ApiConstants.KEY_BITS,
60+
type = CommandType.INTEGER,
61+
description = "Key size for new KEK (default: same as current)")
62+
private Integer keyBits;
63+
64+
/////////////////////////////////////////////////////
65+
/////////////////// Accessors ///////////////////////
66+
/////////////////////////////////////////////////////
67+
68+
public Long getId() {
69+
return id;
70+
}
71+
72+
public Integer getKeyBits() {
73+
return keyBits;
74+
}
75+
76+
/////////////////////////////////////////////////////
77+
/////////////// API Implementation///////////////////
78+
/////////////////////////////////////////////////////
79+
80+
@Override
81+
public void execute() {
82+
try {
83+
kmsManager.rotateKMSKey(this);
84+
} catch (KMSException e) {
85+
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR,
86+
"Failed to rotate KMS key: " + e.getMessage());
87+
}
88+
}
89+
90+
@Override
91+
public String getCommandName() {
92+
return s_name;
93+
}
94+
95+
@Override
96+
public long getEntityOwnerId() {
97+
return Account.ACCOUNT_ID_SYSTEM;
98+
}
99+
100+
@Override
101+
public String getEventType() {
102+
return com.cloud.event.EventTypes.EVENT_KMS_KEK_ROTATE;
103+
}
104+
105+
@Override
106+
public String getEventDescription() {
107+
return "Rotating KMS key: " + _uuidMgr.getUuid(KMSKeyResponse.class, id);
108+
}
109+
110+
@Override
111+
public ApiCommandResourceType getApiResourceType() {
112+
return ApiCommandResourceType.KmsKey;
113+
}
114+
115+
@Override
116+
public Long getApiResourceId() {
117+
return id;
118+
}
119+
}

api/src/main/java/org/apache/cloudstack/api/command/user/kms/CreateKMSKeyCmd.java

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
requestHasSensitiveInfo = false,
4848
responseHasSensitiveInfo = false)
4949
public class CreateKMSKeyCmd extends BaseCmd implements UserCmd {
50-
private static final String s_name = "createkmskeyresponse";
5150

5251
@Inject
5352
private KMSManager kmsManager;
@@ -70,7 +69,7 @@ public class CreateKMSKeyCmd extends BaseCmd implements UserCmd {
7069
@Parameter(name = ApiConstants.PURPOSE,
7170
required = true,
7271
type = CommandType.STRING,
73-
description = "Purpose of the key: VOLUME_ENCRYPTION, TLS_CERT, CONFIG_SECRET")
72+
description = "Purpose of the key: volume, tls")
7473
private String purpose;
7574

7675
@Parameter(name = ApiConstants.ZONE_ID,
@@ -144,11 +143,6 @@ public void execute() throws ResourceAllocationException {
144143
}
145144
}
146145

147-
@Override
148-
public String getCommandName() {
149-
return s_name;
150-
}
151-
152146
@Override
153147
public long getEntityOwnerId() {
154148
Account caller = CallContext.current().getCallingAccount();
@@ -163,4 +157,3 @@ public ApiCommandResourceType getApiResourceType() {
163157
return ApiCommandResourceType.KmsKey;
164158
}
165159
}
166-

api/src/main/java/org/apache/cloudstack/api/command/user/kms/DeleteKMSKeyCmd.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545
requestHasSensitiveInfo = false,
4646
responseHasSensitiveInfo = false)
4747
public class DeleteKMSKeyCmd extends BaseAsyncCmd implements UserCmd {
48-
private static final String s_name = "deletekmskeyresponse";
4948

5049
@Inject
5150
private KMSManager kmsManager;
@@ -85,11 +84,6 @@ public void execute() {
8584
}
8685
}
8786

88-
@Override
89-
public String getCommandName() {
90-
return s_name;
91-
}
92-
9387
@Override
9488
public long getEntityOwnerId() {
9589
return CallContext.current().getCallingAccount().getId();
@@ -110,4 +104,3 @@ public ApiCommandResourceType getApiResourceType() {
110104
return ApiCommandResourceType.KmsKey;
111105
}
112106
}
113-

api/src/main/java/org/apache/cloudstack/api/command/user/kms/ListKMSKeysCmd.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public class ListKMSKeysCmd extends BaseListAccountResourcesCmd implements UserC
5959

6060
@Parameter(name = ApiConstants.PURPOSE,
6161
type = CommandType.STRING,
62-
description = "Filter by purpose: VOLUME_ENCRYPTION, TLS_CERT, CONFIG_SECRET")
62+
description = "Filter by purpose: volume, tls")
6363
private String purpose;
6464

6565
@Parameter(name = ApiConstants.ZONE_ID,
@@ -109,4 +109,3 @@ public String getCommandName() {
109109
return s_name;
110110
}
111111
}
112-

api/src/main/java/org/apache/cloudstack/api/command/user/kms/UpdateKMSKeyCmd.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
requestHasSensitiveInfo = false,
4545
responseHasSensitiveInfo = false)
4646
public class UpdateKMSKeyCmd extends BaseAsyncCmd implements UserCmd {
47-
private static final String s_name = "updatekmskeyresponse";
4847

4948
@Inject
5049
private KMSManager kmsManager;
@@ -111,11 +110,6 @@ public void execute() {
111110
}
112111
}
113112

114-
@Override
115-
public String getCommandName() {
116-
return s_name;
117-
}
118-
119113
@Override
120114
public long getEntityOwnerId() {
121115
return CallContext.current().getCallingAccount().getId();

api/src/main/java/org/apache/cloudstack/api/command/user/volume/CreateVolumeCmd.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.apache.cloudstack.api.command.user.UserCmd;
3131
import org.apache.cloudstack.api.response.DiskOfferingResponse;
3232
import org.apache.cloudstack.api.response.DomainResponse;
33+
import org.apache.cloudstack.api.response.KMSKeyResponse;
3334
import org.apache.cloudstack.api.response.ProjectResponse;
3435
import org.apache.cloudstack.api.response.SnapshotResponse;
3536
import org.apache.cloudstack.api.response.UserVmResponse;
@@ -109,6 +110,13 @@ public class CreateVolumeCmd extends BaseAsyncCreateCustomIdCmd implements UserC
109110
description = "The ID of the Instance; to be used with snapshot Id, Instance to which the volume gets attached after creation")
110111
private Long virtualMachineId;
111112

113+
@Parameter(name = ApiConstants.KMS_KEY_ID,
114+
type = CommandType.UUID,
115+
entityType = KMSKeyResponse.class,
116+
description = "ID of the KMS Key for volume encryption (required if encryption enabled for zone)",
117+
since = "4.23.0")
118+
private Long kmsKeyId;
119+
112120
/////////////////////////////////////////////////////
113121
/////////////////// Accessors ///////////////////////
114122
/////////////////////////////////////////////////////
@@ -169,6 +177,10 @@ public Long getVirtualMachineId() {
169177
return virtualMachineId;
170178
}
171179

180+
public Long getKmsKeyId() {
181+
return kmsKeyId;
182+
}
183+
172184
/////////////////////////////////////////////////////
173185
/////////////// API Implementation///////////////////
174186
/////////////////////////////////////////////////////

0 commit comments

Comments
 (0)