{ # 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.sec_websocket_key before header servers { protocols h1 h2 h2c } } (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 } } (upstream_headers) { header_up X-Real-IP {http.request.remote.host} } (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 } } # Kubernetes health-check :8003 { respond /live 200 respond /ready 200 } # TODO: refactor once we can listen only in localhost :9545 { metrics /metrics { disable_openmetrics } } # public-api api.{$GITPOD_DOMAIN} { log { level DEBUG output stdout } gitpod.cors_origin { allowed_origins https://{$GITPOD_DOMAIN} } reverse_proxy public-api-server.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:9002 } # always redirect to HTTPS http:// { redir https://{host}{uri} permanent } https://{$GITPOD_DOMAIN} { import enable_log import remove_server_header import ssl_configuration import security_headers @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 } @backend_wss { path /api/gitpod } handle @backend_wss { gitpod.sec_websocket_key uri strip_prefix /api reverse_proxy server.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:3000 { import upstream_headers } } @backend path /api/* /headless-logs/* handle @backend { gitpod.cors_origin { base_domain {$GITPOD_DOMAIN} } # note: no compression, as that breaks streaming for headless logs uri strip_prefix /api reverse_proxy server.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:3000 { import upstream_headers import upstream_connection # required for smooth streaming of terminal logs flush_interval -1 } } @codesync path /code-sync* handle @codesync { gitpod.cors_origin { any_domain true } import compression reverse_proxy server.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:3000 { import upstream_headers import upstream_connection flush_interval -1 } } @local_app { path /static/bin/gitpod-local-companion-* } handle @local_app { import compression reverse_proxy ide-proxy.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:80 { import upstream_headers 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_headers import upstream_connection } } handle { reverse_proxy dashboard.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:3001 { import upstream_headers 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 # foreign content route used by vscode to serve webview and webworker resources of the form # https://{{hash_base_32}}.. or https://v--{{hash_base_32}}.. # e.g: # https://0d9rkrj560blqb5s07q431ru9mhg19k1k4bqgd1dbprtgmt7vuhk.ws-us34xl.gitpod.io (for webviews) # https://v--0d9rkrj560blqb5s07q431ru9mhg19k1k4bqgd1dbprtgmt7vuhk.ws-us34xl.gitpod.io (for webworker) @foreign_content2 header_regexp host Host ^(?:v--)?[0-9a-v]+.ws(-[a-z0-9]+)?.{$GITPOD_DOMAIN} handle @foreign_content2 { reverse_proxy https://ws-proxy.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:9090 { import workspace_transport import upstream_headers header_up X-WSProxy-Host {http.request.host} } } @workspace_port header_regexp host Host ^(?P[0-9]{2,5})-(?P[a-z0-9][0-9a-z\-]+).ws(?P-[a-z0-9]+)?.{$GITPOD_DOMAIN} handle @workspace_port { reverse_proxy https://ws-proxy.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:9090 { import workspace_transport import upstream_headers header_up X-Gitpod-WorkspaceId {re.host.workspaceID} header_up X-Gitpod-Port {re.host.workspacePort} header_up X-WSProxy-Host {http.request.host} } } @workspace header_regexp host Host ^(?P[a-z0-9][0-9a-z\-]+).ws(?P-[a-z0-9]+)?.{$GITPOD_DOMAIN} handle @workspace { reverse_proxy https://ws-proxy.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:9090 { import workspace_transport import upstream_headers header_up X-Gitpod-WorkspaceId {re.host.workspaceID} header_up X-WSProxy-Host {http.request.host} } } respond "Not found" 404 } import /etc/caddy/vhosts/vhost.*