{"id":14483,"date":"2023-01-11T05:41:04","date_gmt":"2023-01-11T13:41:04","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/cse\/?p=14483"},"modified":"2024-07-18T11:46:10","modified_gmt":"2024-07-18T18:46:10","slug":"host-ctf-on-azure","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/ise\/host-ctf-on-azure\/","title":{"rendered":"Hosting a Capture-the-Flag event on Azure"},"content":{"rendered":"<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2023\/01\/host-ctf-on-azure-0.png\" alt=\"hackers capturing the flag\" \/><\/p>\n<p>Our team loves a good Capture the Flag (CTF) event! Those are usually a fun way to keep team morale high while up-skilling on new technologies or freshening up the team&#8217;s existing knowledge base.<\/p>\n<p>Traditionally, <a href=\"https:\/\/en.wikipedia.org\/wiki\/Capture_the_flag_(cybersecurity)\">Capture the Flag<\/a> events are cybersecurity exercises in which &#8220;flags&#8221; are secretly hidden in a program or website and competitors steal them from other competitors (attack\/defense-style CTFs), or the organizers (jeopardy-style challenges). However, more engineering practices can be taught and practiced as a CTF event and may even not use the term CTF for it, such as our team&#8217;s <a href=\"https:\/\/github.com\/microsoft\/OpenHack\">OpenHack<\/a> content packs which are very similar to what CTF is all about, and include topics such as AI-Powered Knowledge Mining, ML and DevOps, containers, Serverless and Azure security.<\/p>\n<p>Open-Source CTF frameworks make it easy to turn <strong>any<\/strong> challenge into a CTF event with configurable challenge pages, leader boards, and other expected features of such an event, using zero-code.\nFor instance, OWASP&#8217;s <a href=\"https:\/\/owasp.org\/www-project-juice-shop\/\">Juice-Shop<\/a> has a <a href=\"https:\/\/github.com\/juice-shop\/juice-shop-ctf\">CTF plugin<\/a> that supports several common CTF platforms that you can provision and run for your teams to do security training on.<\/p>\n<p>One of the most popular open CTF platforms is <a href=\"https:\/\/github.com\/CTFd\/CTFd\">CTFd<\/a>. It is easy to use, customize, and built with open-source components. It offers several <a href=\"https:\/\/ctfd.io\/pricing\/\">plans for managed hosting and features<\/a> you may choose from, or you could deploy and maintain your environment. Managing an environment has cost and maintenance implications, but you own the data, you can integrate it with your organization&#8217;s network if required, and typically costs less. Furthermore, using Platform as a Service (PaaS), maintained by your cloud vendor, has the benefit of both worlds; Free, open-source software, and much easier maintenance and IT handling than using virtualized infrastructure components.<\/p>\n<p>This post will help you set up a self-hosted CTFd environment using nothing but Azure PaaS, so your CTF environment is scalable, secured, and easy to maintain, regardless of your team&#8217;s size.\nCode and deployment scripts used in the post are found in <a href=\"https:\/\/github.com\/balteravishay\/CTFd_azure\">this GitHub repository<\/a>, and you are encouraged to drop a discussion or an issue or contribute.<\/p>\n<h2>The Infrastructure Setup<\/h2>\n<p>Let&#8217;s start by exploring the local deployment&#8217;s <a href=\"https:\/\/github.com\/CTFd\/CTFd\/blob\/master\/docker-compose.yml\">docker-compose<\/a> file provided in the CTFd repository and plan its translation to Azure cloud components.<\/p>\n<h3>Docker Compose Architecture<\/h3>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2023\/01\/host-ctf-on-azure-1.png\" alt=\"docker compose image\" \/><\/p>\n<p>Four services and two networks are defined:<\/p>\n<ul>\n<li><strong>Default network<\/strong>: Network which is accessible by clients of CTFd.<\/li>\n<li><strong>Internal network<\/strong>: Private network for backend services.<\/li>\n<li><strong>CTFd<\/strong>: The application server that depends on db service. It is accessible on the default network but also has access to the internal network. It is configured with the db and the cache URIs which include their secrets.<\/li>\n<li><strong>Nginx<\/strong>: Reverse proxy exposing CTFd service.<\/li>\n<li><strong>Db<\/strong>: MariaDB container with some unique settings. Accessible through the internal network.<\/li>\n<li><strong>Cache<\/strong>: Redis cache that is accessible through the internal network.<\/li>\n<\/ul>\n<h3>Azure Deployment Architecture<\/h3>\n<p>Planning the Azure deployment that will host the same functionalities using platform and networking components:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2023\/01\/host-ctf-on-azure-10.png\" alt=\"azure architecture image\" \/><\/p>\n<ul>\n<li><strong>Virtual Network<\/strong>: Maintain private access to internal backend services by using VNet.<\/li>\n<li><strong>CTFd App Service and App Service Plan<\/strong>: An Azure App Service for containers (and its App Service Plan) that can scale to multiple units and replaces both docker-compose services CTFd and nginx. Using the App Service feature for VNet Integration gives the App Service access to resources in the private VNet, without actually hosting the App Service in it. Hosting an App Service within a virtual network is enabled only for App Service Environments, which cost significantly more.<\/li>\n<li><strong>Azure Database for MariaDB<\/strong>: A managed MariaDB community database that is accessible only from within the VNet by using a Private Endpoint.<\/li>\n<li><strong>Azure Cache for Redis<\/strong>: A managed Redis cache that is accessible only from within the VNet by using a Private Endpoint.<\/li>\n<li><strong>Azure Key Vault<\/strong>: Securely store keys and passwords for MariaDB and Redis. The keys can be configured directly to the CTFd App Service configuration, much like the Docker Compose definition has them, but always prefer storing such keys in secrets management solutions. This service is also accessible only from within the VNet.<\/li>\n<\/ul>\n<p>A simpler variation of this deployment reduces the overhead of VNet and the resources required to inject Azure PaaS components to VNet (Private endpoint and its additional networking resources):<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2023\/01\/host-ctf-on-azure-11.png\" alt=\"azure architecture no vnet image\" \/><\/p>\n<h2>Azure Infrastructure as Code<\/h2>\n<p>Let&#8217;s define the resources using <a href=\"https:\/\/learn.microsoft.com\/en-us\/azure\/azure-resource-manager\/bicep\/overview?tabs=bicep\">Bicep<\/a>, Azure&#8217;s domain-specific language for describing and deploying resources. Using <a href=\"https:\/\/learn.microsoft.com\/en-us\/azure\/azure-resource-manager\/bicep\/modules\">modules<\/a> for each layer, starting with the virtual network, the CTFd application server and finally the backend services.\nThe final bicep artifact should be able to deploy all submodules so that the resources are either deployed as public PaaS services, which is a more straightforward deployment, or as PaaS services in a private VNet, which is more secure.<\/p>\n<h3>Virtual Network<\/h3>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2023\/01\/host-ctf-on-azure-2.png\" alt=\"vnet definition\" \/><\/p>\n<p>The <a href=\"https:\/\/github.com\/balteravishay\/CTFd_azure\/blob\/master\/modules\/vnet.bicep\">virtual network module<\/a> defines two subnets, one hosting the private endpoints of MariaDB and Redis Cache and the second hosting the VNet integration element, which handles egress into the network from the App Service.\nVisit <a href=\"https:\/\/github.com\/Azure\/azure-quickstart-templates\/tree\/master\/quickstarts\/microsoft.web\/webapp-privateendpoint-vnet-injection\">this page<\/a> for a reference on how App Services communicates through VNet in the same way as we&#8217;re doing here.<\/p>\n<h3>Log Analytics<\/h3>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2023\/01\/host-ctf-on-azure-12.png\" alt=\"log analytics definition\" \/><\/p>\n<p>The <a href=\"https:\/\/github.com\/balteravishay\/CTFd_azure\/blob\/master\/modules\/loganalytics.bicep\">Log Analytics module<\/a> provisions a Log Analytics workspace and outputs its workspace ID for use in the next module.<\/p>\n<h3>CTF WebApp<\/h3>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2023\/01\/host-ctf-on-azure-3.png\" alt=\"webapp definition\" \/><\/p>\n<p>Next, we define the CTFd <a href=\"https:\/\/github.com\/balteravishay\/CTFd_azure\/blob\/master\/modules\/webapp.bicep\">application server module<\/a>, since we require its <a href=\"https:\/\/learn.microsoft.com\/en-us\/azure\/app-service\/overview-managed-identity?tabs=portal%2Chttp#add-a-system-assigned-identity\">managed identity\u2019s<\/a> Service Principal ID for the next element, Azure Key Vault. That is configured using the identity property.\nThe App Service is configured with MariaDB and Redis Cache URIs using <a href=\"https:\/\/learn.microsoft.com\/en-us\/azure\/app-service\/app-service-key-vault-references?tabs=azure-cli\">Key Vault integration<\/a> to avoid having the secrets as part of the App Service configuration.<\/p>\n<p><a href=\"https:\/\/learn.microsoft.com\/en-us\/azure\/app-service\/overview-vnet-integration\">VNet integration<\/a> enables the App Service to communicate with services in a VNet and is handled if the module&#8217;s input parameter for VNet is set to true by setting the <code>virtualNetworkSubnetId<\/code> and <code>vnetRouteAllEnabled<\/code> accordingly.\nFinally, the CTFd docker hub image is set, and the managed identity\u2019s Service Principal ID is output from the module.<\/p>\n<p>Diagnostic Settings component uses Workspace ID to configure sending the container logs to Log Analytics.<\/p>\n<h3>Azure Key Vault<\/h3>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2023\/01\/host-ctf-on-azure-4.png\" alt=\"key vault definition\" \/><\/p>\n<p>The <a href=\"https:\/\/github.com\/balteravishay\/CTFd_azure\/blob\/master\/modules\/keyvault.bicep\">Azure Key Vault module<\/a> sets up an instance of Key Vault with <a href=\"https:\/\/learn.microsoft.com\/en-us\/azure\/key-vault\/general\/security-features#access-model-overview\">access policy<\/a> allowing the App Service\u2019s managed identity access to list and get the keys and secrets in the vault.\nNote that no secrets are yet in the vault, as that will be handled in later modules, and that the Key Vault instance is optionally configured to grant access only from a VNet.\nDescribing the network component for integration with VNet is done using a submodule that is described later, and by setting the <code>publicNetworkAccess<\/code> parameter to Enabled (without VNet) or Disabled (with VNet).<\/p>\n<h3>Private Endpoint<\/h3>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2023\/01\/host-ctf-on-azure-5.png\" alt=\"private endpoint definition\" \/><\/p>\n<p><a href=\"https:\/\/github.com\/balteravishay\/CTFd_azure\/blob\/master\/modules\/privateendpoint.bicep\">This module<\/a> is used by the Key Vault, MariaDB and Redis modules to define the networking components required to integrate an Azure PaaS with a virtual network.\nFor more info on defining private endpoints with bicep, visit <a href=\"https:\/\/learn.microsoft.com\/en-us\/azure\/private-link\/create-private-endpoint-bicep?tabs=CLI\">this guide<\/a>.<\/p>\n<h3>MariaDB<\/h3>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2023\/01\/host-ctf-on-azure-6.png\" alt=\"maria db definition\" \/><\/p>\n<p><a href=\"https:\/\/github.com\/balteravishay\/CTFd_azure\/blob\/master\/modules\/mariadb.bicep\">This module<\/a> contains resource definitions for MariaDB and uses a submodule for the networking components required to connect it to a VNet, and another submodule for handling provisioning the database secret URI into the Key Vault.\nThe module&#8217;s first parameter determines if the database will be integrated with a VNet and controls the condition for deploying the submodule and the database&#8217;s publicNetworkAccess parameter setting it to Enabled (without VNet) or Disabled (with VNet).\nNote the MariaDB unique configurations, that were set in CTFd\u2019s docker compose, and are set to the service using its configuration sub-elements.\nFinally, the module builds a connection URI that includes the administrator user\u2019s login and password and provides it to the submodule that adds key vault secrets.<\/p>\n<h3>Key Vault Secret<\/h3>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2023\/01\/host-ctf-on-azure-9.png\" alt=\"secret definition\" \/><\/p>\n<p><a href=\"https:\/\/github.com\/balteravishay\/CTFd_azure\/blob\/master\/modules\/keyvaultsecret.bicep\">This<\/a> simple module adds a secret to Azure Key Vault.<\/p>\n<h3>Redis<\/h3>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2023\/01\/host-ctf-on-azure-7.png\" alt=\"redis definition\" \/><\/p>\n<p><a href=\"https:\/\/github.com\/balteravishay\/CTFd_azure\/blob\/master\/modules\/redis.bicep\">This<\/a> module is very similar too the Key Vault and MariaDB bicep definitions; we define the Redis resource, and optionally integrate it to the VNet with a submodule that defines the private endpoint as shown previously and set its <code>publicNetworkAccess<\/code> parameter accordingly.\nAdditionally, we add the Redis URI which contains its secret, that is retrieved using the built in function <a href=\"https:\/\/learn.microsoft.com\/en-us\/azure\/azure-resource-manager\/bicep\/bicep-functions-resource#list\">listKeys()<\/a>, to the Key Vault using the key vault secret submodule.<\/p>\n<h3>Putting it all Together<\/h3>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2023\/01\/host-ctf-on-azure-8.png\" alt=\"deployment definition\" \/><\/p>\n<p>After the hard work we&#8217;ve put into our modules, aggregating them into a <a href=\"https:\/\/github.com\/balteravishay\/CTFd_azure\/blob\/master\/ctfd.bicep\">bicep deployment<\/a> is easy enough and self-explanatory. The deployment has a single required parameter for the database administrator password (for security reasons that cannot have a default value), but other parameters are overridable as well:<\/p>\n<ul>\n<li><strong>vnet<\/strong> &#8211; Determine if the resources are deployed with a VNet, default is true (boolean).<\/li>\n<li><strong>redisServerName<\/strong> &#8211; Name of Redis cache (string).<\/li>\n<li><strong>mariaServerName<\/strong> &#8211; Name of MariaDB (string).<\/li>\n<li><strong>administratorLogin<\/strong> &#8211; MariaDB admin name (string).<\/li>\n<li><strong>administratorLoginPassword<\/strong> &#8211; MariaDB admin password, the only required parameter (string).<\/li>\n<li><strong>keyVaultName<\/strong> \u2013 Name of the key vault service (string).<\/li>\n<li><strong>appServicePlanName<\/strong> &#8211; Name of app service plan (string).<\/li>\n<li><strong>appServicePlanSkuTier<\/strong> &#8211; App Service Plan SKU tier (string).<\/li>\n<li><strong>appServicePlanSkuName<\/strong> &#8211; App Service Plan SKU name (string).<\/li>\n<li><strong>webAppName<\/strong> &#8211; Name of app service webapp (string).<\/li>\n<li><strong>logAnalyticsName<\/strong> &#8211; Name for Log Analytics Workspace (string).<\/li>\n<li><strong>virtualNetworkName<\/strong> &#8211; Name of virtual network (string).<\/li>\n<li><strong>resourcesLocation<\/strong> &#8211; Location of resources, defaults to the resource group location (string).<\/li>\n<\/ul>\n<h2>Run the Code<\/h2>\n<p>To deploy the bicep definition to your Azure subscription, run the following commands:<\/p>\n<pre><code class=\"language-bash\">export DB_PASSWORD='YOUR PASSWORD'\r\nexport RESOURCE_GROUP_NAME='RESOURCE GROUP NAME'\r\n\r\naz deployment group create --resource-group $RESOURCE_GROUP_NAME --template-file ctfd.bicep --parameters administratorLoginPassword=$DB_PASSWORD<\/code><\/pre>\n<p>Now go ahead setting up your team\u2019s <a href=\"https:\/\/docs.ctfd.io\/docs\/overview\">CTF game<\/a> and enjoy; happy hacking!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Guidance for setting up a self-hosted, scalable, secured, and easy to maintain CTFd environment, using nothing Azure Platform as a Service components.<\/p>\n","protected":false},"author":77045,"featured_media":14486,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[60,3364,3365,3363],"class_list":["post-14483","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cse","tag-azure","tag-open-source","tag-paas","tag-web"],"acf":[],"blog_post_summary":"<p>Guidance for setting up a self-hosted, scalable, secured, and easy to maintain CTFd environment, using nothing Azure Platform as a Service components.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/14483","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/users\/77045"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/comments?post=14483"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/14483\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media\/14486"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media?parent=14483"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/categories?post=14483"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/tags?post=14483"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}