Compare commits

..

No commits in common. "master" and "latest" have entirely different histories.

4 changed files with 64 additions and 57 deletions

View File

@ -35,23 +35,6 @@ docker-compose up
``` ```
### Running Backend & Frontend Separately (Docker + Kubernetes) ### Running Backend & Frontend Separately (Docker + Kubernetes)
### Prerequisites
Before you begin, make sure you have:
- A Kubernetes cluster up and running.
- `kubectl` installed and configured to communicate with your cluster.
- Proper permissions to create Deployments, Services, Persistent Volumes, and other Kubernetes resources.
### Overview of Components
- `frontend_deployment.yaml`: Deployment for the SafeShare frontend.
- `frontend_service.yaml`: Service exposing the SafeShare frontend.
- `backend_deployment.yaml`: Deployment for the SafeShare backend.
- `backend_service.yaml`: Service exposing the SafeShare backend.
- `redis_deployment.yaml`: Setup for Redis with persistent storage.
```bash ```bash
# Run frontend using docker-compose # Run frontend using docker-compose
docker compose -f docker-compose-frontend.yml up -d docker compose -f docker-compose-frontend.yml up -d
@ -128,16 +111,4 @@ The application provides an automated cleaning service for the files that are up
The files are deleted after a certain period of time (ttl). The files are deleted after a certain period of time (ttl).
The time period can be set in the `.env` file. (`TRASH_TIMEOUT`) The time period can be set in the `.env` file. (`TRASH_TIMEOUT`)
This service will periodically check redis for the files that have expired and delete them from the server's storage. This service will periodically check redis for the files that have expired and delete them from the server's storage.
## Test Files
Theoretically, the application can handle any type of file. However, we have tested the application with the following file types:
- Text-based `.txt`, `.py`, etc.
- Image-based `.png`, `.jpg`, etc.
- Video-based `.mp4`, etc.
- Executable `.exe`, etc.
The Virus file used for testing is generated with [metasploit](https://www.metasploit.com/) (msfvenom). ([VirusTotal Report](https://www.virustotal.com/gui/file/2fd0c13298f99d5ae10765ef65e1667e205e932376396d92e4343468abe0c541/detection))
It is a simple reverse shell payload that connects to the attacker's machine, Harmless since it is not connected to the attacker's machine. But **DO NOT** run it on your machine.

View File

@ -0,0 +1,30 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: safeshare-frontend
labels:
app: safeshare
tier: frontend
spec:
replicas: 1
selector:
matchLabels:
app: safeshare
tier: frontend
template:
metadata:
labels:
app: safeshare
tier: frontend
spec:
containers:
- name: safeshare-frontend
image: amusement3004/safeshare-frontend
imagePullPolicy: IfNotPresent
env: # Add these environment variables
- name: REACT_APP_API_HOST
value: safeshare-backend-service # The name of the backend service
- name: REACT_APP_API_PORT
value: "8000" # The port the backend service listens on
ports:
- containerPort: 3000

View File

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: safeshare-frontend-service
labels:
app: safeshare
tier: frontend
spec:
type: NodePort
ports:
- port: 3000
nodePort: 32000 # Optional: This is an example. Kubernetes will assign an available port if you don't specify this.
selector:
app: safeshare
tier: frontend

View File

@ -65,9 +65,9 @@ class ManageItemsView(APIView):
timeout_timer.cancel() timeout_timer.cancel()
def _save_file(self, file, ttl, responses): def _save_file(self, file, ttl, responses):
generated_uuid = uuid.uuid4().hex # Generate a UUID key = uuid.uuid4().hex
filename = file.name # Get the original filename filename = file.name
save_path = os.path.join(settings.MEDIA_ROOT, generated_uuid) # Use the UUID as the new filename save_path = os.path.join(settings.MEDIA_ROOT, filename)
hasher = hashlib.sha256() hasher = hashlib.sha256()
with open(save_path, 'wb') as destination: with open(save_path, 'wb') as destination:
@ -95,7 +95,7 @@ class ManageItemsView(APIView):
logger.warning(f'File {filename} is infected with a virus') logger.warning(f'File {filename} is infected with a virus')
return return
# Determine the MIME type of the file using python-magic # Determine the MIME type of the file using python-magic
try: try:
file_type = magic.Magic() file_type = magic.Magic()
mime_type = file_type.from_file(save_path) mime_type = file_type.from_file(save_path)
@ -103,22 +103,21 @@ class ManageItemsView(APIView):
logger.warning(f'Error detecting MIME type: {str(e)}') logger.warning(f'Error detecting MIME type: {str(e)}')
mime_type = 'application/octet-stream' mime_type = 'application/octet-stream'
# Store the file path, generated UUID, original filename, MIME type, and other information in the cache # Store the file path, filename, MIME type, and other information in the cache
cache.set(generated_uuid, { cache.set(key, {
'filename': filename, # Original filename 'filename': filename,
'generated_uuid': generated_uuid, # Generated UUID
'path': save_path, 'path': save_path,
'mime_type': mime_type, 'mime_type': mime_type, # Store the MIME type
}, timeout=ttl) }, timeout=ttl)
response = { response = {
'key': generated_uuid, # Return the generated UUID 'key': key,
'filename': filename, # Return the original filename 'filename': filename,
'mime_type': mime_type, 'mime_type': mime_type, # Include the MIME type in the response
'msg': f"{generated_uuid} successfully set to {filename} with TTL {ttl} seconds", 'msg': f"{key} successfully set to {filename} with TTL {ttl} seconds",
} }
responses.append(response) responses.append(response)
logger.info(f'File {filename} successfully saved to cache with key {generated_uuid} and TTL {ttl} seconds') logger.info(f'File {filename} successfully saved to cache with key {key} and TTL {ttl} seconds')
class ManageItemView(APIView): class ManageItemView(APIView):
@ -148,8 +147,7 @@ class ManageItemView(APIView):
response = HttpResponse(file_data, content_type=mime_type) response = HttpResponse(file_data, content_type=mime_type)
# Set the Content-Disposition with the original filename # Set the Content-Disposition with the original filename
original_filename = value.get('filename', 'file') # Get the original filename from the cache response['Content-Disposition'] = f'attachment; filename="{quote(os.path.basename(file_path))}"'
response['Content-Disposition'] = f'attachment; filename="{quote(original_filename)}"'
logger.info(f'File {file_path} successfully retrieved from cache with key {key}') logger.info(f'File {file_path} successfully retrieved from cache with key {key}')
return response return response
@ -176,13 +174,7 @@ PRIVATE_IPS_PREFIX = ('10.', '172.', '192.')
def get_client_ip(request): def get_client_ip(request):
""" """
Get the client's IP address from the request. Get Client's IP
This function extracts the client's IP address from various request headers,
taking into account possible proxy servers.
:param request: The HTTP request object.
:return: The client's IP address.
""" """
remote_address = request.META.get('HTTP_X_FORWARDED_FOR') or request.META.get('REMOTE_ADDR') remote_address = request.META.get('HTTP_X_FORWARDED_FOR') or request.META.get('REMOTE_ADDR')
ip = remote_address ip = remote_address
@ -191,9 +183,8 @@ def get_client_ip(request):
if x_forwarded_for: if x_forwarded_for:
proxies = x_forwarded_for.split(',') proxies = x_forwarded_for.split(',')
while proxies and proxies[0].startswith(PRIVATE_IPS_PREFIX): while len(proxies) > 0 and proxies[0].startswith(PRIVATE_IPS_PREFIX):
proxies.pop(0) proxies.pop(0)
if proxies: if len(proxies) > 0:
ip = proxies[0] ip = proxies[0]
return ip return ip