From 33b38b0f3ef58ce9a59bfcf99ebc4c2740907712 Mon Sep 17 00:00:00 2001 From: Sebastian Serth Date: Fri, 16 Aug 2024 01:22:35 +0200 Subject: [PATCH] Add support for GitHub Codespaces and port forwarding GitHub Codespaces is a commercial offering for devcontainers. While our setup mostly works out-of-the-box, the host and port forwarding required further adjustments to work as expected. --- README.md | 2 + bin/shakapacker-dev-server | 5 ++ config/environments/development.rb | 11 +++- .../initializers/content_security_policy.rb | 15 ++++-- config/initializers/github_codespaces.rb | 51 +++++++++++++++++++ docs/LOCAL_SETUP_DEVCONTAINER.md | 12 ++++- 6 files changed, 90 insertions(+), 6 deletions(-) create mode 100644 config/initializers/github_codespaces.rb diff --git a/README.md b/README.md index b3c227a3c..caa64b417 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # CodeHarbor +[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/openHPI/codeharbor) + [![Build Status](https://github.com/openHPI/codeharbor/workflows/CI/badge.svg)](https://github.com/openHPI/codeharbor/actions?query=workflow%3ACI) [![codecov](https://codecov.io/gh/openHPI/codeharbor/branch/master/graph/badge.svg?token=lUL0Fq7Uc9)](https://codecov.io/gh/openHPI/codeharbor) diff --git a/bin/shakapacker-dev-server b/bin/shakapacker-dev-server index 83d9c918b..6f9412f69 100755 --- a/bin/shakapacker-dev-server +++ b/bin/shakapacker-dev-server @@ -10,8 +10,13 @@ ENV['SHAKAPACKER_USE_PACKAGE_JSON_GEM'] ||= 'true' require 'bundler/setup' require 'shakapacker' require 'shakapacker/dev_server_runner' +require_relative '../config/initializers/github_codespaces' APP_ROOT = File.expand_path('..', __dir__) Dir.chdir(APP_ROOT) do + if GithubCodespaces.active? + ARGV.push('--client-web-socket-url', GithubCodespaces.client_web_socket_url) + end + Shakapacker::DevServerRunner.run(ARGV) end diff --git a/config/environments/development.rb b/config/environments/development.rb index 6c0ec5144..3188c2adf 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,12 +1,19 @@ # frozen_string_literal: true require 'active_support/core_ext/integer/time' +require_relative '../initializers/github_codespaces' Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. - # Allowed IPs for the Vagrant setup - config.web_console.allowed_ips = '192.168.0.0/16' + if GithubCodespaces.active? || ENV.fetch('USER', nil) == 'vagrant' + # Allow the webconsole to be used; all traffic is proxied and thus appears to come from a private IP + # By default, GitHub requires an authorization to access a Codespaces domain. + # Vagrant is running locally in a private network and thus not accessible from the outside anyway. + private_ips = %w[10.0.0.0/8 172.16.0.0/12 192.168.0.0/16] + config.web_console.permissions = private_ips + private_ips.each {|ip| BetterErrors::Middleware.allow_ip! ip } + end # In the development environment your application's code is reloaded any time # it changes. This slows down response time but is perfect for development diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb index c7e8691ac..5dd0057fe 100644 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@ -9,6 +9,7 @@ require_relative 'sentry_csp' require_relative 'sentry_javascript' require_relative 'devise' +require_relative 'github_codespaces' module CSP def self.apply_yml_settings_for(policy) @@ -35,6 +36,13 @@ def self.apply_omniauth_settings_for(policy) end end + def self.apply_codespaces_settings_for(policy) + %w[wss https].each do |scheme| + url = GithubCodespaces.url_for(:webpack_dev_server, scheme) + add_policy(policy, :connect_src, [url]) + end + end + def self.add_policy(policy, directive, additional_settings) all_settings = additional_settings existing_settings = if directive == 'report_uri' @@ -98,9 +106,10 @@ def self.get_host_source(url) # "An iframe which has both allow-scripts and allow-same-origin for its sandbox attribute can escape its sandboxing." policy.sandbox 'allow-downloads', 'allow-forms', 'allow-modals', 'allow-popups', 'allow-same-origin', 'allow-scripts' - CSP.apply_yml_settings_for policy - CSP.apply_sentry_settings_for policy if SentryJavascript.active? - CSP.apply_omniauth_settings_for policy if Devise.omniauth_configs.present? + CSP.apply_yml_settings_for policy + CSP.apply_sentry_settings_for policy if SentryJavascript.active? + CSP.apply_omniauth_settings_for policy if Devise.omniauth_configs.present? + CSP.apply_codespaces_settings_for policy if GithubCodespaces.active? end # Generate session nonces for permitted importmap, inline scripts, and inline styles. diff --git a/config/initializers/github_codespaces.rb b/config/initializers/github_codespaces.rb new file mode 100644 index 000000000..4c122584d --- /dev/null +++ b/config/initializers/github_codespaces.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +class GithubCodespaces + def self.active? + ENV['CODESPACES'] == 'true' + end + + def self.client_web_socket_url + "#{url_for(:webpack_dev_server, 'wss')}/ws" + end + + def self.url_for(server, scheme = 'https') + "#{scheme}://#{domain_for(server)}" + end + + def self.domain_for(server) + port = send(server) + hostname_for(port) + end + + class << self + private + + def hostname_for(port) + codespace_name = ENV.fetch('CODESPACE_NAME') + forwarding_domain = ENV.fetch('GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN') + + "#{codespace_name}-#{port}.#{forwarding_domain}" + end + + def rails_server + ENV.fetch('PORT', 7500) + end + + def webpack_dev_server + ENV.fetch('SHAKAPACKER_DEV_SERVER_PORT', 3045) + end + end +end + +if GithubCodespaces.active? && defined? Rails + Rails.application.configure do + # Allow the Rails server to be accessed from the Codespaces domain + config.hosts << GithubCodespaces.domain_for(:rails_server) + + # Disable an additional CSRF protection, where the browser's ORIGIN header is + # checked against the site's request URL. Since the Codespaces proxy is not setting + # all required X-FORWARDED-* headers, we currently need to disable this check. + config.action_controller.forgery_protection_origin_check = false + end +end diff --git a/docs/LOCAL_SETUP_DEVCONTAINER.md b/docs/LOCAL_SETUP_DEVCONTAINER.md index fea0a0ae4..68be17562 100644 --- a/docs/LOCAL_SETUP_DEVCONTAINER.md +++ b/docs/LOCAL_SETUP_DEVCONTAINER.md @@ -1,6 +1,8 @@ # Devcontainer setup -With the devcontainer-based setup, you won't need to (manually) install CodeHarbor and all dependencies on your local instance. Instead, a Docker setup containing all requirements will be configured. The development environment is defined in the `.devcontainer` repository folder and will be applied when you open the project in a supported editor or IDE. +With the devcontainer-based setup, you won't need to (manually) install CodeHarbor and all dependencies on your local instance. Instead, a Docker setup containing all requirements will be configured. + +You can either run a devcontainer locally (with Docker) or remotely (e.g., with GitHub Codespaces). In both cases, the development environment is defined in the `.devcontainer` repository folder and will be applied when you open the project in a supported editor or IDE. ## Local setup @@ -53,6 +55,14 @@ Click on the blue "Reopen in Container" button in the bottom right corner of you **RubyMine:** / **IntelliJ IDEA:** Open the file `.devcontainer/devcontainer.json` and click on the blue Docker icon in the top left corner of your editor. More information for [RubyMine](https://www.jetbrains.com/help/ruby/connect-to-devcontainer.html#create_dev_container_inside_ide) or [IntelliJ IDEA](https://www.jetbrains.com/help/idea/connect-to-devcontainer.html#create_dev_container_inside_ide). +## Remote setup with GitHub Codespaces + +You can also run the devcontainer remotely with GitHub Codespaces. This way, you can develop CodeHarbor in the cloud without the need to install any dependencies on your local machine. + +To get started with GitHub Codespaces, click on the ["Open in GitHub Codespaces"](https://codespaces.new/openHPI/codeharbor) button at the top of the [README.md file](../README.md). This will open the project in a new Codespace. You can find more information on how to set up your project for Codespaces in the [official documentation](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/adding-a-dev-container-configuration/introduction-to-dev-containers). + # Start CodeHarbor When developing with the devcontainer, you can run CodeHarbor in the same way as you would on your local machine. The only difference is that you're running it inside the devcontainer. You can find more information on how to run CodeHarbor in the [LOCAL_SETUP.md](LOCAL_SETUP.md#start-codeharbor). All ports are forwarded to your local machine, so you can access CodeHarbor in your browser as usual. + +In GitHub Codespaces, the ports are forwarded automatically, so you can access CodeHarbor in your browser by clicking on the "Open in Browser" button in the Codespaces environment as soon as the Rails server was started. [More information](https://docs.github.com/de/codespaces/developing-in-a-codespace/forwarding-ports-in-your-codespace).