CTFd Provider
CTFd is an open-source CTFd platform famous for its simplicity and extensibility. It has been used for various Capture The Flag (CTF) events, originally for the CSAW, later by others such the BreizhCTF.
One of CTFer approach is seeing the CTFd resources as simply as objects with CRUD operations (e.g. challenges). From this simplification arise a possibility: managing them as Terraform resources, which we did with the Terraform Provider for CTFd. Nevertheless, due to the limitations of Terraform and the necessity to bring reproducibility to Capture The Flag events, as stated by multiple organizers, we had to go further and give it an additional predicate: managing them as code through programming language.
Pulumi is an Infrastructure-as-Code (IaC) and Free Open-Source Software (FOSS) tool that is a first-class opportunity for this.
To sum it up, you can manage a Capture The Flag event based upon CTFd in multiple programming languages.
Use Cases
Users of the CTFd provider can:
- Drop unstable scripts and tooling to manage CTF resources in favor of this SDK.
- Share their configuration to public through a standardized API (common and encouraged after a Capture The Flag event), such that it could be replayed later for training and/or teaching.
- Plug in other providers (e.g. Kubernetes) to provision infrastucture for the platform and challenges.
Provider
To use the CTFd Provider you will have to configure it and use it to create resources: it can’t infer the CTFd url and credentials.
import * as ctfd from '@ctfer-io/pulumi-ctfd';
// Create provider
let pv = new ctfd.Provider('ctfd-fine-grained', {
url: 'https://my-ctf.lan',
apiKey: 'ctfd_xxx', // please do not hardcode your credentials/api keys
});
// Create resources with the custom provider
let ch = new ctfd.Challenge('some-challenge', {
name: 'My Challenge',
category: 'misc',
description: '...',
value: 500,
}, { provider: pv });
import * as ctfd from '@ctfer-io/pulumi-ctfd';
// Stack configuration, other resources, etc.
// ...
// Create provider
let pv = new ctfd.Provider('ctfd-fine-grained', {
url: 'https://my-ctf.lan',
apiKey: 'ctfd_xxx', // please do not hardcode your credentials/api keys
});
// Create resources with the custom provider
let ch = new ctfd.Challenge('some-challenge', {
name: 'My Challenge',
category: 'misc',
description: '...',
value: 500,
}, { provider: pv });
// Other resources, export, etc.
// ...
package main
import (
"github.com/ctfer-io/pulumi-ctfd/sdk/go/ctfd"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
// Stack configuration, other resources, etc.
// ...
// Create provider
pv, err := ctfd.NewProvider(ctx, "ctfd-fine-grained", &ctfd.ProviderArgs{
Url: pulumi.String("https://my-ctf.lan"),
ApiKey: pulumi.String("ctfd_xxx"), // please do not hardcode your credentials/api keys
})
if err != nil {
return err
}
// Build resource options to use it
opts := []pulumi.ResourceOption{
pulumi.Provider(pv),
}
// Create resources with the custom provider
_, err = ctfd.NewChallenge(ctx, "some-challenge", &ctfd.ChallengeArgs{
Name: pulumi.String("My Challenge"),
Category: pulumi.String("misc"),
Description: pulumi.String("..."),
Value: pulumi.Int(500),
}, opts...)
if err != nil {
return err
}
// Other resources, export, etc.
// ...
return nil
})
}
import ctfer-io_pulumi_ctfd as ctfd
import pulumi
# Stack configuration, other resources, etc.
# ...
# Create provider
pv = ctfd.Provider("ctfd-fine-grained", url="https://my-ctf.lan", api_key="ctfd_xxx") # please do not hardcode your credentials/api keys
# Create resources with the custom provider
ch = ctfd.Challenge("some-challenge", name="My Challenge", category="misc", description="...", value=500, opts=pulumi.ResourceOptions(provider=pv))
# Other resources, export, etc.
# ...
using System.Collections.Generic;
using System.Threading.Tasks;
using Pulumi;
using CTFerio.Ctfd;
class Program
{
static Task Main() =>
Deployment.Run(() => {
// Stack configuration, other resources, etc.
// ...
// Create provider
var pv = new Ctfd.Provider("ctfd-fine-grained", new Ctfd.ProviderArgs{
Url = "https://my-ctf.lan",
ApiKey = "ctfd_xxx" // please do not hardcode your credentials/api keys
});
// Create resources with the custom provider
var ch = new Ctfd.Challenge("my-challenge", new Ctfd.ChallengeArgs{
Name = "My Challenge",
Category = "misc",
Description = "...",
Value = 500
}, new Pulumi.CustomResourceOptions { Provider = pv });
// Other resources, export, etc.
// ...
});
}