Compare commits
No commits in common. "master" and "latest" have entirely different histories.
29
README.md
29
README.md
|
@ -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
|
||||||
|
@ -129,15 +112,3 @@ 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.
|
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue