Repository: MorDavid/FlareTunnel Branch: main Commit: 8cd8bbec9aea Files: 11 Total size: 81.7 KB Directory structure: gitextract_pkuk33xv/ ├── .github/ │ └── FUNDING.yml ├── FlareTunnel.go ├── README.md ├── blacklist-aggressive.txt ├── blacklist-minimal.txt ├── blacklist.txt ├── build.bat ├── build.sh ├── example_usage.py ├── go.mod └── go.sum ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: mordavid patreon: mordavid open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry polar: # Replace with a single Polar username buy_me_a_coffee: mordavid thanks_dev: # Replace with a single thanks.dev username custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] ================================================ FILE: FlareTunnel.go ================================================ package main import ( "bufio" "bytes" "crypto/rand" "crypto/rsa" "crypto/tls" "crypto/x509" "crypto/x509/pkix" "encoding/json" "encoding/pem" "fmt" "io" "math/big" "net" "net/http" "net/url" "os" "regexp" "strconv" "strings" "sync" "time" "github.com/olekukonko/tablewriter" ) // ==================================================================== // CONSTANTS & TYPES // ==================================================================== const ( Version = "1.0.0" CloudflareBaseURL = "https://api.cloudflare.com/client/v4" WorkerScript = `/** * FlareTunnel - Cloudflare Worker URL Redirection Script */ addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) }) async function handleRequest(request) { try { const url = new URL(request.url) const targetUrl = getTargetUrl(url, request.headers) if (!targetUrl) { return createErrorResponse('No target URL specified', { usage: { query_param: '?url=https://example.com', header: 'X-Target-URL: https://example.com', path: '/https://example.com' } }, 400) } let targetURL try { targetURL = new URL(targetUrl) } catch (e) { return createErrorResponse('Invalid target URL', { provided: targetUrl }, 400) } // Build target URL with filtered query parameters const targetParams = new URLSearchParams() for (const [key, value] of url.searchParams) { if (!['url', '_cb', '_t'].includes(key)) { targetParams.append(key, value) } } if (targetParams.toString()) { targetURL.search = targetParams.toString() } // Create proxied request const proxyRequest = createProxyRequest(request, targetURL) const response = await fetch(proxyRequest) // Process and return response return createProxyResponse(response, request.method) } catch (error) { return createErrorResponse('Proxy request failed', { message: error.message, timestamp: new Date().toISOString() }, 500) } } function getTargetUrl(url, headers) { // Priority: query param > header > path let targetUrl = url.searchParams.get('url') if (!targetUrl) { targetUrl = headers.get('X-Target-URL') } if (!targetUrl && url.pathname !== '/') { const pathUrl = url.pathname.slice(1) if (pathUrl.startsWith('http')) { targetUrl = pathUrl } } return targetUrl } function createProxyRequest(request, targetURL) { const proxyHeaders = new Headers() const allowedHeaders = [ 'accept', 'accept-language', 'accept-encoding', 'authorization', 'cache-control', 'content-type', 'origin', 'referer', 'user-agent' ] // Copy allowed headers for (const [key, value] of request.headers) { if (allowedHeaders.includes(key.toLowerCase())) { proxyHeaders.set(key, value) } } proxyHeaders.set('Host', targetURL.hostname) // Set X-Forwarded-For header const customXForwardedFor = request.headers.get('X-My-X-Forwarded-For') if (customXForwardedFor) { proxyHeaders.set('X-Forwarded-For', customXForwardedFor) } else { proxyHeaders.set('X-Forwarded-For', generateRandomIP()) } return new Request(targetURL.toString(), { method: request.method, headers: proxyHeaders, body: ['GET', 'HEAD'].includes(request.method) ? null : request.body }) } function createProxyResponse(response, requestMethod) { const responseHeaders = new Headers() // Copy ALL response headers (let browser handle encoding) for (const [key, value] of response.headers) { responseHeaders.set(key, value) } // Add/Override CORS headers responseHeaders.set('Access-Control-Allow-Origin', '*') responseHeaders.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS, PATCH, HEAD') responseHeaders.set('Access-Control-Allow-Headers', '*') if (requestMethod === 'OPTIONS') { return new Response(null, { status: 204, headers: responseHeaders }) } return new Response(response.body, { status: response.status, statusText: response.statusText, headers: responseHeaders }) } function createErrorResponse(error, details, status) { return new Response(JSON.stringify({ error, ...details }), { status, headers: { 'Content-Type': 'application/json' } }) } function generateRandomIP() { return [1, 2, 3, 4].map(() => Math.floor(Math.random() * 255) + 1).join('.') }` ) // Account represents a Cloudflare account configuration type Account struct { Name string `json:"name"` APIToken string `json:"api_token"` AccountID string `json:"account_id"` ZoneID string `json:"zone_id,omitempty"` } // Config represents the FlareTunnel configuration type Config struct { Accounts []Account `json:"accounts"` } // Worker represents a Cloudflare Worker deployment type Worker struct { Name string `json:"name"` URL string `json:"url"` CreatedAt string `json:"created_at"` ID string `json:"id"` AccountID string `json:"account_id"` ConfigAccountName string `json:"config_account_name,omitempty"` } // Analytics represents worker analytics data type Analytics struct { Success bool `json:"success"` TotalRequests int `json:"total_requests"` PerWorker map[string]int `json:"per_worker"` Limit int `json:"limit"` Error string `json:"error,omitempty"` } // ==================================================================== // CLOUDFLARE API CLIENT // ==================================================================== type CloudflareClient struct { APIToken string AccountID string BaseURL string Headers map[string]string subdomain string } func NewCloudflareClient(apiToken, accountID string) *CloudflareClient { return &CloudflareClient{ APIToken: apiToken, AccountID: accountID, BaseURL: CloudflareBaseURL, Headers: map[string]string{ "Authorization": "Bearer " + apiToken, "Content-Type": "application/json", }, } } func (c *CloudflareClient) GetSubdomain() (string, error) { if c.subdomain != "" { return c.subdomain, nil } url := fmt.Sprintf("%s/accounts/%s/workers/subdomain", c.BaseURL, c.AccountID) req, err := http.NewRequest("GET", url, nil) if err != nil { return "", err } for k, v := range c.Headers { req.Header.Set(k, v) } client := &http.Client{Timeout: 30 * time.Second} resp, err := client.Do(req) if err != nil { c.subdomain = strings.ToLower(c.AccountID) return c.subdomain, nil } defer resp.Body.Close() if resp.StatusCode == 200 { var result struct { Result struct { Subdomain string `json:"subdomain"` } `json:"result"` } if err := json.NewDecoder(resp.Body).Decode(&result); err == nil { if result.Result.Subdomain != "" { c.subdomain = result.Result.Subdomain return c.subdomain, nil } } } c.subdomain = strings.ToLower(c.AccountID) return c.subdomain, nil } func (c *CloudflareClient) CreateWorker(name string) (*Worker, error) { if name == "" { name = generateWorkerName() } url := fmt.Sprintf("%s/accounts/%s/workers/scripts/%s", c.BaseURL, c.AccountID, name) var b bytes.Buffer writer := multipartWriter(&b) metadata := map[string]string{ "body_part": "script", "main_module": "worker.js", } metadataJSON, _ := json.Marshal(metadata) writer.WriteField("metadata", string(metadataJSON)) writer.WriteField("script", WorkerScript) contentType := writer.FormDataContentType() writer.Close() req, err := http.NewRequest("PUT", url, &b) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+c.APIToken) req.Header.Set("Content-Type", contentType) client := &http.Client{Timeout: 60 * time.Second} resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != 200 { body, _ := io.ReadAll(resp.Body) return nil, fmt.Errorf("failed to create worker: %s", string(body)) } // Enable subdomain subdomain, _ := c.GetSubdomain() subdomainURL := fmt.Sprintf("%s/accounts/%s/workers/scripts/%s/subdomain", c.BaseURL, c.AccountID, name) subdomainBody := bytes.NewBufferString(`{"enabled":true}`) subdomainReq, _ := http.NewRequest("POST", subdomainURL, subdomainBody) for k, v := range c.Headers { subdomainReq.Header.Set(k, v) } client.Do(subdomainReq) workerURL := fmt.Sprintf("https://%s.%s.workers.dev", name, subdomain) return &Worker{ Name: name, URL: workerURL, CreatedAt: time.Now().Format("2006-01-02 15:04:05"), ID: name, AccountID: c.AccountID, }, nil } func (c *CloudflareClient) ListWorkers() ([]*Worker, error) { url := fmt.Sprintf("%s/accounts/%s/workers/scripts", c.BaseURL, c.AccountID) req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, err } for k, v := range c.Headers { req.Header.Set(k, v) } client := &http.Client{Timeout: 30 * time.Second} resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() var result struct { Result []struct { ID string `json:"id"` CreatedOn string `json:"created_on"` } `json:"result"` } if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, err } subdomain, _ := c.GetSubdomain() workers := []*Worker{} for _, script := range result.Result { if strings.HasPrefix(script.ID, "flaretunnel-") { workers = append(workers, &Worker{ Name: script.ID, URL: fmt.Sprintf("https://%s.%s.workers.dev", script.ID, subdomain), CreatedAt: script.CreatedOn, AccountID: c.AccountID, }) } } return workers, nil } func (c *CloudflareClient) DeleteWorker(name string) error { url := fmt.Sprintf("%s/accounts/%s/workers/scripts/%s", c.BaseURL, c.AccountID, name) req, err := http.NewRequest("DELETE", url, nil) if err != nil { return err } for k, v := range c.Headers { req.Header.Set(k, v) } client := &http.Client{Timeout: 30 * time.Second} resp, err := client.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != 200 && resp.StatusCode != 404 { return fmt.Errorf("failed to delete worker: status %d", resp.StatusCode) } return nil } func (c *CloudflareClient) GetAnalytics() (*Analytics, error) { graphqlURL := "https://api.cloudflare.com/client/v4/graphql" now := time.Now() dateStart := now.Add(-24 * time.Hour).Format("2006-01-02T15:04:05Z") dateEnd := now.Format("2006-01-02T15:04:05Z") query := ` query WorkersAnalytics($accountTag: string!, $datetimeStart: string!, $datetimeEnd: string!) { viewer { accounts(filter: {accountTag: $accountTag}) { workersInvocationsAdaptive( limit: 10000 filter: { datetime_geq: $datetimeStart datetime_leq: $datetimeEnd } orderBy: [sum_requests_DESC] ) { dimensions { scriptName } sum { requests errors subrequests } } } } } ` variables := map[string]interface{}{ "accountTag": c.AccountID, "datetimeStart": dateStart, "datetimeEnd": dateEnd, } payload := map[string]interface{}{ "query": query, "variables": variables, } payloadBytes, _ := json.Marshal(payload) req, err := http.NewRequest("POST", graphqlURL, bytes.NewBuffer(payloadBytes)) if err != nil { return nil, err } for k, v := range c.Headers { req.Header.Set(k, v) } client := &http.Client{Timeout: 30 * time.Second} resp, err := client.Do(req) if err != nil { return &Analytics{Success: false, Limit: 100000, PerWorker: make(map[string]int)}, nil } defer resp.Body.Close() if resp.StatusCode != 200 { return &Analytics{Success: false, Limit: 100000, PerWorker: make(map[string]int)}, nil } var result struct { Data struct { Viewer struct { Accounts []struct { WorkersInvocationsAdaptive []struct { Dimensions struct { ScriptName string `json:"scriptName"` } `json:"dimensions"` Sum struct { Requests int `json:"requests"` } `json:"sum"` } `json:"workersInvocationsAdaptive"` } `json:"accounts"` } `json:"viewer"` } `json:"data"` } if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return &Analytics{Success: false, Limit: 100000, PerWorker: make(map[string]int)}, nil } analytics := &Analytics{ Success: true, Limit: 100000, PerWorker: make(map[string]int), } if len(result.Data.Viewer.Accounts) > 0 { for _, inv := range result.Data.Viewer.Accounts[0].WorkersInvocationsAdaptive { scriptName := inv.Dimensions.ScriptName requests := inv.Sum.Requests analytics.PerWorker[scriptName] = requests analytics.TotalRequests += requests } } return analytics, nil } // ==================================================================== // SSL CERTIFICATE GENERATION // ==================================================================== func generateCACert(certPath, keyPath string) error { if _, err := os.Stat(certPath); err == nil { if _, err := os.Stat(keyPath); err == nil { return nil } } priv, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return err } notBefore := time.Now() notAfter := notBefore.Add(3650 * 24 * time.Hour) serialNumber, _ := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128)) template := x509.Certificate{ SerialNumber: serialNumber, Subject: pkix.Name{ Country: []string{"US"}, Province: []string{"CA"}, Locality: []string{"Local"}, Organization: []string{"FlareTunnel"}, CommonName: "FlareTunnel CA", }, NotBefore: notBefore, NotAfter: notAfter, KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, BasicConstraintsValid: true, IsCA: true, } derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) if err != nil { return err } certOut, err := os.Create(certPath) if err != nil { return err } pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) certOut.Close() keyOut, err := os.Create(keyPath) if err != nil { return err } pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}) keyOut.Close() fmt.Printf("✓ Generated CA certificate: %s\n", certPath) return nil } func generateHostCert(hostname, caCertPath, caKeyPath string) (*tls.Certificate, error) { caCertPEM, err := os.ReadFile(caCertPath) if err != nil { return nil, err } block, _ := pem.Decode(caCertPEM) caCert, err := x509.ParseCertificate(block.Bytes) if err != nil { return nil, err } caKeyPEM, err := os.ReadFile(caKeyPath) if err != nil { return nil, err } keyBlock, _ := pem.Decode(caKeyPEM) caKey, err := x509.ParsePKCS1PrivateKey(keyBlock.Bytes) if err != nil { return nil, err } priv, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return nil, err } notBefore := time.Now() notAfter := notBefore.Add(365 * 24 * time.Hour) serialNumber, _ := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128)) template := x509.Certificate{ SerialNumber: serialNumber, Subject: pkix.Name{ Country: []string{"US"}, Province: []string{"CA"}, Locality: []string{"Local"}, Organization: []string{"FlareTunnel"}, CommonName: hostname, }, NotBefore: notBefore, NotAfter: notAfter, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, BasicConstraintsValid: true, DNSNames: []string{hostname, "*." + hostname}, } derBytes, err := x509.CreateCertificate(rand.Reader, &template, caCert, &priv.PublicKey, caKey) if err != nil { return nil, err } certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}) cert, err := tls.X509KeyPair(certPEM, keyPEM) if err != nil { return nil, err } return &cert, nil } // ==================================================================== // FLARETUNNEL MANAGER // ==================================================================== type FlareTunnel struct { Config *Config Clients map[string]*CloudflareClient EndpointsFile string ConfigFile string workers []*Worker workersMutex sync.RWMutex } func NewFlareTunnel(configFile string) (*FlareTunnel, error) { if configFile == "" { configFile = "flaretunnel.json" } config, err := loadConfig(configFile) if err != nil { return nil, err } ft := &FlareTunnel{ Config: config, Clients: make(map[string]*CloudflareClient), EndpointsFile: "flaretunnel_endpoints.json", ConfigFile: configFile, } for _, account := range config.Accounts { ft.Clients[account.Name] = NewCloudflareClient(account.APIToken, account.AccountID) } return ft, nil } func loadConfig(path string) (*Config, error) { data, err := os.ReadFile(path) if err != nil { return nil, err } // Remove BOM if present (UTF-8-sig compatibility) if len(data) >= 3 && data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF { data = data[3:] } var config Config if err := json.Unmarshal(data, &config); err != nil { return nil, err } return &config, nil } func (ft *FlareTunnel) SaveEndpoints(workers []*Worker) error { data, err := json.MarshalIndent(workers, "", " ") if err != nil { return err } return os.WriteFile(ft.EndpointsFile, data, 0644) } func (ft *FlareTunnel) LoadEndpoints() ([]*Worker, error) { data, err := os.ReadFile(ft.EndpointsFile) if err != nil { return nil, err } // Remove BOM if present if len(data) >= 3 && data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF { data = data[3:] } var workers []*Worker if err := json.Unmarshal(data, &workers); err != nil { return nil, err } return workers, nil } func (ft *FlareTunnel) SyncEndpoints() ([]*Worker, error) { allWorkers := []*Worker{} for accountName, client := range ft.Clients { workers, err := client.ListWorkers() if err != nil { continue } for _, w := range workers { w.ConfigAccountName = accountName allWorkers = append(allWorkers, w) } } if len(allWorkers) > 0 { ft.SaveEndpoints(allWorkers) } return allWorkers, nil } func (ft *FlareTunnel) CreateWorkers(count int, accountName string, distribute bool) error { fmt.Printf("\nCreating %d FlareTunnel endpoint(s)...\n", count) created := []*Worker{} if accountName != "" { // Single account client, ok := ft.Clients[accountName] if !ok { return fmt.Errorf("account '%s' not found", accountName) } fmt.Printf(" Using account: %s\n", accountName) for i := 0; i < count; i++ { worker, err := client.CreateWorker("") if err != nil { fmt.Printf(" [%d/%d] Failed: %v\n", i+1, count, err) continue } worker.ConfigAccountName = accountName created = append(created, worker) fmt.Printf(" [%d/%d] %s -> %s\n", i+1, count, worker.Name, worker.URL) } } else if distribute && len(ft.Clients) > 1 { // Distribute across accounts fmt.Printf(" Distribution mode: Checking quotas across %d account(s)...\n", len(ft.Clients)) accountQuotas := make(map[string]int) for name, client := range ft.Clients { analytics, _ := client.GetAnalytics() remaining := 100000 - analytics.TotalRequests accountQuotas[name] = remaining fmt.Printf(" %s: %d requests remaining\n", name, remaining) } totalQuota := 0 for _, quota := range accountQuotas { totalQuota += quota } if totalQuota == 0 { totalQuota = len(ft.Clients) for name := range accountQuotas { accountQuotas[name] = 1 } } workersPerAccount := make(map[string]int) for name, quota := range accountQuotas { proportion := float64(quota) / float64(totalQuota) workersPerAccount[name] = max(1, int(float64(count)*proportion)) } // Adjust to exact count for sum := sumMap(workersPerAccount); sum < count; sum = sumMap(workersPerAccount) { maxAccount := "" maxQuota := 0 for name, quota := range accountQuotas { if quota > maxQuota { maxQuota = quota maxAccount = name } } workersPerAccount[maxAccount]++ } fmt.Printf("\n Distribution plan:\n") for name, wc := range workersPerAccount { fmt.Printf(" %s: %d worker(s)\n", name, wc) } fmt.Println() createdCount := 0 for name, wc := range workersPerAccount { client := ft.Clients[name] for i := 0; i < wc; i++ { worker, err := client.CreateWorker("") if err != nil { fmt.Printf(" [%d/%d] [%s] Failed: %v\n", createdCount+1, count, name, err) continue } worker.ConfigAccountName = name created = append(created, worker) createdCount++ fmt.Printf(" [%d/%d] [%s] %s -> %s\n", createdCount, count, name, worker.Name, worker.URL) } } } else { // Default to first account var firstClient *CloudflareClient var firstName string for name, client := range ft.Clients { firstClient = client firstName = name break } fmt.Printf(" Using account: %s\n", firstName) for i := 0; i < count; i++ { worker, err := firstClient.CreateWorker("") if err != nil { fmt.Printf(" [%d/%d] Failed: %v\n", i+1, count, err) continue } worker.ConfigAccountName = firstName created = append(created, worker) fmt.Printf(" [%d/%d] %s -> %s\n", i+1, count, worker.Name, worker.URL) } } ft.SyncEndpoints() fmt.Printf("\nCreated: %d, Failed: %d\n", len(created), count-len(created)) return nil } func (ft *FlareTunnel) ListWorkers(verbose, checkStatus bool) error { workers, err := ft.SyncEndpoints() if err != nil || len(workers) == 0 { fmt.Println("❌ No FlareTunnel endpoints found") fmt.Println("💡 Create some with: go run FlareTunnel.go create --count 5") return nil } // Verbose mode: check status as well if verbose { checkStatus = true // Force status check in verbose mode! } // Get analytics for all accounts allAnalytics := make(map[string]*Analytics) for accountName, client := range ft.Clients { analytics, _ := client.GetAnalytics() allAnalytics[accountName] = analytics } if checkStatus { fmt.Printf("\n🔍 Checking status of %d worker(s)...\n", len(workers)) fmt.Println("This may take a few seconds...\n") } fmt.Println() table := tablewriter.NewWriter(os.Stdout) // Build header based on flags header := []string{"#", "Account", "Name"} if verbose { header = append(header, "Created", "Age") } header = append(header, "URL", "Requests") if checkStatus { header = append(header, "Status (ms)") } else { header = append(header, "Status") } table.SetHeader(header) table.SetBorder(true) for idx, worker := range workers { row := []string{strconv.Itoa(idx), worker.ConfigAccountName, worker.Name} // Add verbose info (Created, Age) if verbose { createdStr := "Unknown" ageStr := "Unknown" // Extract timestamp from worker name: flaretunnel-1764721536-kmoiok re := regexp.MustCompile(`flaretunnel-(\d+)-`) if matches := re.FindStringSubmatch(worker.Name); len(matches) > 1 { if timestamp, err := strconv.ParseInt(matches[1], 10, 64); err == nil { createdTime := time.Unix(timestamp, 0) createdStr = createdTime.Format("2006-01-02 15:04:05") // Calculate age age := time.Since(createdTime) if age.Hours() < 1 { ageStr = fmt.Sprintf("%dm ago", int(age.Minutes())) } else if age.Hours() < 24 { ageStr = fmt.Sprintf("%dh ago", int(age.Hours())) } else { ageStr = fmt.Sprintf("%dd ago", int(age.Hours()/24)) } } } row = append(row, createdStr, ageStr) } // Add URL row = append(row, worker.URL) // Add requests count requests := "0" if analytics, ok := allAnalytics[worker.ConfigAccountName]; ok && analytics.Success { if req, ok := analytics.PerWorker[worker.Name]; ok { requests = strconv.Itoa(req) } } row = append(row, requests) // Add status if checkStatus { // Test actual worker testURL := worker.URL + "?url=" + url.QueryEscape("https://httpbin.org/status/200") start := time.Now() client := &http.Client{Timeout: 5 * time.Second} resp, err := client.Get(testURL) elapsed := time.Since(start) if err != nil { row = append(row, "❌ Failed") } else { resp.Body.Close() if resp.StatusCode == 200 { row = append(row, fmt.Sprintf("✅ %dms", elapsed.Milliseconds())) } else { row = append(row, fmt.Sprintf("⚠️ %d", resp.StatusCode)) } } } else { row = append(row, "✅ Active") } table.Append(row) } table.Render() fmt.Println("\n💡 Use worker index with: go run FlareTunnel.go tunnel --workers 0,1,2 --verbose") fmt.Println() return nil } func (ft *FlareTunnel) CleanupWorkers(accountName string) error { if accountName != "" { client, ok := ft.Clients[accountName] if !ok { return fmt.Errorf("account '%s' not found", accountName) } fmt.Printf("\n🗑️ Cleaning up account: %s\n", accountName) workers, _ := client.ListWorkers() for _, worker := range workers { if err := client.DeleteWorker(worker.Name); err != nil { fmt.Printf(" ✗ Failed to delete: %s\n", worker.Name) } else { fmt.Printf(" ✓ Deleted: %s\n", worker.Name) } } } else { fmt.Printf("\n🗑️ Cleaning up ALL accounts (%d total)\n", len(ft.Clients)) for name, client := range ft.Clients { fmt.Printf(" Account: %s\n", name) workers, _ := client.ListWorkers() for _, worker := range workers { if err := client.DeleteWorker(worker.Name); err != nil { fmt.Printf(" ✗ Failed to delete: %s\n", worker.Name) } else { fmt.Printf(" ✓ Deleted: %s\n", worker.Name) } } } os.Remove(ft.EndpointsFile) } return nil } func (ft *FlareTunnel) TestWorkers(targetURL, method string) error { workers, err := ft.LoadEndpoints() if err != nil || len(workers) == 0 { fmt.Println("❌ No workers available") return fmt.Errorf("no workers found") } fmt.Printf("\nTesting %d FlareTunnel endpoint(s) with %s\n", len(workers), targetURL) successCount := 0 uniqueIPs := make(map[string]bool) for _, worker := range workers { fmt.Printf("\nTesting endpoint: %s\n", worker.Name) testURL := worker.URL + "?url=" + url.QueryEscape(targetURL) client := &http.Client{Timeout: 30 * time.Second} req, err := http.NewRequest(method, testURL, nil) if err != nil { fmt.Printf(" ✗ Request failed: %v\n", err) continue } resp, err := client.Do(req) if err != nil { fmt.Printf(" ✗ Request failed: %v\n", err) continue } defer resp.Body.Close() if resp.StatusCode == 200 { successCount++ fmt.Printf(" ✓ Request successful! Status: %d\n", resp.StatusCode) body, _ := io.ReadAll(resp.Body) bodyStr := strings.TrimSpace(string(body)) // Try to extract IP from common formats if strings.Contains(targetURL, "ifconfig.me") { fmt.Printf(" Origin IP: %s\n", bodyStr) uniqueIPs[bodyStr] = true } else if strings.Contains(targetURL, "httpbin.org/ip") { var data map[string]interface{} if json.Unmarshal(body, &data) == nil { if origin, ok := data["origin"].(string); ok { fmt.Printf(" Origin IP: %s\n", origin) uniqueIPs[origin] = true } } } else { fmt.Printf(" Response Length: %d bytes\n", len(body)) } } else { fmt.Printf(" ✗ Request failed! Status: %d\n", resp.StatusCode) } } fmt.Printf("\nTest Results:\n") fmt.Printf(" Working endpoints: %d/%d\n", successCount, len(workers)) if len(uniqueIPs) > 0 { fmt.Printf(" Unique IP addresses: %d\n", len(uniqueIPs)) for ip := range uniqueIPs { fmt.Printf(" - %s\n", ip) } } return nil } func (ft *FlareTunnel) ExportConfig(outputFile string) error { if _, err := os.Stat("flaretunnel.json"); os.IsNotExist(err) { fmt.Println("❌ No configuration file found (flaretunnel.json)") return err } configData, err := os.ReadFile("flaretunnel.json") if err != nil { fmt.Printf("❌ Failed to read configuration: %v\n", err) return err } exportData := map[string]interface{}{ "exported_at": time.Now().Format(time.RFC3339), "config": json.RawMessage(configData), } data, err := json.MarshalIndent(exportData, "", " ") if err != nil { return err } if err := os.WriteFile(outputFile, data, 0644); err != nil { fmt.Printf("❌ Failed to write export file: %v\n", err) return err } var config Config json.Unmarshal(configData, &config) fmt.Printf("✅ Exported configuration to %s\n", outputFile) fmt.Println("\n📊 Summary:") fmt.Printf(" Accounts: %d\n", len(config.Accounts)) for _, acc := range config.Accounts { fmt.Printf(" • %s: %s...\n", acc.Name, acc.AccountID[:min(8, len(acc.AccountID))]) } fmt.Println("\n⚠️ WARNING: This file contains API tokens! Keep it secure! 🔒") return nil } func (ft *FlareTunnel) ImportConfig(inputFile string, merge bool) error { if _, err := os.Stat(inputFile); os.IsNotExist(err) { fmt.Printf("❌ Import file not found: %s\n", inputFile) return err } importData, err := os.ReadFile(inputFile) if err != nil { fmt.Printf("❌ Failed to read import file: %v\n", err) return err } // Remove BOM if present if len(importData) >= 3 && importData[0] == 0xEF && importData[1] == 0xBB && importData[2] == 0xBF { importData = importData[3:] } var importWrapper map[string]interface{} var newConfig Config if err := json.Unmarshal(importData, &importWrapper); err != nil { fmt.Printf("❌ Failed to parse import file: %v\n", err) return err } // Check if it's an export format or direct config if configData, ok := importWrapper["config"]; ok { configBytes, _ := json.Marshal(configData) json.Unmarshal(configBytes, &newConfig) } else { json.Unmarshal(importData, &newConfig) } if len(newConfig.Accounts) == 0 { fmt.Println("❌ No accounts found in import file") return fmt.Errorf("no accounts in import") } fmt.Printf("📥 Import file: %s\n", inputFile) fmt.Printf(" Accounts: %d\n\n", len(newConfig.Accounts)) for idx, acc := range newConfig.Accounts { fmt.Printf(" %d. %s: %s...\n", idx+1, acc.Name, acc.AccountID[:min(8, len(acc.AccountID))]) } var finalConfig Config if merge && fileExists("flaretunnel.json") { existingData, _ := os.ReadFile("flaretunnel.json") json.Unmarshal(existingData, &finalConfig) existingNames := make(map[string]bool) for _, acc := range finalConfig.Accounts { existingNames[acc.Name] = true } for _, acc := range newConfig.Accounts { if !existingNames[acc.Name] { finalConfig.Accounts = append(finalConfig.Accounts, acc) fmt.Printf("➕ Added account: %s\n", acc.Name) } else { fmt.Printf("⚠️ Skipped duplicate: %s\n", acc.Name) } } } else { finalConfig = newConfig } reader := bufio.NewReader(os.Stdin) fmt.Print("\nConfirm import? (y/N): ") confirm, _ := reader.ReadString('\n') if strings.ToLower(strings.TrimSpace(confirm)) != "y" { fmt.Println("Import cancelled.") return nil } data, _ := json.MarshalIndent(finalConfig, "", " ") if err := os.WriteFile("flaretunnel.json", data, 0644); err != nil { fmt.Printf("❌ Failed to save configuration: %v\n", err) return err } fmt.Println("\n✅ Configuration imported!") fmt.Printf("📊 Total accounts: %d\n", len(finalConfig.Accounts)) return nil } func min(a, b int) int { if a < b { return a } return b } func fileExists(path string) bool { _, err := os.Stat(path) return err == nil } // ==================================================================== // PROXY SERVER // ==================================================================== type ProxyServer struct { Host string Port int Workers []*Worker CurrentWorkerIndex int RotationMode string Verbose bool AllowIPAccess bool CACertPath string CAKeyPath string BlacklistPatterns []string InlineBlockPatterns []string BlacklistStats map[string]int UpstreamProxy string UpstreamVerifySSL bool CacheCerts bool NoSSLIntercept bool mutex sync.Mutex certCache map[string]*tls.Certificate certMutex sync.RWMutex } func NewProxyServer(host string, port int) *ProxyServer { return &ProxyServer{ Host: host, Port: port, RotationMode: "round-robin", BlacklistStats: make(map[string]int), certCache: make(map[string]*tls.Certificate), } } func (ps *ProxyServer) LoadWorkers(endpointsFile string, workerIndices []int) error { data, err := os.ReadFile(endpointsFile) if err != nil { return err } var allWorkers []*Worker if err := json.Unmarshal(data, &allWorkers); err != nil { return err } if len(workerIndices) > 0 { selected := []*Worker{} for _, idx := range workerIndices { if idx >= 0 && idx < len(allWorkers) { selected = append(selected, allWorkers[idx]) } } ps.Workers = selected } else { ps.Workers = allWorkers } return nil } func (ps *ProxyServer) LoadBlacklist(blacklistFile string) error { patterns := []string{} // Load from file if blacklistFile != "" { if _, err := os.Stat(blacklistFile); err == nil { file, err := os.Open(blacklistFile) if err == nil { defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line != "" && !strings.HasPrefix(line, "#") { patterns = append(patterns, line) } } if len(patterns) > 0 { fmt.Printf("✅ Loaded %d patterns from %s\n", len(patterns), blacklistFile) } } } } // Add inline patterns if len(ps.InlineBlockPatterns) > 0 { patterns = append(patterns, ps.InlineBlockPatterns...) fmt.Printf("✅ Added %d inline patterns\n", len(ps.InlineBlockPatterns)) } ps.BlacklistPatterns = patterns return nil } func (ps *ProxyServer) GetWorkerURL() string { if len(ps.Workers) == 0 { return "" } ps.mutex.Lock() defer ps.mutex.Unlock() if ps.RotationMode == "random" { return ps.Workers[time.Now().UnixNano()%int64(len(ps.Workers))].URL } else if ps.RotationMode == "round-robin" { worker := ps.Workers[ps.CurrentWorkerIndex] ps.CurrentWorkerIndex = (ps.CurrentWorkerIndex + 1) % len(ps.Workers) return worker.URL } return ps.Workers[0].URL } func (ps *ProxyServer) IsBlacklisted(targetURL string) bool { if len(ps.BlacklistPatterns) == 0 { return false } urlLower := strings.ToLower(targetURL) parsedURL, _ := url.Parse(targetURL) hostname := "" path := "" if parsedURL != nil { hostname = strings.ToLower(parsedURL.Hostname()) path = strings.ToLower(parsedURL.Path) } for _, pattern := range ps.BlacklistPatterns { patternLower := strings.ToLower(pattern) if strings.Contains(hostname, patternLower) || strings.Contains(path, patternLower) || strings.Contains(urlLower, patternLower) || strings.HasSuffix(urlLower, patternLower) { ps.mutex.Lock() ps.BlacklistStats[pattern]++ ps.mutex.Unlock() return true } } return false } func (ps *ProxyServer) HandleHTTP(w http.ResponseWriter, r *http.Request) { targetURL := r.URL.String() if !strings.HasPrefix(targetURL, "http://") && !strings.HasPrefix(targetURL, "https://") { targetURL = "http://" + r.Host + r.URL.String() } if ps.IsBlacklisted(targetURL) { if ps.Verbose { fmt.Printf("🚫 BLOCKED (blacklist): %s\n", targetURL) } http.Error(w, "Blacklisted", http.StatusForbidden) return } parsedURL, _ := url.Parse(targetURL) if !ps.AllowIPAccess && isIPAddress(parsedURL.Hostname()) { if ps.Verbose { fmt.Printf("🛡️ BLOCKED (IP): %s\n", parsedURL.Hostname()) } http.Error(w, "Direct IP access blocked", http.StatusForbidden) return } workerURL := ps.GetWorkerURL() if workerURL == "" { http.Error(w, "No workers available", http.StatusServiceUnavailable) return } proxyURL := workerURL + "?url=" + url.QueryEscape(targetURL) if ps.Verbose { fmt.Printf("\n📤 [%s] %s\n", r.Method, targetURL) fmt.Printf(" ↓ via Worker: %s\n", workerURL) } // Create proxy request proxyReq, err := http.NewRequest(r.Method, proxyURL, r.Body) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // Copy headers for k, v := range r.Header { if !strings.EqualFold(k, "Host") && !strings.EqualFold(k, "Connection") { proxyReq.Header[k] = v } } // Send request with optional upstream proxy transport := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: !ps.UpstreamVerifySSL}, } if ps.UpstreamProxy != "" { proxyURL, err := url.Parse(ps.UpstreamProxy) if err == nil { transport.Proxy = http.ProxyURL(proxyURL) } } client := &http.Client{ Timeout: 30 * time.Second, Transport: transport, CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }, } resp, err := client.Do(proxyReq) if err != nil { http.Error(w, err.Error(), http.StatusBadGateway) return } defer resp.Body.Close() // Copy response headers for k, v := range resp.Header { w.Header()[k] = v } w.WriteHeader(resp.StatusCode) io.Copy(w, resp.Body) if ps.Verbose { status := "✅" if resp.StatusCode >= 400 { status = "⚠️" } fmt.Printf(" ↑ %s %d\n", status, resp.StatusCode) } } func (ps *ProxyServer) HandleCONNECT(w http.ResponseWriter, r *http.Request) { if ps.CACertPath == "" { http.Error(w, "HTTPS not supported", http.StatusNotImplemented) return } host := r.Host hostname := strings.Split(host, ":")[0] if ps.Verbose { fmt.Printf("\n🔒 [CONNECT] %s\n", host) } // Get or generate certificate ps.certMutex.RLock() cert, exists := ps.certCache[hostname] ps.certMutex.RUnlock() if !exists { newCert, err := generateHostCert(hostname, ps.CACertPath, ps.CAKeyPath) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } cert = newCert ps.certMutex.Lock() ps.certCache[hostname] = cert ps.certMutex.Unlock() } // Send 200 Connection Established w.WriteHeader(http.StatusOK) // Hijack connection hijacker, ok := w.(http.Hijacker) if !ok { http.Error(w, "Hijacking not supported", http.StatusInternalServerError) return } clientConn, _, err := hijacker.Hijack() if err != nil { http.Error(w, err.Error(), http.StatusServiceUnavailable) return } defer clientConn.Close() // Wrap with TLS tlsConfig := &tls.Config{ Certificates: []tls.Certificate{*cert}, } tlsConn := tls.Server(clientConn, tlsConfig) defer tlsConn.Close() if err := tlsConn.Handshake(); err != nil { if ps.Verbose { fmt.Printf("✗ SSL handshake failed: %v\n", err) } return } // Read HTTPS request reader := bufio.NewReader(tlsConn) req, err := http.ReadRequest(reader) if err != nil { return } targetURL := "https://" + hostname + req.URL.String() if ps.IsBlacklisted(targetURL) { if ps.Verbose { fmt.Printf(" 🚫 BLOCKED (blacklist): %s\n", targetURL) } return } workerURL := ps.GetWorkerURL() if workerURL == "" { return } proxyURL := workerURL + "?url=" + url.QueryEscape(targetURL) if ps.Verbose { fmt.Printf(" 📤 [%s] %s\n", req.Method, targetURL) fmt.Printf(" ↓ via Worker: %s\n", workerURL) } // Create proxy request proxyReq, err := http.NewRequest(req.Method, proxyURL, req.Body) if err != nil { return } for k, v := range req.Header { if !strings.EqualFold(k, "Host") && !strings.EqualFold(k, "Connection") { proxyReq.Header[k] = v } } // Setup transport with optional upstream proxy transport := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: !ps.UpstreamVerifySSL}, } if ps.UpstreamProxy != "" { upstreamURL, err := url.Parse(ps.UpstreamProxy) if err == nil { transport.Proxy = http.ProxyURL(upstreamURL) } } client := &http.Client{ Timeout: 30 * time.Second, Transport: transport, CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }, } resp, err := client.Do(proxyReq) if err != nil { return } defer resp.Body.Close() // Write response tlsConn.Write([]byte(fmt.Sprintf("HTTP/1.1 %d %s\r\n", resp.StatusCode, resp.Status))) for k, v := range resp.Header { for _, vv := range v { tlsConn.Write([]byte(fmt.Sprintf("%s: %s\r\n", k, vv))) } } tlsConn.Write([]byte("\r\n")) io.Copy(tlsConn, resp.Body) if ps.Verbose { status := "✅" if resp.StatusCode >= 400 { status = "⚠️" } fmt.Printf(" ↑ %s %d\n", status, resp.StatusCode) } } func (ps *ProxyServer) Start(blacklistFile string) error { // Setup SSL ps.CACertPath = "flaretunnel_ca.crt" ps.CAKeyPath = "flaretunnel_ca.key" if !ps.NoSSLIntercept { if err := generateCACert(ps.CACertPath, ps.CAKeyPath); err != nil { fmt.Printf("⚠️ SSL setup failed: %v\n", err) ps.CACertPath = "" } } else { ps.CACertPath = "" } // Load blacklist ps.LoadBlacklist(blacklistFile) fmt.Println("\n" + strings.Repeat("=", 80)) fmt.Println("🚀 FlareTunnel Tunnel Server Started") fmt.Println(strings.Repeat("=", 80)) fmt.Printf("📡 Listening: %s:%d\n", ps.Host, ps.Port) fmt.Printf("⚙️ Workers: %d\n", len(ps.Workers)) fmt.Printf("🔄 Rotation: %s\n", ps.RotationMode) if ps.NoSSLIntercept { fmt.Printf("🔒 SSL/HTTPS: ✗ Disabled by --no-ssl-intercept\n") } else if ps.CACertPath != "" { fmt.Printf("🔒 SSL/HTTPS: ✓ Enabled (HTTPS CONNECT supported)\n") } else { fmt.Printf("🔒 SSL/HTTPS: ✗ Disabled\n") } if ps.AllowIPAccess { fmt.Printf("🛡️ IP Blocking: ❌ Disabled (--unsafe)\n") } else { fmt.Printf("🛡️ IP Blocking: ✅ Enabled (saves Worker requests)\n") } blacklistMsg := fmt.Sprintf("🚫 Blacklist: %d pattern(s)", len(ps.BlacklistPatterns)) if blacklistFile == "blacklist-minimal.txt" && len(ps.BlacklistPatterns) > 0 { blacklistMsg += " ✅ (default: blacklist-minimal.txt)" } fmt.Println(blacklistMsg) if ps.UpstreamProxy != "" { fmt.Printf("🔗 Upstream: %s\n", ps.UpstreamProxy) } else { fmt.Printf("🔗 Upstream: ❌ None (direct to Workers)\n") } if ps.Verbose { fmt.Printf("📝 Verbose: ✅ Enabled\n") } else { fmt.Printf("📝 Verbose: ❌ Disabled\n") } fmt.Println("\n📋 Selected Workers:") for idx, worker := range ps.Workers { workerName := worker.Name if len(workerName) > 50 { workerName = workerName[:47] + "..." } fmt.Printf(" [%d] %s\n", idx, workerName) } if ps.UpstreamProxy != "" { fmt.Println("\n🔀 Request Flow:") fmt.Printf(" Client → FlareTunnel:%d → %s → Cloudflare Workers → Target\n", ps.Port, ps.UpstreamProxy) } fmt.Println("\n⚙️ Proxy Configuration:") fmt.Printf(" HTTP Proxy: %s:%d\n", ps.Host, ps.Port) fmt.Printf(" HTTPS Proxy: %s:%d\n", ps.Host, ps.Port) if ps.CACertPath != "" { fmt.Println("\n🔐 For HTTPS without warnings, install CA certificate:") fmt.Printf(" 📄 File: %s\n", ps.CACertPath) fmt.Printf(" 💡 Or use verify=False in code\n") } fmt.Println("\n💡 Tips:") if len(ps.BlacklistPatterns) > 0 { fmt.Println(" • Blacklist active - saving Worker requests! 💰") } else { fmt.Println(" • No blacklist - use --blacklist blacklist-minimal.txt to save requests") } if !ps.AllowIPAccess { fmt.Println(" • IP blocking active - Cloudflare doesn't support IPs anyway") } fmt.Println(" • Press Ctrl+C to stop") fmt.Println(strings.Repeat("=", 80)) fmt.Println() // Start server handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodConnect { ps.HandleCONNECT(w, r) } else { ps.HandleHTTP(w, r) } }) server := &http.Server{ Addr: fmt.Sprintf("%s:%d", ps.Host, ps.Port), Handler: handler, } return server.ListenAndServe() } // ==================================================================== // UTILITY FUNCTIONS // ==================================================================== func generateWorkerName() string { timestamp := time.Now().Unix() suffix := randomString(6) return fmt.Sprintf("flaretunnel-%d-%s", timestamp, suffix) } func randomString(n int) string { const letters = "abcdefghijklmnopqrstuvwxyz" b := make([]byte, n) for i := range b { b[i] = letters[time.Now().UnixNano()%int64(len(letters))] time.Sleep(1 * time.Nanosecond) } return string(b) } func isIPAddress(hostname string) bool { host := strings.Split(hostname, ":")[0] ip := net.ParseIP(host) return ip != nil } func testWorker(workerURL string) bool { testURL := workerURL + "?url=" + url.QueryEscape("https://httpbin.org/status/200") client := &http.Client{Timeout: 5 * time.Second} resp, err := client.Get(testURL) if err != nil { return false } defer resp.Body.Close() return resp.StatusCode == 200 } func max(a, b int) int { if a > b { return a } return b } func sumMap(m map[string]int) int { sum := 0 for _, v := range m { sum += v } return sum } // Simple multipart writer type simpleMultipartWriter struct { buf *bytes.Buffer boundary string } func multipartWriter(buf *bytes.Buffer) *simpleMultipartWriter { return &simpleMultipartWriter{ buf: buf, boundary: "----FlareTunnelBoundary" + randomString(16), } } func (w *simpleMultipartWriter) WriteField(field, value string) { w.buf.WriteString("--" + w.boundary + "\r\n") w.buf.WriteString(fmt.Sprintf("Content-Disposition: form-data; name=\"%s\"\r\n\r\n", field)) w.buf.WriteString(value + "\r\n") } func (w *simpleMultipartWriter) Close() { w.buf.WriteString("--" + w.boundary + "--\r\n") } func (w *simpleMultipartWriter) FormDataContentType() string { return "multipart/form-data; boundary=" + w.boundary } // ==================================================================== // CLI COMMANDS // ==================================================================== func setupConfig() error { fmt.Println(strings.Repeat("=", 70)) fmt.Println("🔧 FlareTunnel Multi-Account Configuration") fmt.Println(strings.Repeat("=", 70)) fmt.Println() fmt.Println("Getting Cloudflare Credentials:") fmt.Println("1. Sign up at https://cloudflare.com") fmt.Println("2. Go to https://dash.cloudflare.com/profile/api-tokens") fmt.Println("3. Click Create Token - use 'Edit Cloudflare Workers' template") fmt.Println("4. Set account and zone resources to all") fmt.Println("5. Copy the token and Account ID") fmt.Println() fmt.Println("💡 Tip: You can add multiple accounts for more quota!") fmt.Println(" Each account = 100,000 requests/day") fmt.Println() accounts := []Account{} reader := bufio.NewReader(os.Stdin) for accountNum := 1; ; accountNum++ { fmt.Printf("\n📋 Account #%d\n", accountNum) fmt.Println(strings.Repeat("-", 40)) fmt.Printf("Account name (e.g., 'main', 'backup') [account-%d]: ", accountNum) accountName, _ := reader.ReadString('\n') accountName = strings.TrimSpace(accountName) if accountName == "" { accountName = fmt.Sprintf("account-%d", accountNum) } fmt.Print("API token: ") apiToken, _ := reader.ReadString('\n') apiToken = strings.TrimSpace(apiToken) if apiToken == "" { if accountNum == 1 { fmt.Println("❌ API token is required") return fmt.Errorf("no API token provided") } break } fmt.Print("Account ID: ") accountID, _ := reader.ReadString('\n') accountID = strings.TrimSpace(accountID) if accountID == "" { if accountNum == 1 { fmt.Println("❌ Account ID is required") return fmt.Errorf("no account ID provided") } break } accounts = append(accounts, Account{ Name: accountName, APIToken: apiToken, AccountID: accountID, }) fmt.Printf("✅ Account '%s' added!\n", accountName) if accountNum >= 1 { fmt.Print("\nAdd another account? (y/N): ") addMore, _ := reader.ReadString('\n') if strings.ToLower(strings.TrimSpace(addMore)) != "y" { break } } } if len(accounts) == 0 { fmt.Println("❌ No accounts configured") return fmt.Errorf("no accounts configured") } config := Config{Accounts: accounts} data, _ := json.MarshalIndent(config, "", " ") if err := os.WriteFile("flaretunnel.json", data, 0644); err != nil { fmt.Printf("❌ Error saving configuration: %v\n", err) return err } fmt.Println() fmt.Println(strings.Repeat("=", 70)) fmt.Println("✅ Configuration saved!") fmt.Println(strings.Repeat("=", 70)) fmt.Println("📄 Config file: flaretunnel.json") fmt.Printf("📊 Accounts configured: %d\n", len(accounts)) fmt.Printf("🚀 Total daily quota: %d requests\n", len(accounts)*100000) fmt.Println() for _, acc := range accounts { fmt.Printf(" • %s: %s...\n", acc.Name, acc.AccountID[:8]) } fmt.Println() fmt.Println("🎉 FlareTunnel is now configured!") return nil } func parseWorkerIndices(indicesStr string) []int { indices := []int{} parts := strings.Split(indicesStr, ",") for _, part := range parts { part = strings.TrimSpace(part) if strings.Contains(part, "-") { rangeParts := strings.Split(part, "-") if len(rangeParts) == 2 { start, err1 := strconv.Atoi(strings.TrimSpace(rangeParts[0])) end, err2 := strconv.Atoi(strings.TrimSpace(rangeParts[1])) if err1 == nil && err2 == nil { for i := start; i <= end; i++ { indices = append(indices, i) } } } } else { if idx, err := strconv.Atoi(part); err == nil { indices = append(indices, idx) } } } return indices } // ==================================================================== // MAIN // ==================================================================== func main() { if len(os.Args) < 2 { printHelp() return } command := os.Args[1] switch command { case "config": setupConfig() case "create": count := 1 accountName := "" distribute := false for i := 2; i < len(os.Args); i++ { switch os.Args[i] { case "--count": if i+1 < len(os.Args) { count, _ = strconv.Atoi(os.Args[i+1]) i++ } case "--account": if i+1 < len(os.Args) { accountName = os.Args[i+1] i++ } case "--distribute": distribute = true } } ft, err := NewFlareTunnel("flaretunnel.json") if err != nil { fmt.Printf("❌ Configuration error: %v\n", err) fmt.Println("Run: go run FlareTunnel.go config") return } ft.CreateWorkers(count, accountName, distribute) case "list": verbose := false checkStatus := false for i := 2; i < len(os.Args); i++ { switch os.Args[i] { case "--verbose", "-v": verbose = true case "--status": checkStatus = true } } // If both flags provided, verbose already includes status check if verbose && checkStatus { fmt.Println("⚠️ Note: --verbose already includes live status check, --status flag is redundant\n") // checkStatus is already covered by verbose, no need to set it } ft, err := NewFlareTunnel("flaretunnel.json") if err != nil { fmt.Printf("❌ Configuration error: %v\n", err) return } ft.ListWorkers(verbose, checkStatus) case "test": targetURL := "https://ifconfig.me/ip" method := "GET" for i := 2; i < len(os.Args); i++ { switch os.Args[i] { case "--url": if i+1 < len(os.Args) { targetURL = os.Args[i+1] i++ } case "--method": if i+1 < len(os.Args) { method = os.Args[i+1] i++ } } } ft, err := NewFlareTunnel("flaretunnel.json") if err != nil { fmt.Printf("❌ Configuration error: %v\n", err) return } ft.TestWorkers(targetURL, method) case "export": outputFile := "flaretunnel_config_backup.json" for i := 2; i < len(os.Args); i++ { switch os.Args[i] { case "--output": if i+1 < len(os.Args) { outputFile = os.Args[i+1] i++ } } } ft, _ := NewFlareTunnel("flaretunnel.json") ft.ExportConfig(outputFile) case "import": inputFile := "" merge := false for i := 2; i < len(os.Args); i++ { switch os.Args[i] { case "--input": if i+1 < len(os.Args) { inputFile = os.Args[i+1] i++ } case "--merge": merge = true } } if inputFile == "" { fmt.Println("❌ Error: --input required for import command") fmt.Println("Usage: go run FlareTunnel.go import --input config_backup.json [--merge]") return } ft := &FlareTunnel{} ft.ImportConfig(inputFile, merge) case "cleanup": accountName := "" for i := 2; i < len(os.Args); i++ { switch os.Args[i] { case "--account": if i+1 < len(os.Args) { accountName = os.Args[i+1] i++ } } } ft, err := NewFlareTunnel("flaretunnel.json") if err != nil { fmt.Printf("❌ Configuration error: %v\n", err) return } reader := bufio.NewReader(os.Stdin) if accountName != "" { fmt.Printf("Delete ALL workers from account '%s'? (y/N): ", accountName) } else { fmt.Print("Delete ALL FlareTunnel endpoints from ALL accounts? (y/N): ") } confirm, _ := reader.ReadString('\n') if strings.ToLower(strings.TrimSpace(confirm)) == "y" { ft.CleanupWorkers(accountName) } else { fmt.Println("Cleanup cancelled.") } case "tunnel": host := "127.0.0.1" port := 8080 workersStr := "" mode := "round-robin" verbose := false unsafe := false blacklist := "blacklist-minimal.txt" upstreamProxy := "" upstreamVerifySSL := false cacheCerts := false noSSLIntercept := false blockPatterns := []string{} for i := 2; i < len(os.Args); i++ { switch os.Args[i] { case "--host": if i+1 < len(os.Args) { host = os.Args[i+1] i++ } case "--port": if i+1 < len(os.Args) { port, _ = strconv.Atoi(os.Args[i+1]) i++ } case "--workers": if i+1 < len(os.Args) { workersStr = os.Args[i+1] i++ } case "--mode": if i+1 < len(os.Args) { mode = os.Args[i+1] i++ } case "--verbose", "-v": verbose = true case "--unsafe": unsafe = true case "--blacklist": if i+1 < len(os.Args) { blacklist = os.Args[i+1] i++ } case "--upstream-proxy": if i+1 < len(os.Args) { upstreamProxy = os.Args[i+1] i++ } case "--upstream-verify-ssl": upstreamVerifySSL = true case "--cache-certs": cacheCerts = true case "--no-ssl-intercept": noSSLIntercept = true case "--block": if i+1 < len(os.Args) { blockPatterns = append(blockPatterns, os.Args[i+1]) i++ } } } ps := NewProxyServer(host, port) ps.RotationMode = mode ps.Verbose = verbose ps.AllowIPAccess = unsafe ps.UpstreamProxy = upstreamProxy ps.UpstreamVerifySSL = upstreamVerifySSL ps.CacheCerts = cacheCerts ps.NoSSLIntercept = noSSLIntercept // Add inline block patterns if len(blockPatterns) > 0 { ps.InlineBlockPatterns = blockPatterns } var workerIndices []int if workersStr != "" { workerIndices = parseWorkerIndices(workersStr) } if err := ps.LoadWorkers("flaretunnel_endpoints.json", workerIndices); err != nil { fmt.Printf("❌ Failed to load workers: %v\n", err) fmt.Println("Run: go run FlareTunnel.go create --count 5") return } if len(ps.Workers) == 0 { fmt.Println("❌ No workers available") return } ps.Start(blacklist) default: printHelp() } } func printHelp() { help := ` FlareTunnel - Cloudflare Workers Proxy System (Go Version) Usage: go run FlareTunnel.go [options] Commands: config Configure Cloudflare API credentials (interactive) create Create new Cloudflare Workers list List all Workers with analytics test Test Workers with a target URL export Export configuration to backup file import Import configuration from backup file cleanup Delete Workers (prompts for confirmation) tunnel Start local proxy server Examples: # Configuration go run FlareTunnel.go config # Create Workers go run FlareTunnel.go create --count 5 go run FlareTunnel.go create --count 10 --distribute go run FlareTunnel.go create --count 3 --account main # List Workers go run FlareTunnel.go list # Basic list go run FlareTunnel.go list --verbose # Detailed + live status check go run FlareTunnel.go list --status # Only live response times # Test Workers go run FlareTunnel.go test go run FlareTunnel.go test --url https://httpbin.org/ip go run FlareTunnel.go test --url https://example.com --method POST # Export/Import Config go run FlareTunnel.go export --output my_backup.json go run FlareTunnel.go import --input my_backup.json go run FlareTunnel.go import --input config.json --merge # Start Tunnel go run FlareTunnel.go tunnel --verbose go run FlareTunnel.go tunnel --workers 0,1,2 --mode random go run FlareTunnel.go tunnel --port 9090 --blacklist blacklist.txt go run FlareTunnel.go tunnel --upstream-proxy http://127.0.0.1:8888 # Cleanup go run FlareTunnel.go cleanup go run FlareTunnel.go cleanup --account main Options: Create: --count N Number of workers to create (default: 1) --account NAME Create on specific account --distribute Auto-distribute across accounts based on quota List: --verbose, -v Show detailed info + live status check (created, age, response times) --status Check only live worker status (response times, no verbose info) Test: --url URL Target URL to test (default: https://ifconfig.me/ip) --method METHOD HTTP method (default: GET) Export: --output FILE Output file (default: flaretunnel_config_backup.json) Import: --input FILE Input config backup file (REQUIRED) --merge Merge with existing config (skip duplicates) Cleanup: --account NAME Delete from specific account only Tunnel: --host HOST Bind host (default: 127.0.0.1) --port PORT Bind port (default: 8080) --workers INDICES Worker indices (e.g., '0,1,2' or '0-2') --mode MODE Rotation mode: random, round-robin (default: round-robin) --verbose, -v Enable verbose logging --unsafe Allow IP access (not recommended) --upstream-proxy URL Upstream proxy (e.g., http://127.0.0.1:8080) --upstream-verify-ssl Verify SSL for upstream proxy --cache-certs Cache SSL certs to disk --no-ssl-intercept Disable SSL interception --blacklist FILE Blacklist file (default: blacklist-minimal.txt) --block PATTERN Block pattern (can be used multiple times) ` fmt.Println(help) } ================================================ FILE: README.md ================================================ # 🚀 FlareTunnel
FlareTunnel **A unified proxy system that routes traffic through Cloudflare Workers for IP rotation and anonymity** ``` Client → FlareTunnel (local) → Cloudflare Workers → Target Website ``` ![FlareTunnel Banner](https://img.shields.io/badge/FlareTunnel-v1.0.0-red?style=for-the-badge&logo=security&logoColor=white) ![Go](https://img.shields.io/badge/Go-1.19+-00ADD8?style=for-the-badge&logo=go&logoColor=white) ![License](https://img.shields.io/badge/License-Non--Commercial-orange?style=for-the-badge)
**FlareTunnel** is a powerful, unified proxy system that leverages **Cloudflare Workers** to create a robust, rotating proxy network. It allows you to route your traffic through Cloudflare's global edge network, providing high anonymity, speed, and reliability. ## ✨ Features * **🌐 Unlimited Rotating Proxies**: Automatically deploy and manage multiple Cloudflare Workers as proxy endpoints. * **🔄 Smart Load Balancing**: Distributes traffic across your workers using Random or Round-Robin strategies. * **⚡ High Performance**: Uses Cloudflare's global edge network for low latency. * **🔐 SSL/HTTPS Support**: Full support for HTTPS traffic with optional SSL interception for deep inspection. * **👥 Multi-Account Support**: seamless management of multiple Cloudflare accounts to maximize request quotas (100k requests/day per account). * **🛡️ Ad & Tracker Blocking**: Built-in blacklist system to block unwanted traffic and save worker quotas. * **📊 Analytics**: Real-time usage statistics and quota tracking per account. ## 🏗️ Architecture ```mermaid graph LR Client["Client (Browser/App)"] -->|HTTP/HTTPS| LocalProxy["Local Proxy :8080"] subgraph FlareTunnel System LocalProxy -->|Load Balancing| Rotator{Worker Rotator} Rotator -->|Request A| W1[Worker 1] Rotator -->|Request B| W2[Worker 2] Rotator -->|Request C| W3[Worker 3] end W1 -->|Fetch| Target[Target Website] W2 -->|Fetch| Target W3 -->|Fetch| Target style LocalProxy fill:#7289da,stroke:#333,stroke-width:2px,color:white style W1 fill:#f38020,stroke:#333,stroke-width:2px,color:white style W2 fill:#f38020,stroke:#333,stroke-width:2px,color:white style W3 fill:#f38020,stroke:#333,stroke-width:2px,color:white style Client fill:#fff,stroke:#333,stroke-width:2px style Target fill:#fff,stroke:#333,stroke-width:2px ``` ## 📦 Installation ### Build from Source ```bash git clone https://github.com/MorDavid/FlareTunnel.git cd FlareTunnel go build -o FlareTunnel FlareTunnel.go ``` ## 🚀 Usage ### 1. Configuration First, set up your Cloudflare credentials. You'll need your Account ID and an API Token (with "Edit Cloudflare Workers" permission). ```bash ./FlareTunnel config ``` ### 2. Create Proxies Deploy new workers to your Cloudflare account. ```bash # Create 5 new proxy workers ./FlareTunnel create --count 5 ``` ### 3. Start the Tunnel Start the local proxy server. By default, it runs on `localhost:8080`. ```bash ./FlareTunnel tunnel ``` Now configure your browser or application to use the proxy: * **Host**: `127.0.0.1` * **Port**: `8080` ## 🛠️ Commands Reference | Command | Description | |---------|-------------| | `config` | Configure Cloudflare API credentials (supports multiple accounts) | | `create` | Deploy new Worker proxies | | `list` | List all active proxies and show usage stats | | `tunnel` | Start the local proxy server | | `test` | Test connectivity of your proxies | | `cleanup` | Delete all workers from your account | ## 📖 Basic Usage ### Browser Configuration ``` HTTP Proxy: 127.0.0.1:8080 HTTPS Proxy: 127.0.0.1:8080 ``` ### Python ```python import requests import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) proxies = { 'http': 'http://127.0.0.1:8080', 'https': 'http://127.0.0.1:8080' } r = requests.get("https://httpbin.org/ip", proxies=proxies, verify=False) print(r.json()['origin']) # Cloudflare Worker IP ``` ### Quick Test ```bash ./FlareTunnel test ``` --- ## 🎯 Common Commands ```bash # Worker Management ./FlareTunnel list # List all workers ./FlareTunnel list --verbose # Detailed view (created, age, live status) ./FlareTunnel list --status # Check worker response times ./FlareTunnel test # Test workers ./FlareTunnel cleanup # Delete workers from ALL accounts ./FlareTunnel cleanup --account main # Delete workers from 'main' only # Multi-Account Worker Creation ./FlareTunnel create --count 10 --distribute # Auto-distribute based on quota ./FlareTunnel create --count 5 --account main # Create on specific account # Configuration Backup & Restore ./FlareTunnel export # Export config (accounts + credentials) ./FlareTunnel import --input config.json # Import config (replace) ./FlareTunnel import --input config.json --merge # Merge with existing # Tunnel (Proxy Server) ./FlareTunnel tunnel --verbose # Basic ./FlareTunnel tunnel --workers 0-2 # Specific workers ./FlareTunnel tunnel --mode random # Random rotation # With Blacklist (Recommended!) ./FlareTunnel tunnel --verbose # Default: blacklist-minimal.txt ./FlareTunnel tunnel --blacklist blacklist.txt --verbose # With Burp Suite ./FlareTunnel tunnel --port 9090 --upstream-proxy http://127.0.0.1:8080 --verbose ``` --- ## 💡 Blacklist System ### blacklist-minimal.txt (Default) ⚡ ``` ✅ Analytics (google-analytics, mixpanel) ✅ Images (.jpg, .png, .gif, etc.) ✅ Fonts (.woff, .ttf, etc.) ✅ Source maps (.map) Saves: ~30-40% Worker requests Website: Works perfectly in browser ``` ### blacklist.txt (Full) 🔥 ``` ✅ Everything in minimal ✅ Advertising ✅ Social tracking ✅ CSS/JS files ✅ CDN libraries Saves: ~60-70% Worker requests Website: May look broken (missing assets) ``` ### blacklist-aggressive.txt (Maximum) 💪 ``` ✅ Everything in full ✅ Almost everything except HTML/API Saves: ~80-90% Worker requests Website: Will break in browser (automation tools only) ``` --- ## 🌟 Star History [![Star History Chart](https://api.star-history.com/svg?repos=MorDavid/FlareTunnel&type=date&legend=top-left)](https://www.star-history.com/#MorDavid/FlareTunnel&type=date&legend=top-left) ## ⚠️ Disclaimer This tool is for educational and research purposes only. Please respect Cloudflare's Terms of Service. The authors are not responsible for any misuse of this tool. **Made with ❤️ for the security and automation community** ================================================ FILE: blacklist-aggressive.txt ================================================ # FlareTunnel Blacklist - Aggressive Version 🔥💪 # Blocks almost everything except HTML/API requests # Useful for automation tools (scrapers, bruteforce, etc.) # Website will look broken in browsers - but you'll save hundreds of thousands of Worker requests! # ======================================== # 📊 Analytics & Tracking # ======================================== google-analytics.com www.google-analytics.com ssl.google-analytics.com analytics.google.com stats.g.doubleclick.net doubleclick.net googletagmanager.com googletagservices.com facebook.com/tr connect.facebook.net graph.facebook.com px.ads.linkedin.com analytics.linkedin.com analytics.tiktok.com business.tiktok.com ads.tiktok.com log.byteoversea.com hotjar.com static.hotjar.com script.hotjar.com api.mixpanel.com decide.mixpanel.com segment.com cdn.segment.com api.segment.io omtrdc.net adobedc.net demdex.net track.hubspot.com api.hubspot.com fullstory.com script.crazyegg.com js-agent.newrelic.com bam.nr-data.net datadoghq-browser-agent.com browser-intake-datadoghq.com sentry.io browser.sentry-cdn.com criteo.com taboola.com outbrain.com mixpanel.com amplitude.com heap.io logrocket.com newrelic.com quantcast.com clarity.ms datadoghq.com pendo.io mouseflow.com crazyegg.com optimizely.com kissmetrics.com chartbeat.com # ======================================== # 📢 Advertising # ======================================== googlesyndication.com googleadservices.com adnxs.com criteo.com outbrain.com taboola.com media.net pubmatic.com rubiconproject.com adroll.com adsystem.com # ======================================== # 🖼️ ALL Images # ======================================== .jpg .jpeg .png .gif .webp .ico .svg .bmp .tiff .avif # ======================================== # 🎨 ALL CSS & Fonts # ======================================== .css .woff .woff2 .ttf .eot .otf fonts.googleapis.com fonts.gstatic.com # ======================================== # 📦 ALL JavaScript # ======================================== .js .map .min.js .bundle.js ajax.googleapis.com cdnjs.cloudflare.com cdn.jsdelivr.net unpkg.com # ======================================== # 🎬 ALL Media # ======================================== .mp4 .webm .ogg .mp3 .wav .m4a # ======================================== # 📄 Documents # ======================================== .pdf .zip .rar .doc .docx .xls .xlsx # ======================================== # 👥 Social Media # ======================================== facebook.com/plugins twitter.com/widgets platform.twitter.com linkedin.com/widgets instagram.com/embed pinterest.com tiktok.com/embed # ======================================== # 🔌 ALL Widgets # ======================================== disqus.com livechatinc.com tawk.to intercom.io drift.com zendesk.com /chat /livechat /widget # ======================================== # 🎯 Tracking Paths # ======================================== /pixel /track /analytics /beacon /log /collect /event /_ga /_gat /gtag /impression ================================================ FILE: blacklist-minimal.txt ================================================ # ======================================== # 📊 Analytics (Saves the most!) # ======================================== google-analytics.com www.google-analytics.com ssl.google-analytics.com analytics.google.com stats.g.doubleclick.net doubleclick.net googletagmanager.com googletagservices.com facebook.com/tr connect.facebook.net graph.facebook.com px.ads.linkedin.com analytics.linkedin.com analytics.tiktok.com business.tiktok.com ads.tiktok.com log.byteoversea.com hotjar.com static.hotjar.com script.hotjar.com api.mixpanel.com decide.mixpanel.com segment.com cdn.segment.com api.segment.io omtrdc.net adobedc.net demdex.net track.hubspot.com api.hubspot.com fullstory.com script.crazyegg.com js-agent.newrelic.com bam.nr-data.net datadoghq-browser-agent.com browser-intake-datadoghq.com sentry.io browser.sentry-cdn.com criteo.com taboola.com outbrain.com # ======================================== # 🎨 Fonts # ======================================== fonts.googleapis.com fonts.gstatic.com use.typekit.net use.fontawesome.com ================================================ FILE: blacklist.txt ================================================ # FlareTunnel Blacklist - Full Version 🔥 # Save Worker Requests! 💰 # Each line = one pattern. Lines with # = comments # Patterns checked against: hostname, path, extension, full URL # ======================================== # 📊 Analytics & Tracking (Saves a lot!) # ======================================== google-analytics.com googletagmanager.com analytics.google.com doubleclick.net stats.g.doubleclick.net facebook.com/tr connect.facebook.net hotjar.com static.hotjar.com mixpanel.com cdn.mxpnl.com segment.com cdn.segment.com amplitude.com heap.io fullstory.com logrocket.com logrocket.io newrelic.com js-agent.newrelic.com quantcast.com sentry.io sentry-cdn.com clarity.ms datadoghq.com pendo.io mouseflow.com crazyegg.com optimizely.com kissmetrics.com chartbeat.com parsely.com omniconvert.com # ======================================== # 📢 Advertising & Marketing # ======================================== googlesyndication.com googleadservices.com adservice.google.com adnxs.com ads.yahoo.com advertising.com adsystem.com adroll.com criteo.com criteo.net outbrain.com taboola.com media.net yieldmo.com pubmatic.com rubiconproject.com openx.net indexww.com contextweb.com turn.com adform.net smartadserver.com serving-sys.com adsafeprotected.com doubleverify.com moatads.com # ======================================== # 👥 Social Media Tracking # ======================================== facebook.com/plugins twitter.com/widgets platform.twitter.com linkedin.com/widgets platform.linkedin.com instagram.com/embed pinterest.com/widgets snapchat.com/widgets tiktok.com/embed reddit.com/static # ======================================== # 🖼️ Image Formats (Saves a lot!) # ======================================== .jpg .jpeg .png .gif .webp .ico .svg .bmp .tiff .avif .jfif # ======================================== # 🎨 Fonts & CSS # ======================================== .woff .woff2 .ttf .eot .otf .css fonts.googleapis.com fonts.gstatic.com use.typekit.net cloud.typography.com # ======================================== # 📦 JavaScript & Maps # ======================================== .js .map .min.js .bundle.js # ======================================== # 🎬 Media Files (Video/Audio) # ======================================== .mp4 .webm .ogg .mp3 .wav .m4a .flac .aac # ======================================== # 📄 Documents & Archives # ======================================== .pdf .zip .rar .7z .tar .gz .doc .docx .xls .xlsx .ppt .pptx # ======================================== # 🎯 Common Tracking Paths # ======================================== /pixel /pixel.gif /track /tracking /analytics /beacon /log /collect /event /events /telemetry /metrics /stats /ping /heartbeat /_ga /_gat /_gid /gtag /fbevents /tr.gif /impression /view /click # ======================================== # 🌐 CDN & Static Assets # ======================================== cdnjs.cloudflare.com/ajax/libs cdn.jsdelivr.net unpkg.com stackpath.bootstrapcdn.com maxcdn.bootstrapcdn.com ajax.googleapis.com/ajax/libs # ======================================== # 🔌 Third-Party Widgets # ======================================== disqus.com zopim.com livechatinc.com tawk.to intercom.io drift.com olark.com zendesk.com/embeddable helpscout.com uservoice.com # ======================================== # 🎮 Gaming/App Analytics # ======================================== unity3d.com/stats unityads.unity3d.com api.gameanalytics.com firebase.google.com # ======================================== # 🛡️ Security/Bot Detection (Careful!) # ======================================== # Note: Blocking these may break some websites # recaptcha.net # google.com/recaptcha # hcaptcha.com # cloudflare.com/cdn-cgi # ======================================== # 🎯 Specific Trackers # ======================================== bat.bing.com t.co bit.ly ow.ly goo.gl tinyurl.com # ======================================== # 📱 Mobile Analytics # ======================================== app-measurement.com firebaselogging.googleapis.com crashlytics.com fabric.io # ======================================== # 💬 Chat Widgets # ======================================== /chat.js /livechat /widget /embed.js # ======================================== # ⚙️ Add your own patterns here: # ======================================== # Examples: # example-tracker.com # .mp4 # /my-custom-tracking-path ================================================ FILE: build.bat ================================================ @echo off REM Build script for FlareTunnel Go version (Windows) echo 🔨 Building FlareTunnel... echo. echo 📦 Downloading dependencies... go mod download if errorlevel 1 ( echo ❌ Failed to download dependencies pause exit /b 1 ) echo. echo 🏗️ Building for Windows... go build -ldflags="-s -w" -o flaretunnel.exe flaretunnel.go if errorlevel 1 ( echo ❌ Build failed pause exit /b 1 ) echo. echo ✅ Build complete: flaretunnel.exe echo. echo 🚀 Quick start: echo flaretunnel.exe config # Configure accounts echo flaretunnel.exe create --count 5 # Create workers echo flaretunnel.exe list --verbose # List workers echo flaretunnel.exe tunnel --verbose # Start proxy echo. set /p REPLY="Build for other platforms? (y/N) " if /i "%REPLY%"=="y" ( echo. echo 🌍 Building for multiple platforms... echo Building for Linux (amd64)... set GOOS=linux set GOARCH=amd64 go build -ldflags="-s -w" -o flaretunnel-linux-amd64 flaretunnel.go echo Building for macOS (amd64)... set GOOS=darwin set GOARCH=amd64 go build -ldflags="-s -w" -o flaretunnel-macos-amd64 flaretunnel.go echo Building for macOS (arm64)... set GOOS=darwin set GOARCH=arm64 go build -ldflags="-s -w" -o flaretunnel-macos-arm64 flaretunnel.go echo. echo ✅ Cross-compilation complete! echo. echo 📦 Built binaries: dir /b flaretunnel-* echo. ) echo ✨ Done! pause ================================================ FILE: build.sh ================================================ #!/bin/bash # Build script for FlareTunnel Go version set -e echo "🔨 Building FlareTunnel..." # Get dependencies echo "📦 Downloading dependencies..." go mod download # Build for current platform echo "🏗️ Building for current platform..." go build -ldflags="-s -w" -o flaretunnel flaretunnel.go echo "✅ Build complete: ./flaretunnel" echo "" echo "📊 Binary size:" ls -lh flaretunnel | awk '{print $5}' echo "" echo "🚀 Quick start:" echo " ./flaretunnel config # Configure accounts" echo " ./flaretunnel create --count 5 # Create workers" echo " ./flaretunnel list --verbose # List workers" echo " ./flaretunnel tunnel --verbose # Start proxy" echo "" # Optional: Build for other platforms read -p "Build for other platforms? (y/N) " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]] then echo "🌍 Building for multiple platforms..." # Windows echo " Building for Windows (amd64)..." GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o flaretunnel-windows-amd64.exe flaretunnel.go # Linux echo " Building for Linux (amd64)..." GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o flaretunnel-linux-amd64 flaretunnel.go # macOS Intel echo " Building for macOS (amd64)..." GOOS=darwin GOARCH=amd64 go build -ldflags="-s -w" -o flaretunnel-macos-amd64 flaretunnel.go # macOS Apple Silicon echo " Building for macOS (arm64)..." GOOS=darwin GOARCH=arm64 go build -ldflags="-s -w" -o flaretunnel-macos-arm64 flaretunnel.go # Linux ARM (Raspberry Pi, etc.) echo " Building for Linux (arm64)..." GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -o flaretunnel-linux-arm64 flaretunnel.go echo "" echo "✅ Cross-compilation complete!" echo "" echo "📦 Built binaries:" ls -lh flaretunnel-* | awk '{print " " $9 " - " $5}' echo "" fi echo "✨ Done!" ================================================ FILE: example_usage.py ================================================ #!/usr/bin/env python3 """ FlareTunnel - Quick Usage Example Make sure proxy is running: python FlareTunnel.py tunnel --verbose """ import requests import urllib3 import time # Disable SSL warnings urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) # FlareTunnel proxy configuration proxies = { 'http': 'http://127.0.0.1:8080', 'https': 'http://127.0.0.1:8080' } print("=" * 80) print("FlareTunnel - Usage Example") print("=" * 80) print() print("Make sure proxy is running: python FlareTunnel.py tunnel --verbose") print() print("=" * 80) print() # Test 1: Simple GET print("📡 Test 1: GET Request") print("-" * 40) try: response = requests.get( "https://httpbin.org/get?test=123", proxies=proxies, verify=False, timeout=10 ) print(f"✓ Status: {response.status_code}") data = response.json() print(f"✓ Origin IP: {data.get('origin', 'N/A')}") print(f"✓ Args: {data.get('args', {})}") print() except Exception as e: print(f"✗ Error: {e}") print() # Test 2: POST with JSON print("📤 Test 2: POST Request with JSON") print("-" * 40) try: response = requests.post( "https://httpbin.org/post", json={ "username": "testuser", "action": "login", "timestamp": time.time() }, proxies=proxies, verify=False, timeout=10 ) print(f"✓ Status: {response.status_code}") data = response.json() print(f"✓ Received JSON: {data.get('json', {})}") print() except Exception as e: print(f"✗ Error: {e}") print() # Test 3: Custom Headers print("📋 Test 3: Custom Headers") print("-" * 40) try: response = requests.get( "https://httpbin.org/headers", headers={ "X-Custom-Header": "FlareTunnel-Test", "User-Agent": "FlareTunnel/2.0" }, proxies=proxies, verify=False, timeout=10 ) print(f"✓ Status: {response.status_code}") data = response.json() headers_received = data.get('headers', {}) print(f"✓ Custom Header: {headers_received.get('X-Custom-Header', 'Not received')}") print(f"✓ User-Agent: {headers_received.get('User-Agent', 'Not received')}") print() except Exception as e: print(f"✗ Error: {e}") print() # Test 4: IP Rotation Check print("🔄 Test 4: IP Rotation (5 requests)") print("-" * 40) ips = set() for i in range(5): try: response = requests.get( "https://httpbin.org/ip", proxies=proxies, verify=False, timeout=10 ) if response.status_code == 200: ip = response.json().get('origin', 'N/A') ips.add(ip) print(f" Request {i+1}: {ip}") time.sleep(0.5) except Exception as e: print(f" Request {i+1}: Error - {e}") print(f"\n✓ Unique IPs: {len(ips)}") for ip in sorted(ips): print(f" - {ip}") print() # Test 5: Real Website print("🌐 Test 5: Real Website (example.com)") print("-" * 40) try: response = requests.get( "https://example.com", proxies=proxies, verify=False, timeout=10 ) print(f"✓ Status: {response.status_code}") print(f"✓ Size: {len(response.content):,} bytes") # Show first 100 chars content_preview = response.text[:100].replace('\n', ' ') print(f"✓ Preview: {content_preview}...") print() except Exception as e: print(f"✗ Error: {e}") print() # Summary print("=" * 80) print("✅ All tests completed!") print("=" * 80) print() print("💡 Key Points:") print(" • All requests went through Cloudflare Workers") print(" • Use rotation modes to change IPs (--mode random)") print(" • Use blacklist to save Worker requests (default: blacklist-minimal.txt)") print(" • IP addresses are blocked by default (Cloudflare doesn't support them)") print() print("📚 Learn More:") print(" • README.md - Full documentation") print(" • python FlareTunnel.py --help - All commands") print() ================================================ FILE: go.mod ================================================ module flaretunnel go 1.21 require github.com/olekukonko/tablewriter v0.0.5 require github.com/mattn/go-runewidth v0.0.9 // indirect ================================================ FILE: go.sum ================================================ github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=