Added CIDR support, automatically detect Docker subnet, updated makefile

This commit is contained in:
Marc Ole Bulling
2026-01-31 11:29:45 +01:00
parent 8282f729dc
commit fbfc4bc719
5 changed files with 183 additions and 95 deletions

View File

@@ -1,8 +1,14 @@
# Define variables
IMAGE_NAME=gokapi-builder
CONTAINER_WORK_DIR=/usr/src/myapp
#To use podman, use make CONTAINER_TOOL=podman
CONTAINER_TOOL?=podman
# Check for docker, then podman, otherwise use what the user provided
ifeq ($(origin CONTAINER_TOOL), undefined)
CONTAINER_TOOL := $(shell command -v docker 2> /dev/null || command -v podman 2> /dev/null)
endif
# Fallback if neither is found and nothing was provided
CONTAINER_TOOL ?= docker
# Default target
all: compile

View File

@@ -59,81 +59,85 @@ Available environment variables
==================================
+--------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| Name | Action | Persistent [*]_ | Default |
+================================+========================================================================================+=================+=============================+
| GOKAPI_CHUNK_SIZE_MB | Sets the size of chunks that are uploaded in MB | Yes | 45 |
+--------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_CONFIG_DIR | Sets the directory for the config file | No | config |
+--------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_CONFIG_FILE | Sets the name of the config file | No | config.json |
+--------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_DATA_DIR | Sets the directory for the data | Yes | data |
+--------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_DISABLE_CORS_CHECK | Disables the CORS check on startup and during setup, if set to true | No | false |
+--------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_ENABLE_HOTLINK_VIDEOS | Allow hotlinking of videos. Note: Due to buffering, playing a video might count as | No | false |
| | | | |
| | multiple downloads. It is only recommended to use video hotlinking for uploads with | | |
| | | | |
| | unlimited downloads enabled | | |
+--------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_GUEST_UPLOAD_BY_DEFAULT | Allows all users by default to create file requests, if set to true | No | false |
+--------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_LENGTH_HOTLINK_ID | Sets the length of the hotlink IDs. Value must be 8 or greater | No | 40 |
+--------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_LENGTH_ID | Sets the length of the download IDs. Value must be 5 or greater | No | 15 |
+--------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_LOG_STDOUT | Also outputs all log file entries to the console output, if set to true | No | false |
+--------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_MAX_FILESIZE | Sets the maximum allowed file size in MB | Yes | 102400 |
| | | | |
| | Default 102400 = 100GB | | |
+--------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_MAX_FILES_GUESTUPLOAD | Sets the maximum number of files that can be uploaded per file requests created by | No | 100 |
| | | | |
| | non-admin users | | |
| | | | |
| | Set to 0 to allow unlimited file count for all users | | |
+--------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_MAX_MEMORY_UPLOAD | Sets the amount of RAM in MB that can be allocated for an upload chunk or file | Yes | 50 |
| | | | |
| | Any chunk or file with a size greater than that will be written to a temporary file | | |
+--------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_MAX_PARALLEL_UPLOADS | Set the number of chunks that are uploaded in parallel for a single file | Yes | 3 |
+--------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_MAX_SIZE_GUESTUPLOAD | Sets the maximum file size for file requests created by | No | 10240 |
| | | | |
| | non-admin users | | |
| | | | |
| | Set to 0 to allow files with a size of up to a value set with GOKAPI_MAX_FILESIZE | | |
| | | | |
| | for all users | | |
| | | | |
| | Default 10240 = 10GB | | |
+--------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_MIN_FREE_SPACE | Sets the minium free space on the disk in MB for accepting an upload | No | 400 |
+--------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_MIN_LENGTH_PASSWORD | Sets the minium password length. Value must be 6 or greater | No | 8 |
+--------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_PORT | Sets the webserver port | Yes | 53842 |
+--------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_TRUSTED_PROXIES | Sets a list of trusted proxies. If set, the webserver will trust the IP addresses sent | No | 127.0.0.1 |
| | | | |
| | by these proxies with the X-Forwarded-For and X-REAL-IP header | | |
| | | | |
| | List is comma separated | | |
+--------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_USE_CLOUDFLARE | Set this to true if you are using Cloudflare | No | false |
+--------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| TMPDIR | Sets the path which contains temporary files | No | Non-Docker: Default OS path |
| | | | |
| | | | Docker: [DATA_DIR] |
+--------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| DOCKER_NONROOT | DEPRECATED. | No | false |
| | | | |
| | Docker only: Runs the binary in the container as a non-root user, if set to "true" | | |
+--------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| Name | Action | Persistent [*]_ | Default |
+=====================================+========================================================================================+=================+=============================+
| GOKAPI_CHUNK_SIZE_MB | Sets the size of chunks that are uploaded in MB | Yes | 45 |
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_CONFIG_DIR | Sets the directory for the config file | No | config |
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_CONFIG_FILE | Sets the name of the config file | No | config.json |
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_DATA_DIR | Sets the directory for the data | Yes | data |
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_DISABLE_CORS_CHECK | Disables the CORS check on startup and during setup, if set to true | No | false |
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_DISABLE_DOCKER_TRUSTED_PROXY | Disables automatically adding Docker subnet to trusted proxies, if set to true | No | false |
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_ENABLE_HOTLINK_VIDEOS | Allow hotlinking of videos. Note: Due to buffering, playing a video might count as | No | false |
| | | | |
| | multiple downloads. It is only recommended to use video hotlinking for uploads with | | |
| | | | |
| | unlimited downloads enabled | | |
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_GUEST_UPLOAD_BY_DEFAULT | Allows all users by default to create file requests, if set to true | No | false |
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_LENGTH_HOTLINK_ID | Sets the length of the hotlink IDs. Value must be 8 or greater | No | 40 |
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_LENGTH_ID | Sets the length of the download IDs. Value must be 5 or greater | No | 15 |
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_LOG_STDOUT | Also outputs all log file entries to the console output, if set to true | No | false |
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_MAX_FILESIZE | Sets the maximum allowed file size in MB | Yes | 102400 |
| | | | |
| | Default 102400 = 100GB | | |
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_MAX_FILES_GUESTUPLOAD | Sets the maximum number of files that can be uploaded per file requests created by | No | 100 |
| | | | |
| | non-admin users | | |
| | | | |
| | Set to 0 to allow unlimited file count for all users | | |
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_MAX_MEMORY_UPLOAD | Sets the amount of RAM in MB that can be allocated for an upload chunk or file | Yes | 50 |
| | | | |
| | Any chunk or file with a size greater than that will be written to a temporary file | | |
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_MAX_PARALLEL_UPLOADS | Set the number of chunks that are uploaded in parallel for a single file | Yes | 3 |
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_MAX_SIZE_GUESTUPLOAD | Sets the maximum file size for file requests created by | No | 10240 |
| | | | |
| | non-admin users | | |
| | | | |
| | Set to 0 to allow files with a size of up to a value set with GOKAPI_MAX_FILESIZE | | |
| | | | |
| | for all users | | |
| | | | |
| | Default 10240 = 10GB | | |
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_MIN_FREE_SPACE | Sets the minium free space on the disk in MB for accepting an upload | No | 400 |
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_MIN_LENGTH_PASSWORD | Sets the minium password length. Value must be 6 or greater | No | 8 |
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_PORT | Sets the webserver port | Yes | 53842 |
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_TRUSTED_PROXIES | Sets a list of trusted proxies. If set, the webserver will trust the IP addresses sent | No | 127.0.0.1 |
| | | | |
| | by these proxies with the X-Forwarded-For and X-REAL-IP header | | |
| | | | |
| | List is comma separated; entries can be fixed IPs ("10.0.0.1, 10.0.0.2") | | |
| | | | |
| | and subnets ("10.0.0.0/24") | | |
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| GOKAPI_USE_CLOUDFLARE | Set this to true if you are using Cloudflare | No | false |
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| TMPDIR | Sets the path which contains temporary files | No | Non-Docker: Default OS path |
| | | | |
| | | | Docker: [DATA_DIR] |
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
| DOCKER_NONROOT | DEPRECATED. | No | false |
| | | | |
| | Docker only: Runs the binary in the container as a non-root user, if set to "true" | | |
+-------------------------------------+----------------------------------------------------------------------------------------+-----------------+-----------------------------+
.. [*] Variables that are persistent must be submitted during the first start when Gokapi creates a new config file. They can be omitted afterwards. Non-persistent variables need to be set on every start.

