16 Commits

Author SHA1 Message Date
Gianni Carafa
7ea7f253e8 fix Buildjob 2023-11-03 08:40:20 +01:00
Gianni Carafa
bf2529753d accept urls without scheme 2023-11-03 08:24:57 +01:00
Gianni Carafa
e3389d2df3 simplify Noform Option 2023-11-03 08:21:24 +01:00
Gianni Carafa
b786796595 improve logging and add disable form feature 2023-11-03 08:17:11 +01:00
Gianni Carafa
377a577c67 update docs 2023-11-02 23:30:13 +01:00
Gianni Carafa
6eb7b481d8 use fiber constant for statuscode 2023-11-02 23:20:02 +01:00
Gianni Carafa
2f1de95e06 improve docs 2023-11-02 23:05:42 +01:00
Gianni Carafa
7ae1a29932 add loggin switch 2023-11-02 22:59:07 +01:00
Gianni Carafa
63dcaeba3c handle queries 2023-11-02 22:50:03 +01:00
Gianni Carafa
62e03a384a Refactor duplicate code 2023-11-02 22:06:16 +01:00
Gianni Carafa
7f4d749c55 Add Version 2023-11-02 22:05:57 +01:00
Gianni Carafa
e748cb09a5 Add favicon 2023-11-02 22:04:29 +01:00
Gianni Carafa
c98e49f2b3 add basic auth 2023-11-02 20:57:44 +01:00
Gianni Carafa
3e3eebcdc2 make user-agent and x-forwaded-for configurable, rename debug path to raw 2023-11-02 19:35:10 +01:00
Gianni Carafa
45a3fe2adf rename debug path to raw 2023-11-02 19:19:13 +01:00
Gianni Carafa
ec7f2089fc Rename the binary from "kubero" to "ladder" in the .goreleaser.yaml file. 2023-11-02 19:17:03 +01:00
14 changed files with 162 additions and 112 deletions

View File

@@ -22,7 +22,7 @@ jobs:
- -
name: Set version name: Set version
run: | run: |
echo -n $(git describe --tags --abbrev=0) > cmd/VERSION echo -n $(git describe --tags --abbrev=0) > handlers/VERSION
- -
name: Set up Go name: Set up Go
uses: actions/setup-go@v3 uses: actions/setup-go@v3

View File

