diff --git a/.env.example b/.env.example index 4a19923..fe1e6c2 100644 --- a/.env.example +++ b/.env.example @@ -21,6 +21,7 @@ SSL_CERTIFICATE_KEY_FILE=privkey.pem TAPM_DATABASE_DSN=file:/data/tapm.db?_pragma=busy_timeout(5000)&_pragma=foreign_keys(1)&_pragma=journal_mode(WAL) TAPM_PUBLIC_URL=http://broker.example.com:8680 TAPM_GITEA_URL=http://git.example.com:8680 +TAPM_GITEA_INTERNAL_URL=http://gitea:3000 # These values are intentionally blank during initial Gitea bootstrap. # Run ./manage.sh configure-broker before starting the broker. TAPM_GITEA_CLIENT_ID= diff --git a/docs/deployment.md b/docs/deployment.md index e1d8381..35d1374 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -119,6 +119,11 @@ The broker is deliberately not started during the first bootstrap. Blank OAuth/package values are valid for Gitea-only setup, while `start` refuses to launch the broker until all required credentials are present. +`TAPM_GITEA_URL` is the public browser-facing Gitea URL. +`TAPM_GITEA_INTERNAL_URL` defaults to `http://gitea:3000` in installations so +OAuth token exchange and package traffic stay on the private Compose network. +Do not publish Gitea's internal port to make broker authentication work. + For troubleshooting, use `./manage.sh diagnostics`. Do not share unredacted `docker compose config` output: Compose resolves the broker's `.env` file and can print cookie, OAuth, and package secrets. If configuration output is diff --git a/install.sh b/install.sh index 89ae5e1..6585315 100755 --- a/install.sh +++ b/install.sh @@ -114,6 +114,7 @@ umask 077 printf 'TAPM_DATABASE_DSN=file:/data/tapm.db?_pragma=busy_timeout(5000)&_pragma=foreign_keys(1)&_pragma=journal_mode(WAL)\n' printf 'TAPM_PUBLIC_URL=%s://%s%s\n' "$public_scheme" "$broker_domain" "$public_port_suffix" printf 'TAPM_GITEA_URL=%s://%s%s\n' "$public_scheme" "$gitea_domain" "$public_port_suffix" + printf 'TAPM_GITEA_INTERNAL_URL=http://gitea:3000\n' printf 'TAPM_GITEA_CLIENT_ID=\n' printf 'TAPM_GITEA_CLIENT_SECRET=\n' printf 'TAPM_GITEA_PACKAGE_OWNER=TAI\n' diff --git a/internal/app/auth.go b/internal/app/auth.go index 643774f..4ab8446 100644 --- a/internal/app/auth.go +++ b/internal/app/auth.go @@ -5,6 +5,7 @@ import ( "database/sql" "encoding/json" "fmt" + "log" "net/http" "net/url" "strings" @@ -90,6 +91,7 @@ func (s *Server) handleCallback(w http.ResponseWriter, r *http.Request) { user, err := s.exchangeOAuthCode(r.Context(), code) if err != nil { + log.Printf("Gitea OAuth code exchange failed: %v", err) http.Error(w, "Gitea sign-in failed", http.StatusBadGateway) return } @@ -154,7 +156,7 @@ func (s *Server) exchangeOAuthCode(ctx context.Context, code string) (giteaUser, request, err := http.NewRequestWithContext( ctx, http.MethodPost, - joinURL(s.cfg.GiteaURL, "/login/oauth/access_token"), + joinURL(s.cfg.GiteaInternalURL, "/login/oauth/access_token"), strings.NewReader(form.Encode()), ) if err != nil { @@ -181,7 +183,7 @@ func (s *Server) exchangeOAuthCode(ctx context.Context, code string) (giteaUser, request, err = http.NewRequestWithContext( ctx, http.MethodGet, - joinURL(s.cfg.GiteaURL, "/api/v1/user"), + joinURL(s.cfg.GiteaInternalURL, "/api/v1/user"), nil, ) if err != nil { diff --git a/internal/app/config.go b/internal/app/config.go index 4772f42..a6b6c72 100644 --- a/internal/app/config.go +++ b/internal/app/config.go @@ -15,6 +15,7 @@ type Config struct { PublicURL *url.URL DatabaseDSN string GiteaURL *url.URL + GiteaInternalURL *url.URL GiteaClientID string GiteaClientSecret string GiteaPackageOwner string @@ -65,6 +66,14 @@ func LoadConfig() (Config, error) { if err != nil { return cfg, err } + cfg.GiteaInternalURL, err = parseURLValue( + "TAPM_GITEA_INTERNAL_URL", + envDefault("TAPM_GITEA_INTERNAL_URL", "http://gitea:3000"), + true, + ) + if err != nil { + return cfg, err + } cfg.CookieSecure = cfg.PublicURL.Scheme == "https" cfg.DefaultDuration, err = time.ParseDuration( @@ -137,7 +146,10 @@ func LoadConfig() (Config, error) { } func parseAbsoluteURL(name string, allowInsecureHTTP bool) (*url.URL, error) { - raw := os.Getenv(name) + return parseURLValue(name, os.Getenv(name), allowInsecureHTTP) +} + +func parseURLValue(name string, raw string, allowInsecureHTTP bool) (*url.URL, error) { parsed, err := url.Parse(raw) validScheme := parsed != nil && parsed.Scheme == "https" if allowInsecureHTTP && parsed != nil && parsed.Scheme == "http" { diff --git a/internal/app/config_test.go b/internal/app/config_test.go index 95369c3..2b1f47e 100644 --- a/internal/app/config_test.go +++ b/internal/app/config_test.go @@ -47,4 +47,19 @@ func TestHTTPSConfigUsesSecureCookies(t *testing.T) { if !cfg.CookieSecure { t.Fatal("HTTPS mode did not enable secure cookies") } + if got := cfg.GiteaInternalURL.String(); got != "http://gitea:3000" { + t.Fatalf("GiteaInternalURL = %q, want private Compose URL", got) + } +} + +func TestConfigAcceptsExplicitInternalGiteaURL(t *testing.T) { + setRequiredConfigEnvironment(t, "https") + t.Setenv("TAPM_GITEA_INTERNAL_URL", "http://gitea-test:3000") + cfg, err := LoadConfig() + if err != nil { + t.Fatal(err) + } + if got := cfg.GiteaInternalURL.String(); got != "http://gitea-test:3000" { + t.Fatalf("GiteaInternalURL = %q, want configured private URL", got) + } } diff --git a/internal/app/download.go b/internal/app/download.go index 222a1d4..e3b73f9 100644 --- a/internal/app/download.go +++ b/internal/app/download.go @@ -372,7 +372,7 @@ func (s *Server) handlePackageDownload(w http.ResponseWriter, r *http.Request) { } registryURL := joinURL( - s.cfg.GiteaURL, + s.cfg.GiteaInternalURL, fmt.Sprintf( "/api/packages/%s/generic/%s/%s/%s", url.PathEscape(s.cfg.GiteaPackageOwner), diff --git a/internal/app/upload.go b/internal/app/upload.go index 6cca3e7..ba16d07 100644 --- a/internal/app/upload.go +++ b/internal/app/upload.go @@ -240,7 +240,7 @@ func (s *Server) streamPackageToGitea( source io.Reader, ) (string, int64, int, error) { registryURL := joinURL( - s.cfg.GiteaURL, + s.cfg.GiteaInternalURL, fmt.Sprintf( "/api/packages/%s/generic/%s/%s/%s", url.PathEscape(s.cfg.GiteaPackageOwner), @@ -289,7 +289,7 @@ func (s *Server) deletePackageVersion( packageVersion string, ) error { registryURL := joinURL( - s.cfg.GiteaURL, + s.cfg.GiteaInternalURL, fmt.Sprintf( "/api/packages/%s/generic/%s/%s", url.PathEscape(s.cfg.GiteaPackageOwner), diff --git a/internal/app/upload_test.go b/internal/app/upload_test.go index a967688..7d2a9f0 100644 --- a/internal/app/upload_test.go +++ b/internal/app/upload_test.go @@ -59,6 +59,7 @@ func TestStreamPackageToGitea(t *testing.T) { server := &Server{ cfg: Config{ GiteaURL: registryURL, + GiteaInternalURL: registryURL, GiteaPackageOwner: "TAI", GiteaWriteUser: "publisher", GiteaWriteToken: "write-token", @@ -112,6 +113,7 @@ func TestDeletePackageVersion(t *testing.T) { server := &Server{ cfg: Config{ GiteaURL: registryURL, + GiteaInternalURL: registryURL, GiteaPackageOwner: "TAI", GiteaWriteUser: "publisher", GiteaWriteToken: "write-token", diff --git a/tests/test-deployment-scripts.sh b/tests/test-deployment-scripts.sh index 7d30e14..55fb44a 100755 --- a/tests/test-deployment-scripts.sh +++ b/tests/test-deployment-scripts.sh @@ -77,6 +77,7 @@ assert_env() { assert_env 'SSL_MODE=none' assert_env 'TAPM_PUBLIC_URL=http://broker.test:8780' assert_env 'TAPM_GITEA_URL=http://git.test:8780' +assert_env 'TAPM_GITEA_INTERNAL_URL=http://gitea:3000' assert_env 'GITEA_PUBLIC_URL=http://git.test:8780/' grep -F -- '-f compose.yaml -f compose.http.yaml up -d gitea nginx' \ "$MOCK_DOCKER_LOG" >/dev/null || {