Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec4dc5c2cc | ||
|
|
ba87d6b980 | ||
|
|
a6ee6aebfc | ||
|
|
7b519c7016 | ||
|
|
fba9db3d94 | ||
|
|
0dd5edd5ba | ||
|
|
4023718b12 | ||
|
|
cb02f52a46 | ||
|
|
3e20678e3d |
46
README.md
46
README.md
@@ -10,8 +10,6 @@
|
|||||||
|
|
||||||
Freedom of information is an essential pillar of democracy and informed decision-making. While media organizations have legitimate financial interests, it is crucial to strike a balance between profitability and the public's right to access information. The proliferation of paywalls raises concerns about the erosion of this fundamental freedom, and it is imperative for society to find innovative ways to preserve access to vital information without compromising the sustainability of journalism. In a world where knowledge should be shared and not commodified, paywalls should be critically examined to ensure that they do not undermine the principles of an open and informed society.
|
Freedom of information is an essential pillar of democracy and informed decision-making. While media organizations have legitimate financial interests, it is crucial to strike a balance between profitability and the public's right to access information. The proliferation of paywalls raises concerns about the erosion of this fundamental freedom, and it is imperative for society to find innovative ways to preserve access to vital information without compromising the sustainability of journalism. In a world where knowledge should be shared and not commodified, paywalls should be critically examined to ensure that they do not undermine the principles of an open and informed society.
|
||||||
|
|
||||||
Certain sites may display missing images or encounter formatting issues. This can be attributed to the site's reliance on JavaScript or CSS for image and resource loading, which presents a limitation when accessed through this proxy. If you prefer a full experience, please concider buying a subscription for the site.
|
|
||||||
|
|
||||||
> **Disclaimer:** This project is intended for educational purposes only. The author does not endorse or encourage any unethical or illegal activity. Use this tool at your own risk.
|
> **Disclaimer:** This project is intended for educational purposes only. The author does not endorse or encourage any unethical or illegal activity. Use this tool at your own risk.
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
@@ -31,7 +29,15 @@ Certain sites may display missing images or encounter formatting issues. This ca
|
|||||||
- [x] Basic Auth
|
- [x] Basic Auth
|
||||||
- [x] Disable logs
|
- [x] Disable logs
|
||||||
- [x] No Tracking
|
- [x] No Tracking
|
||||||
|
- [x] Limit the proxy to a list of domains
|
||||||
- [ ] Optional TOR proxy
|
- [ ] Optional TOR proxy
|
||||||
|
- [ ] A key to share only one URL
|
||||||
|
- [ ] Fetch from Google Cache if not available
|
||||||
|
|
||||||
|
### Limitations
|
||||||
|
Certain sites may display missing images or encounter formatting issues. This can be attributed to the site's reliance on JavaScript or CSS for image and resource loading, which presents a limitation when accessed through this proxy. If you prefer a full experience, please concider buying a subscription for the site.
|
||||||
|
|
||||||
|
Some sites do not expose their content to search engines, which means that the proxy cannot access the content. A future version will try to fetch the content from Google Cache.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -75,7 +81,7 @@ http://localhost:8080/raw/https://www.example.com
|
|||||||
|
|
||||||
### Environment Variables
|
### Environment Variables
|
||||||
|
|
||||||
| Variable | Description | Default |
|
| Variable | Description | Value |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `PORT` | Port to listen on | `8080` |
|
| `PORT` | Port to listen on | `8080` |
|
||||||
| `PREFORK` | Spawn multiple server instances | `false` |
|
| `PREFORK` | Spawn multiple server instances | `false` |
|
||||||
@@ -85,10 +91,42 @@ http://localhost:8080/raw/https://www.example.com
|
|||||||
| `LOG_URLS` | Log fetched URL's | `true` |
|
| `LOG_URLS` | Log fetched URL's | `true` |
|
||||||
| `DISABLE_FORM` | Disables URL Form Frontpage | `false` |
|
| `DISABLE_FORM` | Disables URL Form Frontpage | `false` |
|
||||||
| `FORM_PATH` | Path to custom Form HTML | `` |
|
| `FORM_PATH` | Path to custom Form HTML | `` |
|
||||||
| `RULES_URL` | URL to a ruleset file | `https://raw.githubusercontent.com/kubero-dev/ladder/main/ruleset.yaml` |
|
| `RULESET` | URL to a ruleset file | `https://raw.githubusercontent.com/kubero-dev/ladder/main/ruleset.yaml` or `/path/to/my/rules.yaml` |
|
||||||
|
| `ALLOWED_DOMAINS` | Comma separated list of allowed domains. Empty = no limitations | `` |
|
||||||
|
| `ALLOWED_DOMAINS_RULESET` | Allow Domains from Ruleset. false = no limitations | `false` |
|
||||||
|
|
||||||
|
`ALLOWED_DOMAINS` and `ALLOWED_DOMAINS_RULESET` are joined together. If both are empty, no limitations are applied.
|
||||||
|
|
||||||
### Ruleset
|
### Ruleset
|
||||||
|
|
||||||
It is possible to apply custom rules to modify the response. This can be used to remove unwanted or modify elements from the page. The ruleset is a YAML file that contains a list of rules for each domain and is loaded on startup
|
It is possible to apply custom rules to modify the response. This can be used to remove unwanted or modify elements from the page. The ruleset is a YAML file that contains a list of rules for each domain and is loaded on startup
|
||||||
|
|
||||||
See in [ruleset.yaml](ruleset.yaml) for an example.
|
See in [ruleset.yaml](ruleset.yaml) for an example.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- domain: www.example.com
|
||||||
|
regexRules:
|
||||||
|
- match: <script\s+([^>]*\s+)?src="(/)([^"]*)"
|
||||||
|
replace: <script $1 script="/https://www.example.com/$3"
|
||||||
|
injections:
|
||||||
|
- position: head # Position where to inject the code
|
||||||
|
append: |
|
||||||
|
<script>
|
||||||
|
window.localStorage.clear();
|
||||||
|
console.log("test");
|
||||||
|
alert("Hello!");
|
||||||
|
</script>
|
||||||
|
- domain: www.anotherdomain.com # Domain where the rule applies
|
||||||
|
path: /article # Path where the rule applies
|
||||||
|
googleCache: false # Search also in Google Cache
|
||||||
|
regexRules: # Regex rules to apply
|
||||||
|
- match: <script\s+([^>]*\s+)?src="(/)([^"]*)"
|
||||||
|
replace: <script $1 script="/https://www.example.com/$3"
|
||||||
|
injections:
|
||||||
|
- position: .left-content article .post-title # Position where to inject the code into DOM
|
||||||
|
replace: |
|
||||||
|
<h1>My Custom Title</h1>
|
||||||
|
- position: .left-content article # Position where to inject the code into DOM
|
||||||
|
prepend: |
|
||||||
|
<h2>Suptitle</h2>
|
||||||
|
```
|
||||||
@@ -18,6 +18,7 @@ import (
|
|||||||
var UserAgent = getenv("USER_AGENT", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)")
|
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")
|
var ForwardedFor = getenv("X_FORWARDED_FOR", "66.249.66.1")
|
||||||
var rulesSet = loadRules()
|
var rulesSet = loadRules()
|
||||||
|
var allowedDomains = strings.Split(os.Getenv("ALLOWED_DOMAINS"), ",")
|
||||||
|
|
||||||
func ProxySite(c *fiber.Ctx) error {
|
func ProxySite(c *fiber.Ctx) error {
|
||||||
// Get the url from the URL
|
// Get the url from the URL
|
||||||
@@ -51,6 +52,10 @@ func fetchSite(urlpath string, queries map[string]string) (string, *http.Request
|
|||||||
return "", nil, nil, err
|
return "", nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(allowedDomains) > 0 && !StringInSlice(u.Host, allowedDomains) {
|
||||||
|
return "", nil, nil, fmt.Errorf("domain not allowed. %s not in %s", u.Host, allowedDomains)
|
||||||
|
}
|
||||||
|
|
||||||
if os.Getenv("DEBUG ") == "true" {
|
if os.Getenv("DEBUG ") == "true" {
|
||||||
log.Println(u.String() + urlQuery)
|
log.Println(u.String() + urlQuery)
|
||||||
}
|
}
|
||||||
@@ -98,7 +103,7 @@ func rewriteHtml(bodyB []byte, u *url.URL) string {
|
|||||||
body = strings.ReplaceAll(body, "url(/", "url(/https://"+u.Host+"/")
|
body = strings.ReplaceAll(body, "url(/", "url(/https://"+u.Host+"/")
|
||||||
body = strings.ReplaceAll(body, "href=\"https://"+u.Host, "href=\"/https://"+u.Host+"/")
|
body = strings.ReplaceAll(body, "href=\"https://"+u.Host, "href=\"/https://"+u.Host+"/")
|
||||||
|
|
||||||
if os.Getenv("RULES_URL") != "" {
|
if os.Getenv("RULESET") != "" {
|
||||||
body = applyRules(u.Host, u.Path, body)
|
body = applyRules(u.Host, u.Path, body)
|
||||||
}
|
}
|
||||||
return body
|
return body
|
||||||
@@ -113,13 +118,16 @@ func getenv(key, fallback string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func loadRules() RuleSet {
|
func loadRules() RuleSet {
|
||||||
rulesUrl := os.Getenv("RULES_URL")
|
rulesUrl := os.Getenv("RULESET")
|
||||||
if rulesUrl == "" {
|
if rulesUrl == "" {
|
||||||
RulesList := RuleSet{}
|
RulesList := RuleSet{}
|
||||||
return RulesList
|
return RulesList
|
||||||
}
|
}
|
||||||
log.Println("Loading rules")
|
log.Println("Loading rules")
|
||||||
|
|
||||||
|
var ruleSet RuleSet
|
||||||
|
if strings.HasPrefix(rulesUrl, "http") {
|
||||||
|
|
||||||
resp, err := http.Get(rulesUrl)
|
resp, err := http.Get(rulesUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("ERROR:", err)
|
log.Println("ERROR:", err)
|
||||||
@@ -134,12 +142,25 @@ func loadRules() RuleSet {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("ERROR:", err)
|
log.Println("ERROR:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ruleSet RuleSet
|
|
||||||
yaml.Unmarshal(body, &ruleSet)
|
yaml.Unmarshal(body, &ruleSet)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("ERROR:", err)
|
log.Println("ERROR:", err)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
yamlFile, err := os.ReadFile(rulesUrl)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR:", err)
|
||||||
|
}
|
||||||
|
yaml.Unmarshal(yamlFile, &ruleSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rule := range ruleSet {
|
||||||
|
//log.Println("Loaded rules for", rule.Domain)
|
||||||
|
if os.Getenv("ALLOWED_DOMAINS_RULESET") == "true" {
|
||||||
|
allowedDomains = append(allowedDomains, rule.Domain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log.Println("Loaded rules for", len(ruleSet), "Domains")
|
log.Println("Loaded rules for", len(ruleSet), "Domains")
|
||||||
return ruleSet
|
return ruleSet
|
||||||
@@ -154,7 +175,7 @@ func applyRules(domain string, path string, body string) string {
|
|||||||
if rule.Domain != domain {
|
if rule.Domain != domain {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if rule.Path != "" && rule.Path != path {
|
if len(rule.Paths) > 0 && !StringInSlice(path, rule.Paths) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, regexRule := range rule.RegexRules {
|
for _, regexRule := range rule.RegexRules {
|
||||||
@@ -192,7 +213,7 @@ type Rule struct {
|
|||||||
|
|
||||||
type RuleSet []struct {
|
type RuleSet []struct {
|
||||||
Domain string `yaml:"domain"`
|
Domain string `yaml:"domain"`
|
||||||
Path string `yaml:"path,omitempty"`
|
Paths []string `yaml:"paths,omitempty"`
|
||||||
GoogleCache bool `yaml:"googleCache,omitempty"`
|
GoogleCache bool `yaml:"googleCache,omitempty"`
|
||||||
RegexRules []Rule `yaml:"regexRules"`
|
RegexRules []Rule `yaml:"regexRules"`
|
||||||
Injections []struct {
|
Injections []struct {
|
||||||
@@ -202,3 +223,12 @@ type RuleSet []struct {
|
|||||||
Replace string `yaml:"replace"`
|
Replace string `yaml:"replace"`
|
||||||
} `yaml:"injections"`
|
} `yaml:"injections"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func StringInSlice(s string, list []string) bool {
|
||||||
|
for _, x := range list {
|
||||||
|
if strings.HasPrefix(s, x) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
54
ruleset.yaml
54
ruleset.yaml
@@ -10,16 +10,46 @@
|
|||||||
console.log("test");
|
console.log("test");
|
||||||
alert("Hello!");
|
alert("Hello!");
|
||||||
</script>
|
</script>
|
||||||
- domain: www.anotherdomain.com # Domain where the rule applies
|
- position: h1
|
||||||
path: /article # Path where the rule applies
|
|
||||||
googleCache: false # Search also in Google Cache
|
|
||||||
regexRules: # Regex rules to apply
|
|
||||||
- match: <script\s+([^>]*\s+)?src="(/)([^"]*)"
|
|
||||||
replace: <script $1 script="/https://www.example.com/$3"
|
|
||||||
injections:
|
|
||||||
- position: .left-content article .post-title # Position where to inject the code into DOM
|
|
||||||
replace: |
|
replace: |
|
||||||
<h1>My Custom Title</h1>
|
<h1>An example with a ladder ;-)</h1>
|
||||||
- position: .left-content article # Position where to inject the code into DOM
|
- domain: www.americanbanker.com
|
||||||
prepend: |
|
paths:
|
||||||
<h2>Suptitle</h2>
|
- /news
|
||||||
|
injections:
|
||||||
|
- position: head
|
||||||
|
append: |
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const inlineGate = document.querySelector('.inline-gate');
|
||||||
|
if (inlineGate) {
|
||||||
|
inlineGate.classList.remove('inline-gate');
|
||||||
|
const inlineGated = document.querySelectorAll('.inline-gated');
|
||||||
|
for (const elem of inlineGated) { elem.classList.remove('inline-gated'); }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
- domain: www.nzz.ch
|
||||||
|
paths:
|
||||||
|
- /international
|
||||||
|
- /sport
|
||||||
|
- /wirtschaft
|
||||||
|
- /technologie
|
||||||
|
- /feuilleton
|
||||||
|
- /zuerich
|
||||||
|
- /wissenschaft
|
||||||
|
- /gesellschaft
|
||||||
|
- /panorama
|
||||||
|
- /mobilitaet
|
||||||
|
- /reisen
|
||||||
|
- /meinung
|
||||||
|
- /finanze
|
||||||
|
injections:
|
||||||
|
- position: head
|
||||||
|
append: |
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const paywall = document.querySelector('.dynamic-regwall');
|
||||||
|
removeDOMElement(paywall)
|
||||||
|
});
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user