Upload providers
Configure file storage with local filesystem, Amazon S3, or Google Cloud Storage.
Upload providers handle storage for both challenge file attachments and team avatars. Both share the same provider, so anything you configure here applies to both.
Warning (v2 needs delete permissions)
Unlike rCTF v1, the v2 upload provider needs permission to delete objects, not just upload them. Avatar replacement and the admin-side file deletion flows both depend on it. If you reuse a v1 IAM policy that only grants s3:GetObject / s3:PutObject, add s3:DeleteObject to it (or the GCS equivalent storage.objects.delete).
Configuration
uploadProvider: name: uploads/s3 options: bucketName: my-ctf-uploads awsKeyId: AKIAIOSFODNN7EXAMPLE awsKeySecret: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY awsRegion: us-east-1You can keep storage credentials out of config files by using environment variables. Set RCTF_UPLOAD_PROVIDER to a provider such as uploads/s3, then provide the settings through the variables listed below. Environment values override the config file.
File handling
- Files are stored by their SHA256 hash, so uploading the same file content twice doesn’t create a duplicate.
- All uploaded files are served with immutable cache headers (
max-age=31536000). - Team avatars are resized to 256x256 pixels and converted to WebP format before upload. The maximum avatar size comes from the
maxAvatarSizeconfig option (default 1 MB).
Providers
Stores files on the local filesystem. This is the default provider.
uploadProvider: name: uploads/local options: uploadDirectory: /path/to/uploads # Optional| Option | Default | Description |
|---|---|---|
uploadDirectory |
{cwd}/uploads/ |
Directory for file storage |
Files are served by the API server at /uploads/*. Path traversal protection is built in.
Tip
The local provider works well for development and small events. S3 or GCS is a better fit when many participants will download large challenge files, since those downloads no longer pass through the rCTF server.
Stores files in an Amazon S3 bucket.
uploadProvider: name: uploads/s3 options: bucketName: my-ctf-uploads awsKeyId: AKIAIOSFODNN7EXAMPLE awsKeySecret: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY awsRegion: us-east-1| Option | Environment Variable | Description |
|---|---|---|
bucketName |
RCTF_S3_BUCKET |
S3 bucket name |
awsKeyId |
RCTF_S3_KEY_ID |
AWS access key ID |
awsKeySecret |
RCTF_S3_KEY_SECRET |
AWS secret access key |
awsRegion |
RCTF_S3_REGION |
AWS region |
Files are stored with public-read ACL and attachment content disposition. The bucket has to allow public reads.
Stores files in a Google Cloud Storage bucket.
uploadProvider: name: uploads/gcs options: projectId: my-gcp-project bucketName: my-ctf-uploads credentials: type: service_account project_id: my-gcp-project private_key_id: ... private_key: ... client_email: ...| Option | Environment Variable | Description |
|---|---|---|
projectId |
RCTF_GCS_PROJECT_ID |
GCP project ID |
bucketName |
RCTF_GCS_BUCKET |
GCS bucket name |
credentials |
RCTF_GCS_CREDENTIALS |
GCP credentials object (env var accepts JSON string) |
Files are stored with public visibility. The bucket has to be configured to allow public access.
Terraform templates
Terraform modules under deploy/terraform/storage/ can create an S3 or GCS bucket, its CORS rules, and credentials limited to the permissions rCTF needs. Each module exposes the generated credentials as sensitive Terraform outputs for use in rctf.d/.
cd deploy/terraform/storage/mainterraform initterraform apply -var="region=eu-west-3" -var="bucket_name=my-ctf-uploads"Read the credentials for the dedicated rctf-bucket IAM user from the sensitive outputs (rctf_iam_user_arn and bucket are printed to stdout on apply):
terraform output -raw access_key_idterraform output -raw secret_access_keyDrop the credentials into your config:
uploadProvider: name: uploads/s3 options: bucketName: my-ctf-uploads awsKeyId: <access_key_id output> awsKeySecret: <secret_access_key output> awsRegion: eu-west-3The module sets up CORS (GET, HEAD from any origin by default, which you can override with -var="cors_allowed_origins=[\"https://ctf.example.com\"]") and grants the IAM user s3:GetObject, s3:PutObject, s3:DeleteObject, plus the matching ACL actions and s3:ListBucket.
cd deploy/terraform/storage/gcsterraform initterraform apply -var="project_id=my-gcp-project" -var="region=europe-west1" -var="bucket_name=my-ctf-uploads"Read the key for the dedicated rctf-bucket service account from the sensitive output (rctf_sa_email and bucket are printed to stdout on apply):
terraform output -raw service_account_keyDrop the credentials into your config (paste the key JSON under credentials):
uploadProvider: name: uploads/gcs options: projectId: my-gcp-project bucketName: my-ctf-uploads credentials: type: service_account project_id: my-gcp-project # ...rest of the service_account_key outputThe module sets up CORS (GET, HEAD from any origin by default, which you can override with -var="cors_allowed_origins=[\"https://ctf.example.com\"]") and grants the service account a custom role with storage.objects.create, storage.objects.get, storage.objects.list, and storage.objects.delete.