@@ -42,7 +42,7 @@ jobs:
- name: Set version - name: Set version
id: version id: version
run: | run: |
echo ${GITHUB_REF#refs/tags/v} > cmd/VERSION echo ${GITHUB_REF#refs/tags/v} > handlers/VERSION
# Install the cosign tool except on PR # Install the cosign tool except on PR
# https://github.com/sigstore/cosign-installer # https://github.com/sigstore/cosign-installer

View File

@@ -7,7 +7,7 @@ before:
builds: builds:
- -
main: cmd/main.go main: cmd/main.go
binary: kubero binary: ladder
env: env:
- CGO_ENABLED=0 - CGO_ENABLED=0
goos: goos:

View File

@@ -14,16 +14,19 @@ Certain sites may display missing images or encounter formatting issues. This ca
### Features ### Features
- [x] Bypass Paywalls - [x] Bypass Paywalls
- [x] Remove CORS headers from responses, Assets, and images ... - [x] Remove CORS headers from responses, assets, and images ...
- [x] Keep site browsable - [x] Keep site browsable
- [x] Add a debug path - [x] API
- [x] Add a API - [x] Show RAW HTML
- [x] Docker container - [x] Docker container
- [x] Linux binary - [x] Linux binary
- [x] Mac OS binary - [x] Mac OS binary
- [x] Windows binary (Untested) - [x] Windows binary (untested)
- [x] Remove most of the ads (unexpected side effect) - [x] Remove most of the ads (unexpected side effect)
- [ ] Basic Auth - [x] Basic Auth
- [x] Disable logs
- [x] Custom User Agent
- [x] Custom X-Forwarded-For IP
## Installation ## Installation
@@ -39,7 +42,7 @@ docker run -p 8080:8080 -d --name ladder ghcr.io/kubero-dev/ladder:latest
### Docker Compose ### Docker Compose
```bash ```bash
wget https://raw.githubusercontent.com/kubero-dev/ladder/main/docker-compose.yaml curl https://raw.githubusercontent.com/kubero-dev/ladder/main/docker-compose.yaml --output docker-compose.yaml
docker-compose up -d docker-compose up -d
``` ```
@@ -53,15 +56,13 @@ docker-compose up -d
Or direct by appending the URL to the end of the proxy URL: Or direct by appending the URL to the end of the proxy URL:
http://localhost:8080/https://www.google.com http://localhost:8080/https://www.google.com
### API ### API
```bash ```bash
curl -X GET "http://localhost:8080/api/https://www.google.com" curl -X GET "http://localhost:8080/api/https://www.google.com"
``` ```
### Debug ### RAW
http://localhost:8080/debug/https://www.google.com http://localhost:8080/raw/https://www.google.com
## Configuration ## Configuration
@@ -71,3 +72,8 @@ http://localhost:8080/debug/https://www.google.com
| --- | --- | --- | | --- | --- | --- |
| `PORT` | Port to listen on | `8080` | | `PORT` | Port to listen on | `8080` |
| `PREFORK` | Spawn multiple server instances | `false` | | `PREFORK` | Spawn multiple server instances | `false` |
| `USER_AGENT` | User agent to emulate | `Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)` |
| `X_FORWARDED_FOR` | IP forwarder address | `66.249.66.1` |
| `USERPASS` | Enables Basic Auth, format `admin:123456` | `` |
| `LOG_URLS` | Log fetched URL's | `true` |
| `DISABLE_FORM` | Disables URL Form Frontpage | `false` |

View File

@@ -159,8 +159,11 @@
}); });
document.getElementById('inputForm').addEventListener('submit', function (e) { document.getElementById('inputForm').addEventListener('submit', function (e) {
e.preventDefault(); e.preventDefault();
const inputValue = document.getElementById('inputField').value; let url = document.getElementById('inputField').value;
window.location.href = '/' + inputValue; if (url.indexOf('http') === -1) {
url = 'https://' + url;
}
window.location.href = '/' + url;
return false; return false;
}); });
</script> </script>

BIN
cmd/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,14 +1,22 @@
package main package main
import ( import (
_ "embed"
"ladder/handlers" "ladder/handlers"
"log" "log"
"os" "os"
"strconv" "strconv"
"strings"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/basicauth"
"github.com/gofiber/fiber/v2/middleware/favicon"
) )
//go:embed favicon.ico
var faviconData string
func main() { func main() {
prefork, _ := strconv.ParseBool(os.Getenv("PREFORK")) prefork, _ := strconv.ParseBool(os.Getenv("PREFORK"))
@@ -18,8 +26,31 @@ func main() {
}, },
) )
userpass := os.Getenv("USERPASS")
if userpass != "" {
userpass := strings.Split(userpass, ":")
app.Use(basicauth.New(basicauth.Config{
Users: map[string]string{
userpass[0]: userpass[1],
},
}))
}
app.Use(favicon.New(favicon.Config{
Data: []byte(faviconData),
URL: "/favicon.ico",
}))
if os.Getenv("NOLOGS") != "true" {
app.Use(func(c *fiber.Ctx) error {
log.Println(c.Method(), c.Path())
return c.Next()
})
}
app.Get("/", handlers.Form) app.Get("/", handlers.Form)
app.Get("debug/*", handlers.Debug)
app.Get("raw/*", handlers.Raw)
app.Get("api/*", handlers.Api) app.Get("api/*", handlers.Api)
app.Get("/*", handlers.ProxySite) app.Get("/*", handlers.ProxySite)

View File

@@ -5,12 +5,15 @@ services:
container_name: ladder container_name: ladder
build: . build: .
#restart: always #restart: always
#command: tail -f /dev/null
#command: sh -c ./ladder #command: sh -c ./ladder
environment: environment:
- PORT=8080 - PORT=8080
- PREFORK=true #- PREFORK=true
#- GODEBUG=netdns=go+4 #- X_FORWARDED_FOR=66.249.66.1
#- USER_AGENT=Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
#- USERPASS=foo:bar
#- LOG_URLS=true
#- GODEBUG=netdns=go
ports: ports:
- "8080:8080" - "8080:8080"
deploy: deploy:

