Skip to content

Feature/huawei cloud scanning - "Added Huawei Cloud Scanning Support for ECS, VPC, and OBS” #2135

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ coverage/
.nyc_output
config.js
package-lock.json
config.js
node_modules/
*.env
*.log
55 changes: 55 additions & 0 deletions collectors/huawei/collector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use strict';

const async = require('async');

// Define API-to-collector mapping (same as in engine.js)
const apiToCollectorMap = {
'ListServersDetails': 'ecs',
'ListVpcs': 'vpcs',
'ListBuckets': 'obs',
'ListUsers': 'iam'
};

module.exports = function(cloudConfig, options, callback) {
const apiCalls = options.api_calls || [];
const collection = {};

// Determine which collectors to run based on API calls
const collectorsToRun = [];
apiCalls.forEach(api => {
if (apiToCollectorMap[api] && !collectorsToRun.includes(apiToCollectorMap[api])) {
collectorsToRun.push(apiToCollectorMap[api]);
}
});

//console.log('DEBUG: Collectors to run in collector.js:', collectorsToRun);

if (!collectorsToRun.length) {
//console.log('INFO: No collectors to run for the given API calls.');
return callback(null, collection);
}

// Load and run each collector
async.eachSeries(collectorsToRun, (collectorName, done) => {
//console.log(`DEBUG: Running collector: ${collectorName}`);
let collector;
try {
collector = require(`./${collectorName}`);
} catch (e) {
console.error(`ERROR: Could not load collector ${collectorName}: ${e.message}`);
return done(e);
}

collector(cloudConfig, (err, data) => {
if (err) {
console.error(`ERROR: Collector ${collectorName} failed: ${err.message}`);
return done(err);
}
collection[collectorName] = data;
done();
});
}, (err) => {
if (err) return callback(err);
callback(null, collection);
});
};
106 changes: 106 additions & 0 deletions collectors/huawei/ecs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
'use strict';

const { EcsClient, ListServersDetailsRequest } = require('@huaweicloud/huaweicloud-sdk-ecs');
const { EvsClient, ShowVolumeRequest } = require('@huaweicloud/huaweicloud-sdk-evs');
const { BasicCredentials } = require('@huaweicloud/huaweicloud-sdk-core');

module.exports = function(cloudConfig, callback) {
//console.log('DEBUG: Starting ECS collection with config:', JSON.stringify(cloudConfig, null, 2));

// Validate required config fields
if (!cloudConfig.accessKeyId || !cloudConfig.secretAccessKey) {
const err = new Error('Missing accessKeyId or secretAccessKey in cloudConfig');
console.error('ERROR: ECS collector validation failed:', err.message);
return callback(err);
}

try {
// Initialize credentials
const credentials = new BasicCredentials()
.withAk(cloudConfig.accessKeyId)
.withSk(cloudConfig.secretAccessKey)
.withProjectId(cloudConfig.projectId || '');

// Create the ECS client
const ecsEndpoint = `https://ecs.${cloudConfig.region}.myhuaweicloud.com`;
//console.log('DEBUG: Using ECS endpoint:', ecsEndpoint);
const ecsClient = EcsClient.newBuilder()
.withCredential(credentials)
.withEndpoint(ecsEndpoint)
.build();

// Create the EVS client
const evsEndpoint = `https://evs.${cloudConfig.region}.myhuaweicloud.com`;
//console.log('DEBUG: Using EVS endpoint:', evsEndpoint);
const evsClient = EvsClient.newBuilder()
.withCredential(credentials)
.withEndpoint(evsEndpoint)
.build();

// Create the request for ListServersDetails
//console.log('DEBUG: Calling ListServersDetails API...');
const request = new ListServersDetailsRequest();
ecsClient.listServersDetails(request)
.then(listServersResult => {
//console.log('DEBUG: Raw listServersDetails response:', JSON.stringify(listServersResult, null, 2));

const servers = listServersResult.servers || [];
//console.log('DEBUG: Found', servers.length, 'ECS instances');

// Process each server and fetch disk encryption status
const serverPromises = servers.map(server => {
const volumes = server['os-extended-volumes:volumes_attached'] || [];
//console.log(`DEBUG: Volumes for server ${server.id}:`, JSON.stringify(volumes, null, 2));

// Fetch encryption status for each volume
const diskPromises = volumes.map(volume => {
const volumeRequest = new ShowVolumeRequest();
volumeRequest.volumeId = volume.id;
return evsClient.showVolume(volumeRequest)
.then(volumeResult => {
// console.log(`DEBUG: ShowVolume response for volume ${volume.id}:`, JSON.stringify(volumeResult, null, 2));
return {
volumeId: volume.id,
encrypted: volumeResult.volume.encrypted || false
};
})
.catch(err => {
console.error(`ERROR: Failed to fetch volume ${volume.id}:`, err.message);
return {
volumeId: volume.id,
encrypted: false // Default to false if the API call fails
};
});
});

return Promise.all(diskPromises).then(diskDetails => {
return {
id: server.id,
name: server.name,
disks: diskDetails
};
});
});

Promise.all(serverPromises)
.then(serverDetails => {
//console.log('DEBUG: ECS instances collected:', serverDetails.length);
const collection = { servers: serverDetails };
callback(null, collection);
})
.catch(err => {
console.error('ERROR: Failed to process ECS instances:', err.message);
callback(err);
});
})
.catch(err => {
console.error('ERROR: Failed to collect ECS instances:', err.message);
console.error('ERROR: Full error details:', JSON.stringify(err, null, 2));
callback(err);
});
} catch (err) {
console.error('ERROR: Failed to initialize ECS/EVS clients:', err.message);
console.error('ERROR: Full error details:', JSON.stringify(err, null, 2));
callback(err);
}
};
8 changes: 8 additions & 0 deletions collectors/huawei/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use strict';

