diff --git a/build/makefile b/build/makefile index a67ff6f..6c7a358 100644 --- a/build/makefile +++ b/build/makefile @@ -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 diff --git a/docs/advanced.rst b/docs/advanced.rst index 32a4c91..cfda300 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -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. diff --git a/internal/environment/Environment.go b/internal/environment/Environment.go index 7a656c9..c19197a 100644 --- a/internal/environment/Environment.go +++ b/internal/environment/Environment.go @@ -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"` diff --git a/internal/logging/Logging.go b/internal/logging/Logging.go index 9adb2ae..c835770 100644 --- a/internal/logging/Logging.go +++ b/internal/logging/Logging.go @@ -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 + } } } diff --git a/makefile b/makefile index c00e594..8f78549 100644 --- a/makefile +++ b/makefile @@ -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