View File

@@ -1,47 +1,31 @@
package handlers package handlers
import ( import (
"io" _ "embed"
"log" "log"
"net/http"
"net/url"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
//go:embed VERSION
var version string
func Api(c *fiber.Ctx) error { func Api(c *fiber.Ctx) error {
// Get the url from the URL // Get the url from the URL
urlQuery := c.Params("*") urlQuery := c.Params("*")
u, err := url.Parse(urlQuery) queries := c.Queries()
body, req, resp, err := fetchSite(urlQuery, queries)
if err != nil { if err != nil {
log.Println("ERROR:", err)
c.SendStatus(500)
return c.SendString(err.Error()) return c.SendString(err.Error())
} }
log.Println(u.String())
// Fetch the site
client := &http.Client{}
req, _ := http.NewRequest("GET", u.String(), nil)
req.Header.Set("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)")
req.Header.Set("X-Forwarded-For", "66.249.66.1")
req.Header.Set("Referer", u.String())
req.Header.Set("Host", u.Host)
resp, err := client.Do(req)
if err != nil {
return c.SendString(err.Error())
}
defer resp.Body.Close()
bodyB, err := io.ReadAll(resp.Body)
if err != nil {
log.Println("ERROR", err)
return c.SendString(err.Error())
}
body := rewriteHtml(bodyB, u)
response := Response{ response := Response{
Body: body, Version: version,
Body: body,
} }
response.Request.Headers = make([]interface{}, 0) response.Request.Headers = make([]interface{}, 0)
for k, v := range req.Header { for k, v := range req.Header {
@@ -63,6 +47,7 @@ func Api(c *fiber.Ctx) error {
} }
type Response struct { type Response struct {
Version string `json:"version"`
Body string `json:"body"` Body string `json:"body"`
Request struct { Request struct {
Headers []interface{} `json:"headers"` Headers []interface{} `json:"headers"`

View File

@@ -1,44 +0,0 @@
package handlers
import (
"io"
"log"
"net/http"
"net/url"
"github.com/gofiber/fiber/v2"
)
func Debug(c *fiber.Ctx) error {
// Get the url from the URL
urlQuery := c.Params("*")
u, err := url.Parse(urlQuery)
if err != nil {
return c.SendString(err.Error())
}
log.Println(u.String())
// Fetch the site
client := &http.Client{}
req, _ := http.NewRequest("GET", u.String(), nil)
req.Header.Set("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)")
req.Header.Set("X-Forwarded-For", "66.249.66.1")
req.Header.Set("Referer", u.String())
req.Header.Set("Host", u.Host)
resp, err := client.Do(req)
if err != nil {
return c.SendString(err.Error())
}
defer resp.Body.Close()
bodyB, err := io.ReadAll(resp.Body)
if err != nil {
log.Println("ERROR", err)
return c.SendString(err.Error())
}
body := rewriteHtml(bodyB, u)
return c.SendString(body)
}

View File

@@ -1,10 +1,20 @@
package handlers package handlers
import "github.com/gofiber/fiber/v2" import (
"os"
"github.com/gofiber/fiber/v2"
)
func Form(c *fiber.Ctx) error { func Form(c *fiber.Ctx) error {
c.Set("Content-Type", "text/html") if os.Getenv("DISABLE_FORM") == "true" {
return c.SendString(html) c.Set("Content-Type", "text/html")
c.SendStatus(fiber.StatusNotFound)
return c.SendString("Form Disabled")
} else {
c.Set("Content-Type", "text/html")
return c.SendString(html)
}
} }
const html = ` const html = `
@@ -169,8 +179,11 @@ const html = `
}); });
document.getElementById('inputForm').addEventListener('submit', function (e) { document.getElementById('inputForm').addEventListener('submit', function (e) {
e.preventDefault(); e.preventDefault();
const inputValue = document.getElementById('inputField').value; let url = document.getElementById('inputField').value;
window.location.href = '/' + inputValue; if (url.indexOf('http') === -1) {
url = 'https://' + url;
}
window.location.href = '/' + url;
return false; return false;
}); });
</script> </script>

View File

@@ -6,49 +6,73 @@ import (
"log" "log"
"net/http" "net/http"
"net/url" "net/url"
"os"
"regexp" "regexp"
"strings" "strings"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
var UserAgent = getenv("USER_AGENT", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)")
var ForwardedFor = getenv("X_FORWARDED_FOR", "66.249.66.1")
func ProxySite(c *fiber.Ctx) error { func ProxySite(c *fiber.Ctx) error {
// Get the url from the URL // Get the url from the URL
urlQuery := c.Params("*") url := c.Params("*")
u, err := url.Parse(urlQuery) queries := c.Queries()
body, _, resp, err := fetchSite(url, queries)
if err != nil { if err != nil {
log.Println("ERROR", err) log.Println("ERROR:", err)
c.SendStatus(500) c.SendStatus(fiber.StatusInternalServerError)
return c.SendString(err.Error()) return c.SendString(err.Error())
} }
log.Println(u.String()) c.Set("Content-Type", resp.Header.Get("Content-Type"))
return c.SendString(body)
}
func fetchSite(urlpath string, queries map[string]string) (string, *http.Request, *http.Response, error) {
urlQuery := "?"
if len(queries) > 0 {
for k, v := range queries {
urlQuery += k + "=" + v + "&"
}
}
urlQuery = strings.TrimSuffix(urlQuery, "&")
urlQuery = strings.TrimSuffix(urlQuery, "?")
u, err := url.Parse(urlpath)
if err != nil {
return "", nil, nil, err
}
if os.Getenv("DEBUG ") == "true" {
log.Println(u.String() + urlQuery)
}
// Fetch the site // Fetch the site
client := &http.Client{} client := &http.Client{}
req, _ := http.NewRequest("GET", u.String(), nil) req, _ := http.NewRequest("GET", u.String()+urlQuery, nil)
req.Header.Set("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)") req.Header.Set("User-Agent", UserAgent)
req.Header.Set("X-Forwarded-For", "66.249.66.1") req.Header.Set("X-Forwarded-For", ForwardedFor)
req.Header.Set("Referer", u.String()) req.Header.Set("Referer", u.String())
req.Header.Set("Host", u.Host) req.Header.Set("Host", u.Host)
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return c.SendString(err.Error()) return "", nil, nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
bodyB, err := io.ReadAll(resp.Body) bodyB, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
log.Println("ERROR", err) return "", nil, nil, err
c.SendStatus(500)
return c.SendString(err.Error())
} }
body := rewriteHtml(bodyB, u) body := rewriteHtml(bodyB, u)
c.Set("Content-Type", resp.Header.Get("Content-Type")) return body, req, resp, nil
return c.SendString(body)
} }
func rewriteHtml(bodyB []byte, u *url.URL) string { func rewriteHtml(bodyB []byte, u *url.URL) string {
@@ -73,3 +97,11 @@ func rewriteHtml(bodyB []byte, u *url.URL) string {
return body return body
} }
func getenv(key, fallback string) string {
value := os.Getenv(key)
if len(value) == 0 {
return fallback
}
return value
}

21
handlers/raw.go Normal file
View File

@@ -0,0 +1,21 @@
package handlers
import (
"log"
"github.com/gofiber/fiber/v2"
)
func Raw(c *fiber.Ctx) error {
// Get the url from the URL
urlQuery := c.Params("*")
queries := c.Queries()
body, _, _, err := fetchSite(urlQuery, queries)
if err != nil {
log.Println("ERROR:", err)
c.SendStatus(500)
return c.SendString(err.Error())
}
return c.SendString(body)
}

View File

@@ -11,9 +11,9 @@ import (
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
func TestDebug(t *testing.T) { func TestRaw(t *testing.T) {
app := fiber.New() app := fiber.New()
app.Get("/debug/*", Debug) app.Get("/raw/*", Raw)
testCases := []struct { testCases := []struct {
name string name string
@@ -34,7 +34,7 @@ func TestDebug(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/debug/"+tc.url, nil) req := httptest.NewRequest(http.MethodGet, "/raw/"+tc.url, nil)
resp, err := app.Test(req) resp, err := app.Test(req)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)