module.exports = {
vpcs: require('./vpcs'),
obs: require('./obs'),
//iam: require('./iam'),
ecs: require('./ecs')
};
43 changes: 43 additions & 0 deletions collectors/huawei/obs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use strict';

const ObsClient = require('esdk-obs-nodejs');

module.exports = async function(cloudConfig, callback) {
const server = `https://obs.${cloudConfig.endpoint.split('.')[1]}.myhuaweicloud.com`;
//console.log('DEBUG: OBS endpoint:', server);
const obsClient = new ObsClient({
access_key_id: cloudConfig.accessKeyId,
secret_access_key: cloudConfig.secretAccessKey,
server: server
});

try {
const listResult = await obsClient.listBuckets();
//console.log('DEBUG: Raw listBuckets response:', JSON.stringify(listResult, null, 2));
const buckets = listResult.InterfaceResult.Buckets || [];
const bucketDetails = await Promise.all(buckets.map(async (bucket) => {
const aclResult = await obsClient.getBucketAcl({ Bucket: bucket.BucketName });
// console.log(`DEBUG: Raw getBucketAcl response for ${bucket.BucketName}:`, JSON.stringify(aclResult, null, 2));
let encryption = null;
try {
const encryptionResult = await obsClient.getBucketEncryption({ Bucket: bucket.BucketName });
//console.log(`DEBUG: Raw getBucketEncryption response for ${bucket.BucketName}:`, JSON.stringify(encryptionResult, null, 2));
encryption = encryptionResult.InterfaceResult || null;
} catch (err) {
//console.log(`DEBUG: No encryption set for ${bucket.BucketName}:`, err.message);
}
return {
name: bucket.BucketName,
creationDate: bucket.CreationDate,
acl: aclResult.InterfaceResult.Grants || [],
encryption: encryption
};
}));

const collection = { buckets: bucketDetails };
callback(null, collection);
} catch (err) {
console.error('ERROR: Failed to collect OBS buckets:', err.message);
callback(err);
}
};
63 changes: 63 additions & 0 deletions collectors/huawei/vpcs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
'use strict';

const { VpcClient, ListVpcsRequest } = require('@huaweicloud/huaweicloud-sdk-vpc');
const { BasicCredentials } = require('@huaweicloud/huaweicloud-sdk-core');

module.exports = function(cloudConfig, callback) {
//console.log('DEBUG: Starting VPC collection with config:', JSON.stringify(cloudConfig, null, 2));

// Validate required config fields
if (!cloudConfig.accessKeyId || !cloudConfig.secretAccessKey) {
const err = new Error('Missing accessKeyId or secretAccessKey in cloudConfig');
console.error('ERROR: VPC collector validation failed:', err.message);
return callback(err);
}

try {
// Initialize credentials
const credentials = new BasicCredentials()
.withAk(cloudConfig.accessKeyId)
.withSk(cloudConfig.secretAccessKey)
.withProjectId(cloudConfig.projectId || '');

// Create the VPC client using the builder pattern
const endpoint = `https://vpc.${cloudConfig.region}.myhuaweicloud.com`;
// console.log('DEBUG: Using VPC endpoint:', endpoint);
const client = VpcClient.newBuilder()
.withCredential(credentials)
.withEndpoint(endpoint)
.build();

// Create the request for ListVpcs
//console.log('DEBUG: Calling ListVpcs API...');
const request = new ListVpcsRequest();
client.listVpcs(request)
.then(listVpcsResult => {
// console.log('DEBUG: Raw listVpcs response:', JSON.stringify(listVpcsResult, null, 2));

const vpcs = listVpcsResult.vpcs || [];
// console.log('DEBUG: Found', vpcs.length, 'VPCs');

const vpcDetails = vpcs.map(vpc => ({
id: vpc.id,
name: vpc.name,
cidr: vpc.cidr,
status: vpc.status,
enable_flow_log: vpc.enable_flow_log || false // For vpcFlowLogsEnabled plugin
}));

// console.log('DEBUG: VPCs collected:', vpcDetails.length);
const collection = { vpcs: vpcDetails };
callback(null, collection);
})
.catch(err => {
console.error('ERROR: Failed to collect VPCs:', err.message);
console.error('ERROR: Full error details:', JSON.stringify(err, null, 2));
callback(err);
});
} catch (err) {
console.error('ERROR: Failed to initialize VPC client:', err.message);
console.error('ERROR: Full error details:', JSON.stringify(err, null, 2));
callback(err);
}
};
35 changes: 35 additions & 0 deletions docs/huawei.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# CloudSploit for Huawei Cloud

