From e8f6936480b7fa9738911e3e7fc2ec0d8a634a88 Mon Sep 17 00:00:00 2001 From: Shtorm <108103062+shtorm-7@users.noreply.github.com> Date: Tue, 11 Aug 2026 23:36:15 +0300 Subject: [PATCH] Add license_key support for masque and warp Cloudflare profiles --- common/cloudflare/api.go | 26 ++++++++++++++++++++++++++ common/cloudflare/models.go | 4 ++++ examples/masque/client.json | 3 ++- examples/warp/client.json | 1 + option/cloudflare.go | 1 + protocol/masque/outbound.go | 5 +++++ protocol/warp/endpoint.go | 5 +++++ 7 files changed, 44 insertions(+), 1 deletion(-) diff --git a/common/cloudflare/api.go b/common/cloudflare/api.go index 85d33252..0dd115ca 100644 --- a/common/cloudflare/api.go +++ b/common/cloudflare/api.go @@ -92,6 +92,32 @@ func (api *CloudflareApi) EnrollKey(ctx context.Context, authToken string, id st return profile, json.NewDecoder(response.Body).Decode(profile) } +func (api *CloudflareApi) UpdateAccount(ctx context.Context, authToken string, id string, license string) (*Account, error) { + data := UpdateAccountRequest{License: license} + jsonData, err := json.Marshal(data) + if err != nil { + return nil, fmt.Errorf("failed to marshal json: %v", err) + } + request, err := http.NewRequest("PUT", ApiUrl+"/"+ApiVersion+"/reg/"+id+"/account", bytes.NewBuffer(jsonData)) + if err != nil { + return nil, err + } + for k, v := range Headers { + request.Header.Set(k, v) + } + request.Header.Set("Authorization", "Bearer "+authToken) + response, err := api.client.Do(request.WithContext(ctx)) + if err != nil { + return nil, err + } + defer response.Body.Close() + if response.StatusCode != http.StatusOK { + return nil, fmt.Errorf("failed to update account: %v", response.StatusCode) + } + account := new(Account) + return account, json.NewDecoder(response.Body).Decode(account) +} + func (api *CloudflareApi) GetProfile(ctx context.Context, authToken string, id string) (*CloudflareProfile, error) { request, err := http.NewRequest("GET", ApiUrl+"/"+ApiVersion+"/reg/"+id, nil) if err != nil { diff --git a/common/cloudflare/models.go b/common/cloudflare/models.go index 591b0338..9eb23780 100644 --- a/common/cloudflare/models.go +++ b/common/cloudflare/models.go @@ -111,6 +111,10 @@ type DeviceUpdate struct { Name string `json:"name,omitempty"` } +type UpdateAccountRequest struct { + License string `json:"license"` +} + type APIError struct { Result interface{} `json:"result"` Success bool `json:"success"` diff --git a/examples/masque/client.json b/examples/masque/client.json index 24def45e..e7cd985e 100644 --- a/examples/masque/client.json +++ b/examples/masque/client.json @@ -33,7 +33,8 @@ "detour": "direct", // For getting existing MASQUE device profile, else sing-box will create new profile "id": "", - "auth_token": "" + "auth_token": "", + "license_key": "" }, "udp_timeout": "5m0s", "udp_keepalive_period": "30s", diff --git a/examples/warp/client.json b/examples/warp/client.json index cfcdc528..6307374f 100644 --- a/examples/warp/client.json +++ b/examples/warp/client.json @@ -44,6 +44,7 @@ "id": "", "private_key": "", "auth_token": "", + "license_key": "", "recreate": false } // Dial Fields diff --git a/option/cloudflare.go b/option/cloudflare.go index 6fbd0805..d6ce7165 100644 --- a/option/cloudflare.go +++ b/option/cloudflare.go @@ -4,6 +4,7 @@ type CloudflareProfile struct { ID string `json:"id,omitempty"` AuthToken string `json:"auth_token,omitempty"` PrivateKey string `json:"private_key,omitempty"` + LicenseKey string `json:"license_key,omitempty"` Recreate bool `json:"recreate,omitempty"` Detour string `json:"detour,omitempty"` } diff --git a/protocol/masque/outbound.go b/protocol/masque/outbound.go index 18632f48..759f04b2 100644 --- a/protocol/masque/outbound.go +++ b/protocol/masque/outbound.go @@ -302,6 +302,11 @@ func (w *Outbound) createConfig() (*Config, error) { return nil, err } } + if w.options.Profile.LicenseKey != "" && profile.Account.License != w.options.Profile.LicenseKey { + if _, err = api.UpdateAccount(w.ctx, profile.Token, profile.ID, w.options.Profile.LicenseKey); err != nil { + return nil, E.New("failed to apply license key: ", err) + } + } privateKey, publicKey, err := masque.GenerateEcKeyPair() if err != nil { return nil, E.New("failed to generate key pair: ", err) diff --git a/protocol/warp/endpoint.go b/protocol/warp/endpoint.go index 997ace68..cb581414 100644 --- a/protocol/warp/endpoint.go +++ b/protocol/warp/endpoint.go @@ -237,6 +237,11 @@ func (w *Endpoint) createConfig() (*Config, error) { return nil, err } } + if w.options.Profile.LicenseKey != "" && profile.Account.License != w.options.Profile.LicenseKey { + if _, err = api.UpdateAccount(w.ctx, profile.Token, profile.ID, w.options.Profile.LicenseKey); err != nil { + return nil, E.New("failed to apply license key: ", err) + } + } return &Config{ PrivateKey: privateKey.String(), Interface: profile.Config.Interface,