Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/proxy/conf/Caddyfile
2500 views
{
	# disable automatic SSL certificate generation
	auto_https off
	# disable admin API server
	# admin localhost:2019
	admin off

	# set default SNI for old clients
	default_sni {$GITPOD_DOMAIN}

	# debug

	# configure plugin order
	# https://caddyserver.com/docs/caddyfile/directives#directive-order
	order gitpod.cors_origin before header
	order gitpod.workspace_download before redir
	order gitpod.headless_log_download before rewrite
	order gitpod.configcat before rewrite
	order gitpod.analytics before rewrite
	order gitpod.sec_websocket_key before header
	order gitpod.frontend_dev after forward_auth

	servers {
		protocols h1 h2 h2c
		metrics
	}
}

(compression) {
	encode zstd gzip
}

# configure headers to force HTTPS and enable more strict rules for the browser
(security_headers) {
	header {
		# enable HSTS
		Strict-Transport-Security max-age=31536000
		# disable clients from sniffing the media type
		X-Content-Type-Options nosniff
		# Define valid parents that may embed a page
		Content-Security-Policy "frame-ancestors 'self' https://*.{$GITPOD_DOMAIN} https://{$GITPOD_DOMAIN}"
		# keep referrer data off of HTTP connections
		Referrer-Policy no-referrer-when-downgrade
		# Enable cross-site filter (XSS) and tell browser to block detected attacks
		X-XSS-Protection "1; mode=block"

		defer # delay changes
	}
}

# workspace security headers
(workspace_security_headers) {
	header {
		# Disallow sharing the same browsing context when opened in a popup
		Cross-Origin-Opener-Policy same-origin-allow-popups
	}
	import security_headers
}

(enable_log) {
	log {
		output stdout
		format if "status > 399" jsonselect "{severity:level} {timestamp:ts} {logName:logger} {httpRequest>requestMethod:request>method} {httpRequest>protocol:request>proto} {httpRequest>status:status} {httpRequest>responseSize:size} {httpRequest>userAgent:request>headers>User-Agent>[0]} {httpRequest>requestUrl:request>uri} {httpRequest>requestHost:request>host} {cacheStatus:resp_headers>X-Cache-Status>[0]}" {
			level_format "upper"
			time_format "rfc3339_nano"
		}
	}
}

(enable_log_debug) {
	log {
		output stdout
		format jsonselect "{severity:level} {timestamp:ts} {logName:logger} {httpRequest>requestMethod:request>method} {httpRequest>protocol:request>proto} {httpRequest>status:status} {httpRequest>responseSize:size} {httpRequest>userAgent:request>headers>User-Agent>[0]} {httpRequest>requestUrl:request>uri} {httpRequest>requestHost:request>host} {cacheStatus:resp_headers>X-Cache-Status>[0]}" {
			level_format "upper"
			time_format "rfc3339_nano"
		}
	}
}

(remove_server_header) {
	header {
		-server
		-x-powered-by
	}
}

(ssl_configuration) {
	tls /etc/caddy/certificates/tls.crt /etc/caddy/certificates/tls.key {
		protocols tls1.2
		#ca_root   <pem_file>
	}
}

(upstream_connection) {
	lb_try_duration 1s
}

(debug_headers) {
	header X-Gitpod-Region {$GITPOD_REGION}.{$GITPOD_INSTALLATION_SHORTNAME}
}

(workspace_transport) {
	transport http {
		tls_insecure_skip_verify
		keepalive 60s
		keepalive_idle_conns 100
	}
}

(google_storage_headers) {
	header {
		-x-guploader-uploadid
		-etag
		-x-goog-generation
		-x-goog-metageneration
		-x-goog-hash
		-x-goog-stored-content-length
		-x-gitpod-region
		-x-goog-stored-content-encoding
		-x-goog-storage-class
		-x-goog-generation
		-x-goog-metageneration
		-cache-control
		-expires

		defer # delay changes
	}
}

(server_public_api) {
	import compression

	reverse_proxy server.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:3001 {
		import upstream_connection
	}
}

# Kubernetes health-check
:8003 {
	respond /live 200
	respond /ready 200
}

# TODO: refactor once we can listen only in localhost
:9545 {

	metrics /metrics {
		disable_openmetrics
	}
}

# Internal analytics endpoint
:9546 {
	handle /analytics* {
		gitpod.analytics
	}
}

# Internal configcat endpoint
:9547 {
	handle /configcat* {
		gitpod.configcat
	}
}