## Create IAM Policy for Security Scanning

1. Log into your Huawei Cloud Console and navigate to **Identity and Access Management (IAM)**.
2. Go to **Policies** under **Permissions** and click **Create Custom Policy**.
3. Set the **Policy Name** to `CloudSploitSecurityAudit` and choose **JSON** as the policy configuration mode.
4. Copy and paste the following JSON code into the policy editor. This policy grants read-only permissions for CloudSploit to scan Huawei Cloud resources, including Kubernetes, disks, bandwidth, Elastic IPs, clusters, nodes, and keys. *Note*: Adjust the resource scope based on your tenant or project requirements.

{ "Version": "1.1", "Statement": \[ { "Effect": "Allow", "Action": \[ "ecs:servers:list", "ecs:servers:get", "ecs:securityGroups:list", "ecs:securityGroups:get", "ecs:flavors:list", "ecs:volumes:list", "ecs:volumes:get", "ecs:disks:list", "ecs:disks:get", "ecs:networkInterfaces:list", "ecs:keypairs:list", "ecs:publicIps:list", "ecs:bandwidths:list", "ecs:bandwidths:get", "ecs:eips:list", "ecs:eips:get", "vpc:vpcs:list", "vpc:vpcs:get", "vpc:subnets:list", "vpc:subnets:get", "vpc:securityGroups:list", "vpc:securityGroups:get", "vpc:routes:list", "vpc:bandwidths:list", "vpc:bandwidths:get", "iam:users:list", "iam:roles:list", "iam:groups:list", "iam:policies:list", "iam:permissions:get", "rds:instances:list", "rds:instances:get", "rds:backups:list", "rds:parameters:get", "obs:buckets:list", "obs:buckets:get", "obs:objects:list", "obs:policies:get", "kms:keys:list", "kms:keys:get", "kms:aliases:list", "waf:domains:list", "waf:policies:list", "waf:certificates:list", "elb:loadbalancers:list", "elb:loadbalancers:get", "elb:certificates:list", "elb:healthmonitors:list", "cce:clusters:list", "cce:clusters:get", "cce:nodes:list", "cce:nodes:get", "cce:nodePools:list", "cce:nodePools:get", "cce:jobs:list", "nat:gateways:list", "nat:gateways:get", "nat:snatRules:list", "nat:dnatRules:list", "dns:zones:list", "dns:recordsets:list", "hss:hosts:list", "hss:vulnerabilities:get", "antiddos:resources:list", "antiddos:configurations:get" \], "Resource": \["\*"\] } \] }

5. Click **OK** to save the custom policy.

## Create IAM User for CloudSploit

1. In the Huawei Cloud Console, navigate to **Identity and Access Management (IAM)** > **Users**.
2. Click **Create User**.
3. Set the **User Name** to `CloudSploitScanner` and select **Programmatic Access** as the access type.
4. Under **User Groups**, assign the user to a group or directly attach the `CloudSploitSecurityAudit` policy under **Permissions**.
- If creating a new group, name it `CloudSploitAccessGroup`, and attach the `CloudSploitSecurityAudit` policy to the group.
5. Complete the user creation process by clicking **Create**.
6. After creation, go to **Security Credentials** for the `CloudSploitScanner` user and click **Create Access Key**.
7. Download the **Access Key ID** and **Secret Access Key** (CSV file). Save these securely, as they will not be displayed again.
8. Use the **Access Key ID** and **Secret Access Key** to configure CloudSploit for Huawei Cloud scanning. Refer to CloudSploit’s documentation to input these credentials (e.g., in a `config.js` file or environment variables).

## Notes

- The updated permissions include actions for **Kubernetes (CCE)** (`cce:clusters:list`, `cce:nodes:list`, `cce:nodePools:list`), **disks** (`ecs:disks:list`), **bandwidth** (`ecs:bandwidths:list`, `vpc:bandwidths:list`), **Elastic IPs** (`ecs:eips:list`), **clusters** and **nodes** (`cce:clusters:get`, `cce:nodes:get`), and **keys** (`kms:keys:list`, `ecs:keypairs:list`).
- These permissions cover key Huawei Cloud services for CSPM scanning. Adjust the policy if you need to scan additional services or restrict access to specific resources.
- Ensure the Huawei Cloud region and tenant ID are correctly configured in CloudSploit to align with the scanned resources.
- Regularly rotate the access keys for security and monitor the `CloudSploitScanner` user’s activity via Huawei Cloud’s **Cloud Trace Service (CTS)**.

- Sample Screenshot when scanning the Huawei cloud

- <img width="682" alt="image" src="https://github.com/user-attachments/assets/f244de78-f459-488d-a441-3f2935a88081" />
Loading