Skip to content

Commit 654729d

Browse files
authored
GCS (#2712)
* GCS
1 parent 36af5c9 commit 654729d

File tree

6 files changed

+213
-0
lines changed

6 files changed

+213
-0
lines changed

conf/default/reporting.conf.default

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,21 @@ enabled = no
216216

217217
[browserext]
218218
enabled = no
219+
220+
# Google Cloud Storage - Store all copy of analysis foldr in GCS
221+
[gcs]
222+
enabled = no
223+
# The name of your Google Cloud Storage bucket where files will be uploaded.
224+
bucket_name = your-gcs-bucket-name
225+
226+
# Comma-separated list of DIRECTORY names to exclude from the upload.
227+
# Good examples are 'shots' (contains all screenshots) or 'memory' (for full memory dumps).
228+
exclude_dirs = logs, shots
229+
230+
# Comma-separated list of exact FILENAMES to exclude from the upload.
231+
# Good examples are large report formats you don't need in GCS.
232+
exclude_files =
233+
234+
# The absolute path to your Google Cloud service account JSON key file.
235+
# This file is required for authentication.
236+
credentials_path = data/gcp-credentials.json
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
.. _installation-and-setup:
2+
3+
Installation and Setup
4+
----------------------
5+
6+
Follow these steps to install and configure the GCS reporting module in your CAPE Sandbox environment.
7+
8+
Prerequisites: Google Cloud Setup
9+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10+
11+
Before installing the module, you need to prepare your Google Cloud environment.
12+
13+
1. **Create a GCS Bucket:** If you don't already have one, create a new bucket in the `Google Cloud Console <https://console.cloud.google.com/storage/browser>`_.
14+
15+
2. **Create a Service Account:**
16+
* Go to **IAM & Admin** > **Service Accounts** in the Google Cloud Console.
17+
* Click **Create Service Account** and give it a name (e.g., ``cape-sandbox-uploader``).
18+
* Grant it the **Storage Object Creator** or **Storage Object Admin** role. This permission is necessary to write files to the bucket.
19+
20+
3. **Download JSON Key:**
21+
* After creating the service account, go to its **Keys** tab.
22+
* Click **Add Key** > **Create new key**.
23+
* Select ``JSON`` as the key type and click **Create**. A JSON file will be downloaded.
24+
* **Securely move this JSON file to your CAPE server**, for example, to ``/opt/CAPEv2/data/gcp-credentials.json``.
25+
26+
.. warning::
27+
Do not place the credentials file in a publicly accessible directory.
28+
29+
30+
Module Installation and Configuration
31+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
32+
33+
1. **Install the Python Library:**
34+
The module depends on the official Google Cloud Storage library. Install it within your CAPE virtual environment.
35+
36+
.. note::
37+
Install dependency ``poetry run pip install google-cloud-storage``.
38+
39+
2. **Update Configuration:**
40+
* Edit ``/opt/CAPEv2/conf/reporting.conf``.
41+
* ``[gcs]`` section, enable ``enabled=yes``.
42+
* Set ``bucket_name`` to the name of your GCS bucket.
43+
* Set ``credentials_path`` to the **absolute path** where you saved your service account JSON key file.
44+
45+
3. **Restart CAPE-processor:**
46+
Restart the CAPE service: ``systemctl restart cape-processor`` for the changes to take effect.

docs/book/src/installation/host/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ reference system for the commands examples.
1313
configuration
1414
routing
1515
cloud
16+
gcs

modules/reporting/gcs.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import os
2+
import logging
3+
from lib.cuckoo.common.constants import CUCKOO_ROOT
4+
from lib.cuckoo.common.abstracts import Report
5+
from lib.cuckoo.common.exceptions import CuckooReportError
6+
7+
# Set up a logger for this module
8+
log = logging.getLogger(__name__)
9+
10+
try:
11+
# Import the Google Cloud Storage client library
12+
from google.cloud import storage
13+
from google.oauth2 import service_account
14+
15+
HAVE_GCS = True
16+
except ImportError:
17+
HAVE_GCS = False
18+
19+
20+
class GCS(Report):
21+
"""
22+
Uploads all analysis files to a Google Cloud Storage (GCS) bucket.
23+
"""
24+
25+
# This Report module is not executed by default
26+
order = 9999
27+
28+
def run(self, results):
29+
"""
30+
Run the Report module.
31+
32+
Args:
33+
results (dict): The analysis results dictionary.
34+
"""
35+
# Ensure the required library is installed
36+
if not HAVE_GCS:
37+
log.error(
38+
"Failed to run GCS reporting module: the 'google-cloud-storage' "
39+
"library is not installed. Please run 'poetry run pip install google-cloud-storage'."
40+
)
41+
return
42+
43+
# Read configuration options from gcs.conf
44+
# Read configuration options from gcs.conf and validate them
45+
bucket_name = self.options.get("bucket_name")
46+
if not bucket_name:
47+
raise CuckooReportError("GCS bucket_name is not configured in reporting.conf -> gcs")
48+
49+
credentials_path_str = self.options.get("credentials_path")
50+
if not credentials_path_str:
51+
raise CuckooReportError("GCS credentials_path is not configured in reporting.conf -> gcs")
52+
53+
credentials_path = os.path.join(CUCKOO_ROOT, credentials_path_str)
54+
if not os.path.isfile(credentials_path):
55+
raise CuckooReportError(
56+
"GCS credentials_path '%s' is invalid or file does not exist in reporting.conf -> gcs", credentials_path
57+
)
58+
59+
# Read the exclusion lists, defaulting to empty strings
60+
exclude_dirs_str = self.options.get("exclude_dirs", "")
61+
exclude_files_str = self.options.get("exclude_files", "")
62+
63+
# --- NEW: Parse the exclusion strings into sets for efficient lookups ---
64+
# The `if item.strip()` ensures we don't have empty strings from trailing commas
65+
exclude_dirs = {item.strip() for item in exclude_dirs_str.split(",") if item.strip()}
66+
exclude_files = {item.strip() for item in exclude_files_str.split(",") if item.strip()}
67+
68+
if exclude_dirs:
69+
log.debug("GCS reporting will exclude directories: %s", exclude_dirs)
70+
if exclude_files:
71+
log.debug("GCS reporting will exclude files: %s", exclude_files)
72+
73+
try:
74+
# --- Authentication ---
75+
log.debug("Authenticating with Google Cloud Storage...")
76+
credentials = service_account.Credentials.from_service_account_file(credentials_path)
77+
storage_client = storage.Client(credentials=credentials)
78+
bucket = storage_client.bucket(bucket_name)
79+
80+
# Check if the bucket exists and is accessible
81+
if not bucket.exists():
82+
raise CuckooReportError(
83+
"The specified GCS bucket '%s' does not exist or you don't have permission to access it.", bucket_name
84+
)
85+
86+
# --- File Upload ---
87+
# Use the analysis ID as a "folder" in the bucket
88+
analysis_id = results.get("info", {}).get("id")
89+
if not analysis_id:
90+
raise CuckooReportError("Could not get analysis ID from results.")
91+
92+
log.debug("Uploading files for analysis ID %d to GCS bucket '%s'", analysis_id, bucket_name)
93+
94+
# self.analysis_path is the path to the analysis results directory
95+
# e.g., /opt/cape/storage/analyses/123/
96+
source_directory = self.analysis_path
97+
98+
for root, dirs, files in os.walk(source_directory):
99+
# We modify 'dirs' in-place to prevent os.walk from descending into them.
100+
# This is the most efficient way to skip entire directory trees.
101+
dirs[:] = [d for d in dirs if d not in exclude_dirs]
102+
103+
for filename in files:
104+
# --- NEW: File Exclusion Logic ---
105+
if filename in exclude_files:
106+
log.debug("Skipping excluded file: %s", os.path.join(root, filename))
107+
continue # Skip to the next file
108+
109+
local_path = os.path.join(root, filename)
110+
relative_path = os.path.relpath(local_path, source_directory)
111+
blob_name = f"{analysis_id}/{relative_path}"
112+
113+
log.debug("Uploading '%s' to '%s'", local_path, blob_name)
114+
115+
blob = bucket.blob(blob_name)
116+
blob.upload_from_filename(local_path)
117+
118+
log.info("Successfully uploaded files for analysis %d to GCS.", analysis_id)
119+
120+
except Exception as e:
121+
raise CuckooReportError("Failed to upload report to GCS: %s", str(e))

web/web/middleware/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
from .custom_auth import CustomAuth # noqa
22
from .db_transaction import DBTransactionMiddleware # noqa
3+
from .disable_auth_in_local import DisableAllauthMiddleware # noqa
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from allauth.account.middleware import AccountMiddleware
2+
3+
4+
class DisableAllauthMiddleware:
5+
def __init__(self, get_response):
6+
self.get_response = get_response
7+
# Instantiate the real AllAuth middleware that we will be wrapping.
8+
self.allauth_middleware = AccountMiddleware(get_response)
9+
10+
def __call__(self, request):
11+
# Get the remote IP address, handling proxies.
12+
remote_ip = request.META.get("HTTP_X_FORWARDED_FOR", request.META.get("REMOTE_ADDR", "")).split(",")[0].strip()
13+
14+
# Define the IPs for which we want to skip the middleware.
15+
local_ips = ["127.0.0.1", "::1", "localhost"]
16+
17+
if remote_ip in local_ips:
18+
# The IP is local. Skip the AllAuth middleware by calling
19+
# the next middleware/view in the chain directly.
20+
print("Skipping AllAuth middleware for local request.") # Optional: for debugging
21+
response = self.get_response(request)
22+
return response
23+
else:
24+
# The IP is not local. Execute the AllAuth middleware as usual
25+
# by calling its __call__ method.
26+
return self.allauth_middleware(request)

0 commit comments

Comments
 (0)