Fix four deployability blockers found by QA (closes #189, closes #190, closes #191, closes #192)

- CSRF over plain HTTP (#189): gorilla/csrf assumed https for its
  same-origin check, so setup and every POST returned 403 over plain
  HTTP. Gate csrf.PlaintextHTTPRequest on a new UPAAS_PLAINTEXT_HTTP
  config value; the default keeps https, correct for a TLS-terminating
  reverse proxy. The README plain-HTTP recipe now sets it.
- git image never pulled (#190): ensureImage pulls alpine/git (pinned
  digest unchanged) when absent, before the clone container is created.
- port-mapping 500 (#192): the ports delete form used {{ .CSRFField }}
  inside {{range .Ports}}, where the dot is a *models.Port; use
  {{ $.CSRFField }} like the labels and volumes blocks.
- env-var 403 (#191): the editor read the CSRF token from $el (the
  submitting form, which has none) instead of $root, sending an empty
  token; read from $root.

Model: opus-4-8
This commit is contained in:
2026-09-09 14:12:09 +00:00
parent 7a34fc999c
commit cdcf527b25
8 changed files with 149 additions and 4 deletions

View File

@@ -49,6 +49,7 @@ type Config struct {
DockerHost string
SentryDSN string
MaintenanceMode bool
PlaintextHTTP bool // clients reach µPaaS over plain HTTP (no TLS-terminating proxy)
MetricsUsername string
MetricsPassword string
SessionSecret string `json:"-"`
@@ -100,6 +101,7 @@ func setupViper(name string) {
viper.SetDefault("DOCKER_HOST", "unix:///var/run/docker.sock")
viper.SetDefault("SENTRY_DSN", "")
viper.SetDefault("MAINTENANCE_MODE", false)
viper.SetDefault("PLAINTEXT_HTTP", false)
viper.SetDefault("METRICS_USERNAME", "")
viper.SetDefault("METRICS_PASSWORD", "")
viper.SetDefault("SESSION_SECRET", "")
@@ -135,6 +137,7 @@ func buildConfig(log *slog.Logger, params *Params) (*Config, error) {
DockerHost: viper.GetString("DOCKER_HOST"),
SentryDSN: viper.GetString("SENTRY_DSN"),
MaintenanceMode: viper.GetBool("MAINTENANCE_MODE"),
PlaintextHTTP: viper.GetBool("PLAINTEXT_HTTP"),
MetricsUsername: viper.GetString("METRICS_USERNAME"),
MetricsPassword: viper.GetString("METRICS_PASSWORD"),
SessionSecret: viper.GetString("SESSION_SECRET"),