View File

@@ -32,6 +32,8 @@ type Environment struct {
DataDir string `env:"DATA_DIR" envDefault:"data" persistent:"true"`
// Disables the CORS check on startup and during setup, if set to true
DisableCorsCheck bool `env:"DISABLE_CORS_CHECK" envDefault:"false"`
// Disables automatically adding Docker subnet to trusted proxies, if set to true
DisableDockerTrustedProxy bool `env:"DISABLE_DOCKER_TRUSTED_PROXY" envDefault:"false"`
// Sets the size of chunks that are uploaded in MB
ChunkSizeMB int `env:"CHUNK_SIZE_MB" envDefault:"45" onlyPositive:"true" persistent:"true"`
// Sets the length of the download IDs
@@ -66,7 +68,8 @@ type Environment struct {
PermRequestGrantedByDefault bool `env:"GUEST_UPLOAD_BY_DEFAULT" envDefault:"false"`
// Sets a list of trusted proxies. If set, the webserver will trust the IP addresses sent
// by these proxies with the X-Forwarded-For and X-REAL-IP header
// List is comma separated
// List is comma separated; entries can be fixed IPs ("10.0.0.1, 10.0.0.2")
// and subnets ("10.0.0.0/24")
TrustedProxies []string `env:"TRUSTED_PROXIES" envSeparator:"," envDefault:"127.0.0.1"`
// Set this to true if you are using Cloudflare
UseCloudFlare bool `env:"USE_CLOUDFLARE" envDefault:"false"`

View File

@@ -28,15 +28,69 @@ const categoryWarning = "warning"
var outputToStdout = false
var useCloudflare = false
var trustedProxies []string
var parsedTrustedIPs []net.IP
var parsedTrustedCIDRs []*net.IPNet
// Init sets the path where to write the log file to
func Init(filePath string) {
logPath = filePath + "/log.txt"
env := environment.New()
outputToStdout = env.LogToStdout
trustedProxies = env.TrustedProxies
useCloudflare = env.UseCloudFlare
parseTrustedProxies(env.TrustedProxies, !env.DisableDockerTrustedProxy)
}
// parseTrustedProxies processes the raw strings into net.IP and net.IPNet objects
func parseTrustedProxies(proxies []string, useDockerSubnet bool) {
parsedTrustedIPs = nil
parsedTrustedCIDRs = nil
if environment.IsDockerInstance() && useDockerSubnet {
subnet, err := getDockerSubnet()
if err == nil {
parsedTrustedCIDRs = append(parsedTrustedCIDRs, subnet)
}
}
for _, proxy := range proxies {
proxy = strings.TrimSpace(proxy)
if strings.Contains(proxy, "/") {
// Handle CIDR (e.g., "10.0.0.0/24")
_, ipNet, err := net.ParseCIDR(proxy)
if err == nil {
parsedTrustedCIDRs = append(parsedTrustedCIDRs, ipNet)
}
} else {
// Handle Fixed IP (e.g., "127.0.0.1")
ip := net.ParseIP(proxy)
if ip != nil {
parsedTrustedIPs = append(parsedTrustedIPs, ip)
}
}
}
}
func getDockerSubnet() (*net.IPNet, error) {
addrs, err := net.InterfaceAddrs()
if err != nil {
return nil, err
}
for _, addr := range addrs {
if ipnet, ok := addr.(*net.IPNet); ok {
// Docker typically uses these private ranges
// Common: 172.16.0.0/12, 192.168.0.0/16, 10.0.0.0/8
// Docker bridge default: 172.17.0.0/16
if ipnet.IP.IsPrivate() && !ipnet.IP.IsLoopback() {
// Skip if it's just the host IP (not a subnet)
if ipnet.IP.To4() != nil {
return ipnet, nil
}
}
}
}
return nil, fmt.Errorf("no Docker subnet found")
}
// GetAll returns all log entries as a single string and if the log file exists
@@ -339,24 +393,26 @@ func getDate(timestamp time.Time) string {
return timestamp.UTC().Format(time.RFC1123)
}
func isTrustedProxy(ip string) bool {
for _, proxy := range trustedProxies {
if ip == proxy {
func isTrustedProxy(ip net.IP) bool {
// Check against fixed IPs
for _, trustedIP := range parsedTrustedIPs {
if trustedIP.Equal(ip) {
return true
}
}
// Check against CIDR ranges
for _, trustedNet := range parsedTrustedCIDRs {
if trustedNet.Contains(ip) {
return true
}
}
return false
}
// GetIpAddress returns the IP address of the requester
func GetIpAddress(r *http.Request) string {
ip, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
ip = r.RemoteAddr
}
// Clean up if it is an IPv6 zone
netIP := net.ParseIP(ip)
if useCloudflare {
cfIp := r.Header.Get("CF-Connecting-IP")
@@ -365,19 +421,30 @@ func GetIpAddress(r *http.Request) string {
}
}
ip, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
ip = r.RemoteAddr
}
// Clean up if it is an IPv6 zone
netIP := net.ParseIP(ip)
// Check if the immediate connector is a Trusted Proxy and if yes, use their header for IP
// Otherwise this returns the actual IP used for the connection
if netIP != nil && isTrustedProxy(netIP.String()) {
if netIP != nil && isTrustedProxy(netIP) {
// Check X-Forwarded-For
// Ideally, use the last IP in the list if a proxy appends to it
xff := r.Header.Get("X-FORWARDED-FOR")
if xff != "" {
ips := strings.Split(xff, ",")
//Take the last IP or investigate based on proxy setup
realIP := strings.TrimSpace(ips[len(ips)-1])
if net.ParseIP(realIP) != nil {
return realIP
// Iterate from right to left, skip trusted proxies
for i := len(ips) - 1; i >= 0; i-- {
ipXff := strings.TrimSpace(ips[i])
parsedIP := net.ParseIP(ipXff)
if parsedIP != nil && !isTrustedProxy(parsedIP) {
return ipXff
}
}
}

View File

@@ -2,8 +2,16 @@ GOPACKAGE=github.com/forceu/gokapi
BUILD_FLAGS=-ldflags="-s -w -X '$(GOPACKAGE)/internal/environment.Builder=Make Script' -X '$(GOPACKAGE)/internal/environment.BuildTime=$(shell date)'"
BUILD_FLAGS_DEBUG=-ldflags="-X '$(GOPACKAGE)/internal/environment.Builder=Make Script' -X '$(GOPACKAGE)/internal/environment.BuildTime=$(shell date)'"
DOCKER_IMAGE_NAME=gokapi
# Check for docker, then podman, otherwise use what the user provided
ifeq ($(origin CONTAINER_TOOL), undefined)
CONTAINER_TOOL := $(shell command -v docker 2> /dev/null || command -v podman 2> /dev/null)
endif
# Fallback if neither is found and nothing was provided
CONTAINER_TOOL ?= docker
# Default target
.PHONY: all
all: build