From 0375e25092a28431a3bae4ac615192587f9585ba Mon Sep 17 00:00:00 2001 From: Owen Date: Thu, 3 Sep 2026 17:21:58 -0400 Subject: [PATCH] Support aliased SITE_ID and SITE_SECRET --- newtconfig/newtconfig.go | 22 +++++++++++++++++-- newtconfig/newtconfig_test.go | 40 ++++++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/newtconfig/newtconfig.go b/newtconfig/newtconfig.go index 5468f03..b6071d6 100644 --- a/newtconfig/newtconfig.go +++ b/newtconfig/newtconfig.go @@ -188,6 +188,20 @@ func applyEnvBool(dst *bool, envName, key string, sources map[string]string) { } } +// applyEnvStrAlias behaves like applyEnvStr, but checks a preferred env var +// first and only falls back to an alias name when the preferred one is unset. +func applyEnvStrAlias(dst *string, envName, aliasEnvName, key string, sources map[string]string) { + if v := os.Getenv(envName); v != "" { + *dst = v + sources[key] = string(sourceEnv) + return + } + if v := os.Getenv(aliasEnvName); v != "" { + *dst = v + sources[key] = string(sourceEnv) + } +} + // validateTLSConfig validates that TLS config fields are consistent and that // referenced files exist. func validateTLSConfig(cfg newtpkg.Config) error { @@ -361,8 +375,12 @@ func Load(opts Options) (newtpkg.Config, error) { // ---- layer 2: environment variables ---- applyEnvStr(&cfg.Endpoint, "PANGOLIN_ENDPOINT", "endpoint", sources) - applyEnvStr(&cfg.ID, "NEWT_ID", "id", sources) - applyEnvStr(&cfg.Secret, "NEWT_SECRET", "secret", sources) + // SITE_ID/SITE_SECRET are accepted as aliases for NEWT_ID/NEWT_SECRET + // (NEWT_ID/NEWT_SECRET win if both are set) so a site tunnel's + // credentials can be named consistently with other Pangolin CLI + // connection types (e.g. CLIENT_ID/CLIENT_SECRET for `up client`). + applyEnvStrAlias(&cfg.ID, "NEWT_ID", "SITE_ID", "id", sources) + applyEnvStrAlias(&cfg.Secret, "NEWT_SECRET", "SITE_SECRET", "secret", sources) applyEnvStr(&cfg.ProvisioningKey, "NEWT_PROVISIONING_KEY", "provisioning-key", sources) applyEnvStr(&cfg.NewtName, "NEWT_NAME", "name", sources) diff --git a/newtconfig/newtconfig_test.go b/newtconfig/newtconfig_test.go index b313276..0547709 100644 --- a/newtconfig/newtconfig_test.go +++ b/newtconfig/newtconfig_test.go @@ -11,12 +11,50 @@ func clearNewtEnv(t *testing.T) { for _, k := range []string{ "PANGOLIN_ENDPOINT", "NEWT_ID", "NEWT_SECRET", "DNS", "LOG_LEVEL", "MTU", "CONFIG_FILE", "NEWT_PROVISIONING_KEY", "NEWT_NAME", - "DISABLE_SSH", "DISABLE_CLIENTS", + "DISABLE_SSH", "DISABLE_CLIENTS", "SITE_ID", "SITE_SECRET", } { t.Setenv(k, "") } } +func TestLoadNewtConfig_SiteIDSecretEnvAliases(t *testing.T) { + clearNewtEnv(t) + t.Setenv("SITE_ID", "from-site-id") + t.Setenv("SITE_SECRET", "from-site-secret") + + cfg, err := Load(Options{Args: []string{"--config-file", filepath.Join(t.TempDir(), "missing.json")}}) + if err != nil { + t.Fatalf("Load returned error: %v", err) + } + + if cfg.ID != "from-site-id" { + t.Errorf("expected id from SITE_ID, got %q", cfg.ID) + } + if cfg.Secret != "from-site-secret" { + t.Errorf("expected secret from SITE_SECRET, got %q", cfg.Secret) + } +} + +func TestLoadNewtConfig_NewtIDSecretWinOverSiteAliases(t *testing.T) { + clearNewtEnv(t) + t.Setenv("SITE_ID", "from-site-id") + t.Setenv("SITE_SECRET", "from-site-secret") + t.Setenv("NEWT_ID", "from-newt-id") + t.Setenv("NEWT_SECRET", "from-newt-secret") + + cfg, err := Load(Options{Args: []string{"--config-file", filepath.Join(t.TempDir(), "missing.json")}}) + if err != nil { + t.Fatalf("Load returned error: %v", err) + } + + if cfg.ID != "from-newt-id" { + t.Errorf("expected NEWT_ID to win over SITE_ID, got %q", cfg.ID) + } + if cfg.Secret != "from-newt-secret" { + t.Errorf("expected NEWT_SECRET to win over SITE_SECRET, got %q", cfg.Secret) + } +} + func TestLoadNewtConfig_Defaults(t *testing.T) { clearNewtEnv(t)