20 Commits

Author SHA1 Message Date
Milo Schwartz
1d15b5b175 Merge pull request #17 from fosrl/dev
1.3.0
2025-12-21 13:59:58 -08:00
miloschwartz
3eea242a8e strip remote role 2025-12-21 16:58:26 -05:00
miloschwartz
2bc45f31f7 update readme 2025-12-20 21:37:42 -05:00
miloschwartz
ad3c3b71e6 support pulling real ip from proxy 2025-12-20 18:30:10 -05:00
miloschwartz
52f47fa24e Merge branch 'main' into dev 2025-12-20 17:21:40 -05:00
Julien Breton
83e894f23f Feature: Allow Basic Auth challenge 2025-12-20 16:44:47 -05:00
Owen Schwartz
7d75628d86 Merge pull request #15 from fosrl/oschwartz10612-patch-1
Update README.md
2025-11-08 14:46:30 -08:00
Owen Schwartz
4adf71ec3c Update README.md
Fixes #14
2025-11-08 14:46:21 -08:00
Owen Schwartz
881b9d665e Merge pull request #12 from Pallavikumarimdb/feat/string-interpolation-in-the-headers
Role in headers
2025-11-08 14:42:16 -08:00
miloschwartz
cc45f414de update license copyright 2025-11-01 12:51:50 -07:00
miloschwartz
14df42f0ab strip headers, change display name, add icon 2025-11-01 12:50:15 -07:00
Pallavi Kumari
88c453cae1 role in headers 2025-10-06 22:10:23 +05:30
miloschwartz
cab2424e6e add templates 2025-09-29 16:40:42 -07:00
Owen Schwartz
d3adb46c6a Merge pull request #8 from Lokowitz/sync-go-version
Sync go version
2025-09-21 20:11:38 -04:00
Marvin
a8ba33b9b5 add PULL_REQUEST_TEMPLATE
add dependabot
update to go 1.25
2025-08-17 12:02:19 +00:00
Marvin
11c7340e63 go.mod aktualisieren 2025-07-26 10:29:15 +02:00
Marvin
e933d3ca62 .go-version aktualisieren 2025-07-26 10:28:59 +02:00
Milo Schwartz
d553b6ca77 Merge pull request #7 from pyrho/feat/auth-headers
feat: add auth headers to request
2025-06-04 12:35:46 -04:00
Damien Rajon
0baba997d7 feat: add auth headers to request
These changes are needed for the related changes in pangolin to work.
See https://github.com/fosrl/pangolin/pull/807#event-17985130896
2025-06-04 18:30:25 +02:00
miloschwartz
e951e42b4d remove explicit access token check and pass query params and headers in verify session 2025-04-06 11:24:47 -04:00
15 changed files with 553 additions and 34 deletions

BIN
.assets/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -0,0 +1,47 @@
body:
- type: textarea
attributes:
label: Summary
description: A clear and concise summary of the requested feature.
validations:
required: true
- type: textarea
attributes:
label: Motivation
description: |
Why is this feature important?
Explain the problem this feature would solve or what use case it would enable.
validations:
required: true
- type: textarea
attributes:
label: Proposed Solution
description: |
How would you like to see this feature implemented?
Provide as much detail as possible about the desired behavior, configuration, or changes.
validations:
required: true
- type: textarea
attributes:
label: Alternatives Considered
description: Describe any alternative solutions or workarounds you've thought about.
validations:
required: false
- type: textarea
attributes:
label: Additional Context
description: Add any other context, mockups, or screenshots about the feature request here.
validations:
required: false
- type: markdown
attributes:
value: |
Before submitting, please:
- Check if there is an existing issue for this feature.
- Clearly explain the benefit and use case.
- Be as specific as possible to help contributors evaluate and implement.