# public-api
api.{$GITPOD_DOMAIN} {
	log {
		level DEBUG
		output stdout
	}

	gitpod.cors_origin {
		allowed_origins https://{$GITPOD_DOMAIN}
	}

	@to_server path /auth/*/callback
	handle @to_server {
		import compression

		reverse_proxy server.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:3000 {
			import upstream_connection
		}
	}

	handle /gitpod.v1.* {
		import server_public_api
	}

	reverse_proxy public-api-server.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:9002
}

# always redirect to HTTPS
http:// {
	redir https://{host}{uri} permanent
}

# The services directly correlate to Public Services exposed by Dedicated
# When adding new endpoints, please notify Team Engine
https://services.{$GITPOD_DOMAIN} {
	import enable_log
	import remove_server_header
	import ssl_configuration
	import security_headers

	handle /idp/* {
		reverse_proxy public-api-server.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:9002
	}

	handle /apps/* {
		reverse_proxy server.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:3000
	}
}

https://{$GITPOD_DOMAIN} {
	import enable_log
	import remove_server_header
	import ssl_configuration
	import security_headers

	# Make it possible to get an exposed healthcheck, for example for HTTP(s) NLBs
	handle /api/health {
		respond 200
	}

	@proxy_server_public_api path /public-api/gitpod.experimental.v1.HelloService*
	handle @proxy_server_public_api {
		uri strip_prefix /public-api

		import server_public_api
	}

	@proxy_server_public_api_2 path /public-api/gitpod.v1.*
	handle @proxy_server_public_api_2 {
		uri strip_prefix /public-api

		import server_public_api
	}

	@proxy_public_api path /public-api*
	handle @proxy_public_api {
		uri strip_prefix /public-api

		reverse_proxy public-api-server.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:9002
	}

	@workspace_download path /workspace-download*
	handle @workspace_download {
		import google_storage_headers

		header {
			# The browser needs to see the correct archive content type to trigger the download.
			content-type "application/tar+gzip"
		}

		gitpod.workspace_download {
			service http://server.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:3000
		}

		# redirect works here because we "navigate" to this URL, which makes the browser handle this as primary request, and not fuff around with CORS at all
		redir {http.gitpod.workspace_download_url} 303
	}

	@headless_log_download path /headless-log-download*
	handle @headless_log_download {
		header {
			# Alltough logs are plain text "text/html" works for reliably for streaming
			content-type "text/html; charset=utf-8"
		}

		# Perform lookup to server and actual reverse_proxy in one go because caddy's `reverse_proxy` is not powerful enough
		gitpod.headless_log_download {
			service http://server.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:3000
		}
	}

	@configcat path /configcat*
	handle @configcat {
		gitpod.cors_origin {
			any_domain true
		}
		gitpod.configcat
	}

	@analytics path /analytics*
	handle @analytics {
		gitpod.cors_origin {
			any_domain true
		}
		gitpod.analytics
	}

	@browser_analytics path /_analytics*
	handle @browser_analytics {
		import compression

		reverse_proxy server.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:3000 {
			import upstream_connection
		}
	}

	@backend_wss {
		path /api/gitpod /api/v1
	}
	handle @backend_wss {
		gitpod.cors_origin {
			base_domain {$GITPOD_DOMAIN}
		}

		gitpod.sec_websocket_key

		uri strip_prefix /api

		reverse_proxy server.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:3000 {
			import upstream_connection
		}
	}

	@backend path /api/* /headless-logs/* /prebuild-logs/*
	handle @backend {
		gitpod.cors_origin {
			base_domain {$GITPOD_DOMAIN}
		}

		uri strip_prefix /api

		reverse_proxy server.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:3000 {
			import upstream_connection

			# required for smooth streaming of terminal logs
			flush_interval -1
		}
	}

	@iam path /iam/*
	handle @iam {

		gitpod.cors_origin {
			any_domain true
		}

		uri strip_prefix /iam

		reverse_proxy public-api-server.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:9002 {
			import upstream_connection
		}
	}

	@codesync path /code-sync*
	handle @codesync {
		gitpod.cors_origin {
			any_domain true
		}

		import compression

		reverse_proxy server.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:3000 {
			import upstream_connection

			flush_interval -1
		}
	}

	@ide_bin {
		path /static/bin/*
	}
	handle @ide_bin {
		import compression

		reverse_proxy ide-proxy.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:80 {
			import upstream_connection
		}
	}

	@to_server path /auth/github/callback /auth /auth/* /apps /apps/*
	handle @to_server {
		import compression

		reverse_proxy server.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:3000 {
			import upstream_connection
		}
	}

	# Default: route to dashboard
	handle {
		# Dev URL header present?
		@frontend_dev {
			header X-Frontend-Dev-URL *
		}
		handle @frontend_dev {
			# ...and allowed to use it?
			forward_auth server.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:3000 {
				uri /auth/frontend-dev

				header_up -Connection
				header_up -Upgrade
			}
			# Then handle it with our plugin!
			gitpod.frontend_dev {
				upstream http://dashboard.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:3001
			}
		}

		reverse_proxy dashboard.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:3001 {
			import upstream_connection
		}
	}

	@legacy_urls path /github.com/* /gitlab.com/* /bitbucket.org/*
	handle @legacy_urls {
		redir https://{$GITPOD_DOMAIN}/#{uri} permanent
	}

	handle_errors {
		redir https://{$GITPOD_DOMAIN}/sorry/#Error%20{http.reverse_proxy.status_text} 302
	}
}

# workspaces
https://*.*.{$GITPOD_DOMAIN} {
	import enable_log
	import workspace_security_headers
	import remove_server_header
	import ssl_configuration
	import debug_headers

	# Dear future reader: If you wonder about the asterisk in the line below: So do I!
	# But as of now (caddy 2.6.2) it's the only way to make caddy import a file.
	import /etc/caddy/workspace-handler/*.{$WORKSPACE_HANDLER_FILE}
}


import /etc/caddy/vhosts/vhost.*