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-1File 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 # OptionalFiles are served by the API server at /uploads/*. Path traversal protection is built in.
Tip
The local provider is fine for development and small CTFs. For production deployments with many participants, S3 or GCS is the better choice since it takes load off the platform 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-1Files 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: ...Files are stored with public visibility. The bucket has to be configured to allow public access.
Terraform templates#
The repo ships Terraform modules under deploy/terraform/storage/ that provision an S3 or GCS bucket with the right CORS rules, an IAM user/service account, and a policy scoped to exactly what rCTF needs. Each module writes the generated credentials to a local file so you can paste them straight into your rctf.d/ config.
cd deploy/terraform/storage/mainterraform initterraform apply -var="region=eu-west-3" -var="bucket_name=my-ctf-uploads"Outputs are written next to the module:
aws-rctf-access-key.jsonholds{ access_key_id, secret_access_key }for the dedicatedrctf-bucketIAM user.rctf_iam_user_arnandbucketare Terraform outputs printed to stdout.
Drop the credentials into your config:
uploadProvider: name: uploads/s3 options: bucketName: my-ctf-uploads awsKeyId: <access_key_id from aws-rctf-access-key.json> awsKeySecret: <secret_access_key from aws-rctf-access-key.json> 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"Outputs are written next to the module:
gcs-sa-key.jsonholds the service-account key for the dedicatedrctf-bucketservice account.rctf_sa_emailandbucketare Terraform outputs printed to stdout.
Drop the credentials into your config (paste the contents of gcs-sa-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 gcs-sa-key.jsonThe 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.