51
.github/ISSUE_TEMPLATE/1.bug_report.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
name: Bug Report
description: Create a bug report
labels: []
body:
- type: textarea
attributes:
label: Describe the Bug
description: A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
attributes:
label: Environment
description: Please fill out the relevant details below for your environment.
value: |
- OS Type & Version: (e.g., Ubuntu 22.04)
- Pangolin Version:
- Gerbil Version:
- Traefik Version:
- Newt Version:
- Olm Version: (if applicable)
validations:
required: true
- type: textarea
attributes:
label: To Reproduce
description: |
Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below.
If using code blocks, make sure syntax highlighting is correct and double-check that the rendered preview is not broken.
validations:
required: true
- type: textarea
attributes:
label: Expected Behavior
description: A clear and concise description of what you expected to happen.
validations:
required: true
- type: markdown
attributes:
value: |
Before posting the issue go through the steps you've written down to make sure the steps provided are detailed and clear.
- type: markdown
attributes:
value: |
Contributors should be able to follow the steps provided in order to reproduce the bug.

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Need help or have questions?
url: https://github.com/orgs/fosrl/discussions
about: Ask questions, get help, and discuss with other community members
- name: Request a Feature
url: https://github.com/orgs/fosrl/discussions/new?category=feature-requests
about: Feature requests should be opened as discussions so others can upvote and comment

10
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,10 @@
## Community Contribution License Agreement
By creating this pull request, I grant the project maintainers an unlimited,
perpetual license to use, modify, and redistribute these contributions under any terms they
choose, including both the AGPLv3 and the Fossorial Commercial license terms. I
represent that I have the right to grant this license for all contributed content.
## Description
## How to test?

6
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "daily"

1
.gitignore vendored
View File

@@ -1 +1,2 @@
go.sum
.DS_Store

View File

@@ -1 +1 @@
1.23.2
1.25

View File

