{"id":19880657,"url":"https://github.com/emzi0767/rosettactf","last_synced_at":"2025-05-02T13:32:24.725Z","repository":{"id":39020015,"uuid":"283594386","full_name":"Emzi0767/RosettaCTF","owner":"Emzi0767","description":"Simple tool for running CTF events, built on ASP.NET Core, and compatible with CTFtime","archived":false,"fork":false,"pushed_at":"2023-01-06T14:59:36.000Z","size":11799,"stargazers_count":6,"open_issues_count":20,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-07T02:35:06.048Z","etag":null,"topics":["angular","asp-net-core","aspnetcore","ctf","ctf-engine","ctf-event","ctf-events","ctftime"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Emzi0767.png","metadata":{"files":{"readme":"README.MD","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.TXT","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":["Emzi0767"],"patreon":"emzi0767","ko_fi":"emzi0767","custom":["https://paypal.me/Emzi0767/5USD"]}},"created_at":"2020-07-29T20:18:40.000Z","updated_at":"2024-12-05T18:38:24.000Z","dependencies_parsed_at":"2023-02-06T05:30:33.016Z","dependency_job_id":null,"html_url":"https://github.com/Emzi0767/RosettaCTF","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Emzi0767%2FRosettaCTF","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Emzi0767%2FRosettaCTF/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Emzi0767%2FRosettaCTF/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Emzi0767%2FRosettaCTF/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Emzi0767","download_url":"https://codeload.github.com/Emzi0767/RosettaCTF/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252046274,"owners_count":21685983,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","asp-net-core","aspnetcore","ctf","ctf-engine","ctf-event","ctf-events","ctftime"],"created_at":"2024-11-12T17:12:09.199Z","updated_at":"2025-05-02T13:32:23.431Z","avatar_url":"https://github.com/Emzi0767.png","language":"C#","readme":"# RosettaCTF\nA simple CTF platform for quicktly setting up your own events, built using ASP.NET Core 3.1 and Angular 10. It's quick \nand easy to set up (all you need to do is fill appropriate values in relevant config files), is fully Cloudflare- and \nDocker-compatible.\n\nRosetta uses PostgreSQL as its main storage engine, and Redis as its cache storage provider, however it's possible and \nrelatively easy to implement other providers; all you have to do is implement provided interfaces and flag your \nassemblies with appropriate attributes. You can read more in [Extending RosettaCTF section](#extending-rosettactf).\n\n## Setup\nRosetta provides 2 different ways of authenticating users - via OAuth2, and/or via username/password authentication, \nwith optional TOTP 2FA support. By default only Discord has a fully-featured built-in OAuth2 provider, however, the \ninstance operator can configure any number of custom providers via config.json. You can also implement proper support \nfor external providers, as outlined in [Extending RosettaCTF section](#extending-rosettactf).\n\nTo enable OAuth, set `authentication / oauth / enable` to `true`. If OAuth is enabled, the `tokenKey` under \n`authentication / oauth` should be set to a strong random string, as it will be used for token storage. \n\nTo enable local username/password authentication, set `authentication / localLogin` to `true`.\n\nTo use Discord as an authentication provider, create an application on [Discord developers website](https://discord.com/developers/applications), \nand paste the client ID and secret into appropriate places in the config (or supply them via other means, such as \nenvironment variables or commandline args). In the OAuth2 tab, you should add a redirect URL to the application. It \nshould have a path of `/session/callback` (so the URL should look like `https://myrosettactfinstance.xyz/session/callback`). \nYou should also set `guildId` to your event server's ID. Rosetta will only authorize users who are in your specified \nserver to take part in the event.\n\nNext step is configuring Rosetta's JWT provider. You should provide values for the `tokenIssuer` and `tokenKey` fields \nunder `authentication`. Your key should be sufficiently strong to be considered secure, as it will be used to sign \ntokens. A weak key might lead to users impersonating other users.\n\nNext up is HTTP configuration. You can specify multiple HTTP listen endpoints, each as a separate item in the listen \narray. If you want an endpoint to use TLS, you should set `useSsl` to `true`, and supply the path to the certificate \nfile in PKCS#12 format, and a file containing the password to it. It is also configure proxy settings in here, if the \nAPI server is deployed behind one, such as nginx reverse proxy or Cloudflare.\n\nFor cache and database, options are provider-dependent. You should configure them appropriately for your providers.\n\nLastly, `eventConfiguration` should lead to a YAML file with all the necessary challenge definitions. The YAML file \nshould contain 2 documents, the first one should be event configuration (name, start, end, organizers, etc.), whereas \nthe second one should contain all event definitions.\n\nSee `config.json.example` and `event.yml.example` in the root of this repository for example configuration files.\n\n### Configuring via JSON file\nBy default, Rosetta will attempt to read any file called `config.json` in its working directory, and parse its values \nas configuration. \n\nThe name and location of this file can be overriden via environment variables, by setting \n`ROSETTACTF__JSONCONFIGURATION` (note the double underscore) variable to a valid JSON file path. For example, to load \nJSON configuration from a Docker secret called `rosetta-config`, you would specify the variable like so: \n`ROSETTACTF__JSONCONFIGURATION=/run/secrets/rosetta-config`.\n\nThe location can also be overriden via commandline, by setting `--jsonConfiguration=path`.\n\n### Configuring via YAML file\nMuch like with JSON, Rosetta will attempt to read and parse `config.yml` from its working directory as configuration.\n\nMuch like with JSON configuration, its name and location can be overriden via `ROSETTACTF__YAMLCONFIGURATION` \nenvironment variable or `--yamlConfiguration` commandline option.\n\nJSON and YAML configuration can be used at the same time, however some caveats apply.\n\n### Configuring via environment variables\nAll of the configuration settings listed in `config.json.example` can also be provided via environment variables. \nRosetta will automatically parse any variables with names starting with `ROSETTACTF__` (note the double underscore). \nDouble underscores will be treated as path separators, meaning that in order to set value for `discord / clientId`, \nyou have to create a variable called `ROSETTACTF__DISCORD__CLIENTID` (again, note double underscores), and set its \nvalue appropriately.\n\nTo set value for list items, such as `http / listen / * / address`, you use the index as if it was a property name. \nRemember that indexes are 0-based, so to provide the bind address for the first endpoint, you would specify it via \nvariable called `ROSETTACTF__HTTP__LISTEN__0__ADDRESS`.\n\n### Configuring via commandline options\nSimilarly to environment variables, Rosetta accepts settings input from commandline, using `:` as path separator. This \nmeans that in order to set `discord / clientId`, you would pass `--discord:clientId=my_client_id` via commandline. \nSimilarly to environment variables, list items use index as property name, so to set bind address of first listen \nendpoint (`http / listen / 0 / address`), you would specify `--http:listen:0:address=localhost`.\n\n### appsettings.json and appsettings.\\*.json\nDefault ASP.NET Core configuration facilities load and parse this file as the first configuration source. It is \ntherefore possible to also use it as a configuration source for all the options from `config.json.example`. \nFurthermore, it is possible to specify the JSON and YAML configuration paths, by setting \n`\"jsonConfiguration\": \"path\"` and/or `\"yamlConfiguration\": \"path\"` in the root object of the file.\n\nIt is also possible to set different configuration options for all 3 types of environments supported by ASP.NET Core, \nby using the `appsettings.Environment.json` file (where `Environment` is one of `Development`, `Staging`, or \n`Production`). The values from `appsettings.Environment.json` will be merged with `appsettings.json` at runtime, and \nany values specified in environment-specific version of the file will overwrite the values specified in the base file.\n\nPlease note that caveats apply.\n\n### Configuration priority\nAll 5 configuration sources can be used at the same time, however some caveats apply and should be kept in mind. \n\nFirstly, the order of configuration loading. When the application starts, the first loaded configuration is \n`appsettings.json` and `appsettings.Environment.json` (where `Environment` is the current environment, see the \nrelevant section of this readme for more details). Next, Rosetta will read any configuration from environment \nvariables and commandline options. From these sources, the locations and names of JSON and YAML configuration are \ndetermined, if applicable. Locations defined in commandline options will take precedence over those defined in \nenvironment variables, which in turn take precedence over `appconfig.json` and `appconfig.Environment.json`. Next, \nRosetta will attempt to load any configuration from the specified JSON and YAML files. Values from these sources are \nthen merged with the rest.\n\nSecond thing to keep in mind is the configuration priority. The final value priority is as follows (from lowest to \nhighest priority):\n\n1. `appsettings.json`\n2. `appsettings.Environment.json`\n3. JSON configuration file (defaults to `config.json` in working directory; skipped if not found)\n4. YAML configuration file (defaults to `config.yml` in working directoryl skipped if not found)\n5. Environment variables prefixed with `ROSETTACTF__`\n6. Commandline options\n\n## Setting up challenges\nAs per the configuration, you will have to create event configuration as a 2-document YAML file. An example file is \nprovided as `event.yml.example` in the root of the repository. Remember that YAML is a very sensitive format, and it's \nhighly recommended you use an editor with proper support for it (such as Notepad++ or Visual Studio Code).\n\nFor more information on editing YAML files, see [YAML page on Wikipedia](https://en.wikipedia.org/wiki/YAML), \n[The official YAML website](https://yaml.org/), and a \n[website about multiline strings in YAML](https://yaml-multiline.info/).\n\n## Deployment\nOnce you have configured your event, you can proceed to deployment. The process consists of 2 main parts. You have to \nconfigure and run the API server, and then you have to deploy the SPA files to a place where static content is served \nfrom.\n\nNext, you have to configure your HTTP server, such that it passes any request to `/api/*` to the API server **without \nrewriting the path**, and any other request should be served as a file, unless it does not exist, in which case \n`index.html` should be returned.\n\n### Example nginx configuration for Linux\nPlease note that while nginx is recommended, Apache should also work.\n\n```\n# HTTP/80\n# Redirect this to HTTPS/443\nserver {\n        # Listen for HTTP connections on port 80\n        listen      80;\n        listen [::]:80;\n\n        # Set the server name\n        server_name myrosettactfinstance.xyz;\n\n        # Permanent redirect to HTTPS\n        return 301 https://$server_name$request_uri;\n}\n\n# HTTPS/443\n# Use HTTPS with HTTP/2 and HSTS\nserver {\n        # Listen for HTTPS connections on port 443\n        listen      443 ssl http2;\n        listen [::]:443 ssl http2;\n\n        # Set the server name\n        server_name myrosettactfinstance.xyz;\n\n        # Include certificate configuration\n        include snippets.d/ssl-letsencrypt.conf;\n\n        # Set content root and content index\n        root /var/www/myrosettactfinstance.xyz/pub_html;\n        index index.html;\n\n        # Reverse proxy\n        # Proxy all other requests to another server\n        location /api {\n                proxy_pass          https://localhost:5000;\n                proxy_set_header    X-Real-IP           $remote_addr;\n                proxy_set_header    Host                $http_host;\n                proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;\n                proxy_set_header    X-Forwarded-Proto   $scheme;\n                proxy_set_header    X-Forwarded-Scheme  $scheme;\n                proxy_set_header    X-Forwarded-Host    $http_host;\n\n                proxy_redirect off;\n                proxy_buffering off;\n        }\n\n        # Handle all other requests as usual, but instead of returning 404s, return index.html\n        location / {\n                try_files $uri $uri/ /index.html;\n        }\n}\n```\n\n### Example IIS 10.0 configuration for Windows 10 and Windows Server 2016/2019\n[Instructions on separate page](SETUP-IIS.MD)\n\n## CTFtime integration\nRosettaCTF exposes 2 CTFtime info endpoints: `/api/ctftime/scoreboard`, which can be used to provide scoreboard \ninformation to CTFtime, and `/api/ctftime/feed`, which provides information about task solving events. You can learn \nmore about these endpoints at [CTFtime](https://ctftime.org/json-scoreboard-feed).\n\n## Other considerations\nOn startup, Rosetta transfers all challenges from your event configuration file (default: `event.yml`) to the \npersistent storage that is database for integrity purposes. This means that if you want to update challenges, you will \nhave to either clear the relevant tables in your database (for default postgres provider that is `challenges`, \n`challenge_categories`, `challenge_hints`, and `challenge_attachments`) to let Rosetta reinstall the challenges on \nnext startup, or manually reflect the changes in the database. Keep in mind that in case of relational databases, \nthere will be foreign key constraints between tables (which will also involve solve table, `solves` in the default \nprovider), so be careful with this.\n\n## Extending RosettaCTF\nIt is possible to implement your own cache, database, and OAuth providers. You can check the existing providers for \nreference on how to do so.\n\nYour project should reference `RosettaCTF.Abstractions`, which provides all the necessary abstractions, common types, \nand utilities which are needed to implement a Rosetta component.\n\nThe assembly should be tagged with `CacheProviderAttribute`, `DatabaseProviderAttribute`, or `OAuthProviderAttribute` \nrespectively for cache, database, and OAuth providers. Furthermore, the assembly should provide a class which \nimplements `ICacheServiceInitializer`, `IDatabaseServiceInitializer`, or `IOAuthProviderServiceInitializer` interface, \nrespective for the type of component, which is default-constructible. The class has methods for registering and \ninitializing the provider, to allow for performing any startup tasks.\n\nIt is typically expected that a cache provider will provide implementations for `ICtfChallengeCacheRepository`, \n`IOAuthStateRepository`, and `IMfaStateRepository`. A database provider should typically provide `IUserRepository`, \n`ICtfChallengeRepository`, and `IMfaRepository`. This, however, is not strongly-defined, and these types can be \nprovided by any of these components. The general idea is that cache should provide short-lived data, such as states \nand current point counts, whereas a database should be a persistent storage.\n\nAn OAuth provider should provide an implementation of `IOAuthProvider`.\n\nThe built assemblies can be dropped into RosettaCTF's API server working directory, along with any dependencies. They \nshould be detected and loaded automatically. However, that might not always work, due to how .NET Core handles \ndependencies. In such event, a project can be created in respective directory, and then RosettaCTF.API project can be \nbuilt. The API project is set up such that it automatically references any projects in `src/cache`, `src/database`, \nand `src/oauth`. This adds those projects to the dependency tree, which causes them to be automatically deployed with \nall their dependencies.\n\n## Building RosettaCTF\nRosettaCTF consists of 2 parts: the API server (RosettaCTF.API project), and the SPA webapp (RosettaCTF.UI project). \nTo build the former, .NET Core SDK 3.1 (version 3.1.401 or better) is required. To build the latter, node.js 10.0.0 or \nbetter is required.\n\nBy default, building the API project will build the SPA as well. This can take quite a while, so in order to omit \nbuilding SPA, you can specify `/p:BuildSpa=False` flag.\n\nTo build the SPA separately from the API project, run `npm run prod`. This will build a deployment-ready production \nversion of the SPA. Do not forget to restore packages by doing `npm install` before building.\n\n## Credits and Acknowledgements\nI'd like to thank the following people, for helping make this project possible:\n- **Still Hsu** - for bearing with me while, at the same time, providing feedback and testing the application. They \n  provided me with a lot of invaluable input on both inner and outer workings of Rosetta. Make sure to check out their \n  GitHub and socials.\n   - [GitHub](https://github.com/Still34)\n   - [Twitter](https://twitter.com/StillAzureH)\n   - [Blog/Website](https://stillu.cc/)\n- **Quahu** - for .NET rubberducking, which helped me get through some mental blockage moments, as well as some \n  general programming input, which helped me squeeze out a bit more out of .NET, be it functionality or performance.\n   - [GitHub](https://github.com/Quahu)\n\n## Support me\nLots of effort went into making this software.\n\nIf you feel like I'm doing a good job, or just want to throw money at me, you can do so through any of the following:\n- [Patreon](https://www.patreon.com/emzi0767)\n- [PayPal](https://paypal.me/Emzi0767/5USD)\n\n## Other questions\nIf you have other questions or would like to talk in general, feel free to visit my Discord server.\n\n[![Emzi's Central Dispatch](https://discordapp.com/api/guilds/207879549394878464/embed.png?style=banner1)](https://discord.gg/rGKrJDR)\n","funding_links":["https://github.com/sponsors/Emzi0767","https://patreon.com/emzi0767","https://ko-fi.com/emzi0767","https://paypal.me/Emzi0767/5USD","https://www.patreon.com/emzi0767"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femzi0767%2Frosettactf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Femzi0767%2Frosettactf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femzi0767%2Frosettactf/lists"}