fix: import Cloudflare Secret credentials

This commit is contained in:
eding 2026-08-04 20:48:18 +02:00
parent f17097ecae
commit a08eb8616d
2 changed files with 58 additions and 10 deletions

View file

@ -1,6 +1,7 @@
package cloudflare
import (
"encoding/base64"
"encoding/json"
"errors"
"io"
@ -53,9 +54,42 @@ func IsLegacyRunTokenState(values map[string]string) bool {
func ReadCredentialsFile(path string) (Credentials, error) {
contents, err := os.ReadFile(path)
if err != nil {
return Credentials{}, errors.New("read Cloudflare credentials file")
return Credentials{}, errors.New("invalid Cloudflare credentials file")
}
return parseCredentials(contents)
if credentials, err := parseCredentials(contents); err == nil {
return credentials, nil
}
credentials, err := credentialsFromSecret(contents)
if err != nil {
return Credentials{}, errors.New("invalid Cloudflare credentials file")
}
return credentials, nil
}
func credentialsFromSecret(contents []byte) (Credentials, error) {
var secret struct {
APIVersion string `yaml:"apiVersion"`
Kind string `yaml:"kind"`
StringData map[string]string `yaml:"stringData"`
Data map[string]string `yaml:"data"`
}
decoder := yaml.NewDecoder(strings.NewReader(string(contents)))
if err := decoder.Decode(&secret); err != nil || decoder.Decode(&struct{}{}) != io.EOF || secret.APIVersion != "v1" || secret.Kind != "Secret" {
return Credentials{}, errors.New("invalid Secret")
}
plaintext, inStringData := secret.StringData[credentialsKey]
encoded, inData := secret.Data[credentialsKey]
if inStringData == inData {
return Credentials{}, errors.New("missing Secret credentials")
}
if inStringData {
return parseCredentials([]byte(plaintext))
}
decoded, err := base64.StdEncoding.DecodeString(encoded)
if err != nil {
return Credentials{}, errors.New("invalid Secret credentials")
}
return parseCredentials(decoded)
}
func NewRoute(hostname, service string) (Route, error) {

View file

@ -1,6 +1,7 @@
package cloudflare
import (
"encoding/base64"
"os"
"path/filepath"
"testing"
@ -31,18 +32,31 @@ func TestStoredTunnelRejectsLegacyOrPartialState(t *testing.T) {
func TestReadCredentialsFileRequiresKnownFields(t *testing.T) {
path := filepath.Join(t.TempDir(), "credentials.json")
if err := os.WriteFile(path, []byte(`{"AccountTag":"account","TunnelSecret":"secret","TunnelID":"tunnel"}`), 0600); err != nil {
t.Fatal(err)
}
credentials, err := ReadCredentialsFile(path)
if err != nil || credentials.TunnelID != "tunnel" {
t.Fatal("valid credential file was not accepted")
raw := `{"AccountTag":"account","TunnelSecret":"secret","TunnelID":"tunnel"}`
for _, contents := range []string{
raw,
"apiVersion: v1\nkind: Secret\nstringData:\n credentials.json: |\n " + raw + "\n",
"apiVersion: v1\nkind: Secret\ndata:\n credentials.json: " + base64.StdEncoding.EncodeToString([]byte(raw)) + "\n",
} {
if err := os.WriteFile(path, []byte(contents), 0600); err != nil {
t.Fatal(err)
}
credentials, err := ReadCredentialsFile(path)
if err != nil || credentials.TunnelID != "tunnel" {
t.Fatal("supported credential file was not accepted")
}
}
if err := os.WriteFile(path, []byte(`{"AccountTag":"account","TunnelSecret":"secret","TunnelID":"tunnel","extra":"value"}`), 0600); err != nil {
t.Fatal(err)
}
if _, err := ReadCredentialsFile(path); err == nil {
t.Fatal("credential file with unknown fields was accepted")
if _, err := ReadCredentialsFile(path); err == nil || err.Error() != "invalid Cloudflare credentials file" {
t.Fatal("credential file with unknown fields did not return a safe error")
}
if err := os.WriteFile(path, []byte("apiVersion: v1\nkind: Secret\ndata: {}\n"), 0600); err != nil {
t.Fatal(err)
}
if _, err := ReadCredentialsFile(path); err == nil || err.Error() != "invalid Cloudflare credentials file" {
t.Fatal("Secret without credentials did not return a safe error")
}
}