@@ -1,5 +1,6 @@
displayName: Fossorial Badger
displayName: Pangolin (Badger)
type: middleware
iconPath: .assets/icon.png
import: github.com/fosrl/badger
@@ -8,5 +9,4 @@ summary: Middleware auth bouncer for Pangolin
testData:
apiBaseUrl: "http://localhost:3001/api/v1"
userSessionCookieName: "p_session_token"
accessTokenQueryParam: "p_token"
resourceSessionRequestParam: "p_session_request"

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2024 Fossorial LLC
Copyright (c) 2025 Fossorial Inc
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,26 +1,99 @@
# Badger Plugin for Traefik with Pangolin Integration
# Pangolin Middleware: Badger
Badger is a middleware plugin designed to work with the Traefik reverse proxy in conjunction with [Pangolin](https://github.com/fosrl/pangolin), a multi-tenant tunneled reverse proxy server and management interface with identity and access management. Badger acts as an authentication bouncer, ensuring only authenticated and authorized requests are allowed through the proxy.
Badger is a middleware plugin designed to work with Traefik in conjunction with [Pangolin](https://github.com/fosrl/pangolin), an identity-aware reverse proxy and zero-trust VPN. Badger acts as an authentication bouncer, ensuring only authenticated and authorized requests are allowed through the proxy.
This plugin is **required** to be configured alongside [Pangolin](https://github.com/fosrl/pangolin) to enforce secure authentication and session management.
> [!NOTE]
> Badger can also be used standalone for IP handling (Cloudflare and custom proxy support) without Pangolin. Simply set `disableForwardAuth: true` in your configuration. See the [Disabling Forward Auth](#disabling-forward-auth) section below for details.
This plugin is **required** to be installed alongside [Pangolin](https://github.com/fosrl/pangolin) to enforce secure authentication and session management.
## Installation
Learn how to set up [Pangolin](https://github.com/fosrl/pangolin) and Badger in the [Pangolin Documentation](https://github.com/fosrl/pangolin).
Badger is automatically installed with Pangolin. Learn how to install Pangolin in the [Pangolin Documentation](https://docs.pangolin.net/self-host/quick-install).
## Configuration
Badger requires the following configuration parameters to be specified in your [Traefik configuration file](https://doc.traefik.io/traefik/getting-started/configuration-overview/). These coincide with the separate [Pangolin](https://github.com/fosrl/pangolin) configuration file.
Pangolin will provide the necessary configuration to Badger automatically via the HTTP provider. However, you can override the configuration settings by manually providing them in the Traefik config.
### Configuration Options
### Required Configuration Options
When forward auth is enabled (default), the following options are required:
```yaml
apiBaseUrl: "http://localhost:3001/api/v1"
userSessionCookieName: "p_session_token"
accessTokenQueryParam: "p_token"
resourceSessionRequestParam: "p_session_request"
```
### Disabling Forward Auth
To disable forward auth and only use IP handling, set `disableForwardAuth: true`. When enabled, all requests pass through without authentication, and the required configuration options above are not needed:
Only do this if you do not need Pangolin's authentication features and only want IP handling.
```yaml
disableForwardAuth: true
```
### IP Handling Configuration
Badger automatically extracts the real client IP from requests. By default, it trusts Cloudflare IP ranges and uses the `CF-Connecting-IP` header.
#### Using with Cloudflare (Default)
No additional configuration needed. Badger automatically:
- Trusts Cloudflare IP ranges
- Extracts IP from `CF-Connecting-IP` header
- Sets `X-Real-IP` and `X-Forwarded-For` headers for downstream services
#### Using without Cloudflare
If you're using a different proxy or load balancer, configure custom trusted IPs and/or a custom IP header:
Ensure you always disable the default Cloudflare IP ranges by setting `disableDefaultCFIPs: true` and provide your own trusted IP ranges in CIDR format under `trustip` if using a different proxy.
```yaml
apiBaseUrl: "http://localhost:3001/api/v1"
userSessionCookieName: "p_session_token"
resourceSessionRequestParam: "p_session_request"
# Disable Cloudflare IP ranges
disableDefaultCFIPs: true
# Add your proxy/load balancer IP ranges (CIDR format)
trustip:
- "10.0.0.0/8"
- "172.16.0.0/12"
# Optional: Use a custom header instead of CF-Connecting-IP
customIPHeader: "X-Forwarded-For"
```
### Configuration Options Reference
| Option | Type | Required\* | Default | Description |
| ----------------------------- | -------- | ---------- | ------- | ----------------------------------------------------------------------------------- |
| `disableForwardAuth` | bool | No | `false` | Disable forward auth; only IP handling is performed |
| `apiBaseUrl` | string | Yes\* | - | Base URL of the Pangolin API |
| `userSessionCookieName` | string | Yes\* | - | Cookie name for user sessions |
| `resourceSessionRequestParam` | string | Yes\* | - | Query parameter name for resource session requests |
| `trustip` | []string | No | `[]` | Array of trusted IP ranges in CIDR format |
| `disableDefaultCFIPs` | bool | No | `false` | Disable default Cloudflare IP ranges |
| `customIPHeader` | string | No | `""` | Custom header name to extract IP from (only used if request is from trusted source) |
\* Required only when `disableForwardAuth` is `false` (default)
## Updating Cloudflare IPs
To update the Cloudflare IP ranges, run:
```bash
./updateCFIps.sh
```
This fetches the latest IP ranges from Cloudflare and updates `ips/ips.go`.
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

2
go.mod
View File

@@ -1,3 +1,3 @@
module github.com/fosrl/badger
go 1.23.1
go 1.25

28
ips/ips.go Normal file
View File

@@ -0,0 +1,28 @@
package ips
func CFIPs() []string {
return []string{
"173.245.48.0/20",
"103.21.244.0/22",
"103.22.200.0/22",
"103.31.4.0/22",
"141.101.64.0/18",
"108.162.192.0/18",
"190.93.240.0/20",
"188.114.96.0/20",
"197.234.240.0/22",
"198.41.128.0/17",
"162.158.0.0/15",
"104.16.0.0/13",
"104.24.0.0/14",
"172.64.0.0/13",
"131.0.72.0/22",
"2400:cb00::/32",
"2606:4700::/32",
"2803:f800::/32",
"2405:b500::/32",
"2405:8100::/32",
"2a06:98c0::/29",
"2c0f:f248::/32",
}
}

283
main.go
View File

@@ -5,24 +5,40 @@ import (
"context"
"encoding/json"
"fmt"
"net"
"net/http"
"strings"
"github.com/fosrl/badger/ips"
)
type Config struct {
APIBaseUrl string `json:"apiBaseUrl"`
UserSessionCookieName string `json:"userSessionCookieName"`
AccessTokenQueryParam string `json:"accessTokenQueryParam"`
ResourceSessionRequestParam string `json:"resourceSessionRequestParam"`
APIBaseUrl string `json:"apiBaseUrl,omitempty"`
UserSessionCookieName string `json:"userSessionCookieName,omitempty"`
ResourceSessionRequestParam string `json:"resourceSessionRequestParam,omitempty"`
DisableForwardAuth bool `json:"disableForwardAuth,omitempty"`
TrustIP []string `json:"trustip,omitempty"`
DisableDefaultCFIPs bool `json:"disableDefaultCFIPs,omitempty"`
CustomIPHeader string `json:"customIPHeader,omitempty"`
}
const (
xRealIP = "X-Real-Ip"
xForwardFor = "X-Forwarded-For"
xForwardProto = "X-Forwarded-Proto"
cfConnectingIP = "CF-Connecting-IP"
cfVisitor = "CF-Visitor"
)
type Badger struct {
next http.Handler
name string
apiBaseUrl string
userSessionCookieName string
accessTokenQueryParam string
resourceSessionRequestParam string
disableForwardAuth bool
trustIP []*net.IPNet
customIPHeader string
}
type VerifyBody struct {
@@ -32,15 +48,22 @@ type VerifyBody struct {
RequestHost *string `json:"host"`
RequestPath *string `json:"path"`
RequestMethod *string `json:"method"`
AccessToken *string `json:"accessToken,omitempty"`
TLS bool `json:"tls"`
RequestIP *string `json:"requestIp,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
Query map[string]string `json:"query,omitempty"`
}
type VerifyResponse struct {
Data struct {
Valid bool `json:"valid"`
RedirectURL *string `json:"redirectUrl"`
HeaderAuthChallenged bool `json:"headerAuthChallenged"`
Valid bool `json:"valid"`
RedirectURL *string `json:"redirectUrl"`
Username *string `json:"username,omitempty"`
Email *string `json:"email,omitempty"`
Name *string `json:"name,omitempty"`
Role *string `json:"role,omitempty"`
ResponseHeaders map[string]string `json:"responseHeaders,omitempty"`
} `json:"data"`
}
@@ -52,8 +75,9 @@ type ExchangeSessionBody struct {
type ExchangeSessionResponse struct {
Data struct {
Valid bool `json:"valid"`
Cookie *string `json:"cookie"`
Valid bool `json:"valid"`
Cookie *string `json:"cookie"`
ResponseHeaders map[string]string `json:"responseHeaders,omitempty"`
} `json:"data"`
}
@@ -62,27 +86,70 @@ func CreateConfig() *Config {
}
func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error) {
return &Badger{
badger := &Badger{
next: next,
name: name,
apiBaseUrl: config.APIBaseUrl,
userSessionCookieName: config.UserSessionCookieName,
accessTokenQueryParam: config.AccessTokenQueryParam,
resourceSessionRequestParam: config.ResourceSessionRequestParam,
}, nil
disableForwardAuth: config.DisableForwardAuth,
customIPHeader: config.CustomIPHeader,
}
// Validate required fields only if forward auth is enabled
if !config.DisableForwardAuth {
if config.APIBaseUrl == "" {
return nil, fmt.Errorf("apiBaseUrl is required when forward auth is enabled")
}
if config.UserSessionCookieName == "" {
return nil, fmt.Errorf("userSessionCookieName is required when forward auth is enabled")
}
if config.ResourceSessionRequestParam == "" {
return nil, fmt.Errorf("resourceSessionRequestParam is required when forward auth is enabled")
}
}
if config.TrustIP != nil {
for _, v := range config.TrustIP {
_, trustip, err := net.ParseCIDR(v)
if err != nil {
return nil, err
}
badger.trustIP = append(badger.trustIP, trustip)
}
}
if !config.DisableDefaultCFIPs {
for _, v := range ips.CFIPs() {
_, trustip, err := net.ParseCIDR(v)
if err != nil {
return nil, err
}
badger.trustIP = append(badger.trustIP, trustip)
}
}
return badger, nil
}
func (p *Badger) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
realIP := p.getRealIP(req)
p.setIPHeaders(req, realIP)
if p.disableForwardAuth {
p.next.ServeHTTP(rw, req)
return
}
cookies := p.extractCookies(req)
var accessToken *string
queryValues := req.URL.Query()
if sessionRequestValue := queryValues.Get(p.resourceSessionRequestParam); sessionRequestValue != "" {
body := ExchangeSessionBody{
RequestToken: &sessionRequestValue,
RequestHost: &req.Host,
RequestIP: &req.RemoteAddr,
RequestIP: &realIP,
}
jsonData, err := json.Marshal(body)
@@ -116,17 +183,18 @@ func (p *Badger) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
originalRequestURL = fmt.Sprintf("%s?%s", originalRequestURL, cleanedQuery)
}
if result.Data.ResponseHeaders != nil {
for key, value := range result.Data.ResponseHeaders {
rw.Header().Add(key, value)
}
}
fmt.Println("Got exchange token, redirecting to", originalRequestURL)
http.Redirect(rw, req, originalRequestURL, http.StatusFound)
return
}
}
if token := queryValues.Get(p.accessTokenQueryParam); token != "" {
accessToken = &token
queryValues.Del(p.accessTokenQueryParam)
}
cleanedQuery := queryValues.Encode()
originalRequestURL := fmt.Sprintf("%s://%s%s", p.getScheme(req), req.Host, req.URL.Path)
if cleanedQuery != "" {
@@ -135,6 +203,20 @@ func (p *Badger) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
verifyURL := fmt.Sprintf("%s/badger/verify-session", p.apiBaseUrl)
headers := make(map[string]string)
for name, values := range req.Header {
if len(values) > 0 {
headers[name] = values[0] // Send only the first value for simplicity
}
}
queryParams := make(map[string]string)
for key, values := range queryValues {
if len(values) > 0 {
queryParams[key] = values[0]
}
}
cookieData := VerifyBody{
Sessions: cookies,
OriginalRequestURL: originalRequestURL,
@@ -142,9 +224,10 @@ func (p *Badger) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
RequestHost: &req.Host,
RequestPath: &req.URL.Path,
RequestMethod: &req.Method,
AccessToken: accessToken,
TLS: req.TLS != nil,
RequestIP: &req.RemoteAddr,
RequestIP: &realIP,
Headers: headers,
Query: queryParams,
}
jsonData, err := json.Marshal(cookieData)
@@ -176,6 +259,31 @@ func (p *Badger) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
return
}
req.Header.Del("Remote-User")
req.Header.Del("Remote-Email")
req.Header.Del("Remote-Name")
req.Header.Del("Remote-Role")
if result.Data.ResponseHeaders != nil {
for key, value := range result.Data.ResponseHeaders {
rw.Header().Add(key, value)
}
}
if result.Data.HeaderAuthChallenged {
fmt.Println("Badger: challenging client for header authentication")
rw.Header().Add("WWW-Authenticate", "Basic realm=\"pangolin\"")
if result.Data.RedirectURL != nil && *result.Data.RedirectURL != "" {
rw.Header().Set("Content-Type", "text/html; charset=utf-8")
rw.WriteHeader(http.StatusUnauthorized)
rw.Write([]byte(p.renderRedirectPage(*result.Data.RedirectURL)))
} else {
http.Error(rw, "Unauthorized", http.StatusUnauthorized)
}
return
}
if result.Data.RedirectURL != nil && *result.Data.RedirectURL != "" {
fmt.Println("Badger: Redirecting to", *result.Data.RedirectURL)
http.Redirect(rw, req, *result.Data.RedirectURL, http.StatusFound)
@@ -183,6 +291,23 @@ func (p *Badger) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
}
if result.Data.Valid {
if result.Data.Username != nil {
req.Header.Add("Remote-User", *result.Data.Username)
}
if result.Data.Email != nil {
req.Header.Add("Remote-Email", *result.Data.Email)
}
if result.Data.Name != nil {
req.Header.Add("Remote-Name", *result.Data.Name)
}
if result.Data.Role != nil {
req.Header.Add("Remote-Role", *result.Data.Role)
}
fmt.Println("Badger: Valid session")
p.next.ServeHTTP(rw, req)
return
@@ -213,3 +338,117 @@ func (p *Badger) getScheme(req *http.Request) string {
}
return "http"
}
func (p *Badger) renderRedirectPage(redirectURL string) string {
return fmt.Sprintf(`<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Redirecting...</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f5f5f5;
}
.container {
text-align: center;
padding: 2rem;
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
a {
color: #0066cc;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class="container">
<p>Redirecting...</p>
<p>If you are not redirected automatically, <a href="%s">click here</a>.</p>
</div>
<script>
window.location.href = "%s";
</script>
</body>
</html>`, redirectURL, redirectURL)
}
func (p *Badger) getRealIP(req *http.Request) string {
// Check if request comes from a trusted source
isTrusted := p.isTrustedIP(req.RemoteAddr)
// If custom IP header is configured, use it
if p.customIPHeader != "" {
if customIP := req.Header.Get(p.customIPHeader); customIP != "" && isTrusted {
return customIP
}
}
// Default: use CF-Connecting-IP if from trusted source
if isTrusted {
if cfIP := req.Header.Get(cfConnectingIP); cfIP != "" {
return cfIP
}
}
// Fallback: extract IP from RemoteAddr
ip, _, err := net.SplitHostPort(req.RemoteAddr)
if err != nil {
// If parsing fails, return RemoteAddr as-is (might be just IP without port)
return req.RemoteAddr
}
return ip
}
func (p *Badger) isTrustedIP(remoteAddr string) bool {
ipStr, _, err := net.SplitHostPort(remoteAddr)
if err != nil {
return false
}
ip := net.ParseIP(ipStr)
if ip == nil {
return false
}
for _, network := range p.trustIP {
if network.Contains(ip) {
return true
}
}
return false
}
func (p *Badger) setIPHeaders(req *http.Request, realIP string) {
isTrusted := p.isTrustedIP(req.RemoteAddr)
if isTrusted {
// Handle CF-Visitor header for scheme
if req.Header.Get(cfVisitor) != "" {
var cfVisitorValue struct {
Scheme string `json:"scheme"`
}
if err := json.Unmarshal([]byte(req.Header.Get(cfVisitor)), &cfVisitorValue); err == nil {
req.Header.Set(xForwardProto, cfVisitorValue.Scheme)
}
}
// Set headers with the real IP (already extracted from CF-Connecting-IP or custom header)
req.Header.Set(xForwardFor, realIP)
req.Header.Set(xRealIP, realIP)
} else {
// Not from trusted source, use direct IP
req.Header.Set(xRealIP, realIP)
// Remove CF headers if present
req.Header.Del(cfVisitor)
req.Header.Del(cfConnectingIP)
}
}

56
updateCFIps.sh Executable file
View File

@@ -0,0 +1,56 @@
rm CFIPs.txt
curl https://www.cloudflare.com/ips-v4 >>CFIPs.txt
echo "" >>CFIPs.txt
curl https://www.cloudflare.com/ips-v6 >>CFIPs.txt
echo "" >>CFIPs.txt
OUTPUT_GO_CONFIG="./ips/ips.go"
OUTPUT_GO_CONFIG_OLD="./ips-temp.go"
mv $OUTPUT_GO_CONFIG $OUTPUT_GO_CONFIG_OLD
echo "// Package ips contains a list of current cloud flare IP ranges" >>$OUTPUT_GO_CONFIG
echo "package ips" >>$OUTPUT_GO_CONFIG
echo "" >>$OUTPUT_GO_CONFIG
echo "// CFIPs is the CloudFlare Server IP list (this is checked on build)." >>$OUTPUT_GO_CONFIG
echo "func CFIPs() []string {" >>$OUTPUT_GO_CONFIG
echo " return []string{" >>$OUTPUT_GO_CONFIG
cat CFIPs.txt | while read line || [[ -n $line ]]; do
printf '%s\n' "CF IP: $line"
echo " \"${line}\"," >>$OUTPUT_GO_CONFIG
done
echo " }" >>$OUTPUT_GO_CONFIG
echo "}" >>$OUTPUT_GO_CONFIG
rm CFIPs.txt
if [ "${1}" == "pc" ]; then
echo "Run on pre-commit hook."
if cmp --silent -- "$OUTPUT_GO_CONFIG" "$OUTPUT_GO_CONFIG_OLD"; then
echo "No changes, nothing to worry about"
else
echo "Cloud flare have changed their IPs, adding changes to commit."
touch ./.commit
fi
rm $OUTPUT_GO_CONFIG_OLD
exit
fi
if [ "${1}" != "ci" ]; then
echo "Not run on CI, exit ok"
rm $OUTPUT_GO_CONFIG_OLD
exit
fi
if cmp --silent -- "$OUTPUT_GO_CONFIG" "$OUTPUT_GO_CONFIG_OLD"; then
echo "No changes to Cloud Flare IP list"
rm $OUTPUT_GO_CONFIG_OLD
else
echo "Cloud flare have changed their IPs, re-run updateCFIps.sh and commit the changes!"
rm $OUTPUT_GO_CONFIG_OLD
exit 6
fi