Working
This commit is contained in:
parent
c67be49a46
commit
03251290fc
34
Dockerfile
Normal file
34
Dockerfile
Normal file
@ -0,0 +1,34 @@
|
||||
FROM golang:1.22 as builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy go module files and download dependencies
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
# Copy the rest of the application
|
||||
COPY . .
|
||||
|
||||
# Build the Go application
|
||||
RUN go build -o main .
|
||||
|
||||
# Stage 2: Create a minimal production image
|
||||
FROM alpine:latest
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy the built application from the builder stage
|
||||
COPY --from=builder /app/main .
|
||||
|
||||
# Copy the .env file (if exists)
|
||||
COPY .env .env
|
||||
|
||||
# Expose the necessary port (change as needed)
|
||||
EXPOSE 8080
|
||||
|
||||
# Run the application
|
||||
CMD ["./main"]
|
||||
|
||||
# Healthcheck to ensure the app is running
|
||||
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
|
||||
CMD curl -f http://localhost:8080/health || exit 1
|
||||
@ -1,2 +1,10 @@
|
||||
# CCTV
|
||||
|
||||
## .env Configuration
|
||||
```env
|
||||
UploadDir=./upload
|
||||
LOG_FILE=auth_failures.log
|
||||
MaxFailedAttempts=3
|
||||
BanDuration=5
|
||||
BearerToken=jhasd083raASDasdoad§$234
|
||||
````
|
||||
41
cmd/main.go
Normal file
41
cmd/main.go
Normal file
@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.neunzweinull.com/jan/CCTV/config"
|
||||
"git.neunzweinull.com/jan/CCTV/internal"
|
||||
)
|
||||
|
||||
const (
|
||||
CleanupInterval = 5 * time.Minute
|
||||
)
|
||||
|
||||
func init() {
|
||||
//Load the env file
|
||||
config.LoadEnv()
|
||||
|
||||
//Initialize the log file
|
||||
internal.InitLog()
|
||||
|
||||
//Initialize the Upload Directory
|
||||
config.InitUploadDir()
|
||||
|
||||
//Start background cleanup for expired bans
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(CleanupInterval)
|
||||
internal.CleanupBans()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func main() {
|
||||
defer internal.CloseLog()
|
||||
|
||||
http.HandleFunc("/upload", internal.AuthMiddleware(internal.UploadHandler))
|
||||
http.HandleFunc("/health", internal.HealthHandler)
|
||||
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
||||
62
config/env.go
Normal file
62
config/env.go
Normal file
@ -0,0 +1,62 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"git.neunzweinull.com/jan/CCTV/internal"
|
||||
)
|
||||
|
||||
const (
|
||||
Prefix = "CCTV_"
|
||||
)
|
||||
|
||||
func LoadEnv() error {
|
||||
// TODO: Load env from custom file
|
||||
setDefaultEnvs()
|
||||
|
||||
file, err := os.Open(".env")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := strings.Split(scanner.Text(), "=")
|
||||
|
||||
//Ignore empty lines and comments
|
||||
if len(line) < 2 || strings.HasPrefix(line[0], "#") {
|
||||
continue
|
||||
}
|
||||
|
||||
key := strings.TrimSpace(line[0])
|
||||
value := strings.TrimSpace(line[1])
|
||||
|
||||
if key == "" || value == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Add prefix
|
||||
key = Prefix + key
|
||||
|
||||
// Remove quotes
|
||||
value = strings.Trim(value, `"'`)
|
||||
|
||||
os.Setenv(key, value)
|
||||
|
||||
internal.SetShit()
|
||||
}
|
||||
|
||||
return scanner.Err()
|
||||
}
|
||||
|
||||
func setDefaultEnvs() {
|
||||
os.Setenv(Prefix+"UploadDir", "uploads/")
|
||||
os.Setenv(Prefix+"LOG_FILE", "auth_failures.log")
|
||||
|
||||
os.Setenv(Prefix+"MaxFailedAttempts", "3")
|
||||
os.Setenv(Prefix+"BanDuration", "5")
|
||||
}
|
||||
18
config/upload.go
Normal file
18
config/upload.go
Normal file
@ -0,0 +1,18 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func InitUploadDir() {
|
||||
if _, err := os.Stat(os.Getenv("CCTV_UploadDir")); err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
err := os.Mkdir(os.Getenv("CCTV_UploadDir"), 0755)
|
||||
if err != nil {
|
||||
fmt.Println("Error creating upload directory:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
13
internal/bans.go
Normal file
13
internal/bans.go
Normal file
@ -0,0 +1,13 @@
|
||||
package internal
|
||||
|
||||
import "time"
|
||||
|
||||
func CleanupBans() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
for ip, banTime := range bannedIPs {
|
||||
if time.Now().After(banTime) {
|
||||
delete(bannedIPs, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
71
internal/log.go
Normal file
71
internal/log.go
Normal file
@ -0,0 +1,71 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
logFile *os.File
|
||||
mu sync.Mutex
|
||||
)
|
||||
|
||||
func InitLog() {
|
||||
var err error
|
||||
logFileName := os.Getenv("CCTV_LOG_FILE")
|
||||
if logFileName == "" {
|
||||
logFileName = "app.log" // Default log file name if the environment variable is not set
|
||||
}
|
||||
|
||||
var logFilePath string
|
||||
if runtime.GOOS == "windows" {
|
||||
logFilePath = filepath.Join("C:\\ProgramData\\CCTV", logFileName)
|
||||
} else {
|
||||
logFilePath = filepath.Join("/var/log", logFileName)
|
||||
}
|
||||
|
||||
// Ensure directory exists
|
||||
err = os.MkdirAll(filepath.Dir(logFilePath), 0755)
|
||||
if err != nil {
|
||||
fmt.Println("Error creating log directory:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
logFile, err = os.OpenFile(logFilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
fmt.Println("Error opening log file:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func LogFailedAttempt(ip string) {
|
||||
logEntry := fmt.Sprintf("[%s] Failed auth attempt from IP: %s\n", time.Now().Format(time.RFC3339), ip)
|
||||
logFile.WriteString(logEntry)
|
||||
logFile.Sync()
|
||||
|
||||
fmt.Print(logEntry)
|
||||
}
|
||||
|
||||
func LogBan(ip string) {
|
||||
logEntry := fmt.Sprintf("[%s] Banned IP: %s\n", time.Now().Format(time.RFC3339), ip)
|
||||
logFile.WriteString(logEntry)
|
||||
logFile.Sync()
|
||||
|
||||
fmt.Print(logEntry)
|
||||
}
|
||||
|
||||
func LogSuccessUpload(ip string, fileName string) {
|
||||
logEntry := fmt.Sprintf("[%s] Successfully uploaded file %s from IP: %s\n", time.Now().Format(time.RFC3339), fileName, ip)
|
||||
logFile.WriteString(logEntry)
|
||||
logFile.Sync()
|
||||
|
||||
fmt.Print(logEntry)
|
||||
}
|
||||
|
||||
func CloseLog() {
|
||||
logFile.Close()
|
||||
}
|
||||
131
internal/server.go
Normal file
131
internal/server.go
Normal file
@ -0,0 +1,131 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
MaxFailedAttempts = 3
|
||||
BanDuration = 5 * time.Minute
|
||||
UploadDir = ""
|
||||
failedAttempts = make(map[string]int)
|
||||
bannedIPs = make(map[string]time.Time)
|
||||
)
|
||||
|
||||
func SetShit() {
|
||||
if maxAttempts, err := strconv.Atoi(os.Getenv("CCTV_MaxFailedAttempts")); err == nil {
|
||||
MaxFailedAttempts = maxAttempts
|
||||
}
|
||||
if banDuration, err := time.ParseDuration(os.Getenv("CCTV_BanDuration")); err == nil {
|
||||
BanDuration = banDuration * time.Minute
|
||||
}
|
||||
UploadDir = os.Getenv("CCTV_UploadDir")
|
||||
}
|
||||
|
||||
func UploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||
err := r.ParseMultipartForm(10 << 20)
|
||||
if err != nil {
|
||||
http.Error(w, "Error parsing form", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
file, handler, err := r.FormFile("file")
|
||||
if err != nil {
|
||||
http.Error(w, "Error retrieving file", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
filePath := filepath.Join(UploadDir, handler.Filename)
|
||||
|
||||
if _, err := os.Stat(filePath); err == nil {
|
||||
http.Error(w, "File already exists", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
out, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
http.Error(w, "Error creating file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
defer out.Close()
|
||||
|
||||
_, err = io.Copy(out, file)
|
||||
if err != nil {
|
||||
http.Error(w, "Error saving file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
LogSuccessUpload(getClientIP(r), handler.Filename)
|
||||
}
|
||||
|
||||
func AuthMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ip := getClientIP(r)
|
||||
|
||||
mu.Lock()
|
||||
|
||||
// Check if IP is banned
|
||||
if banTime, banned := bannedIPs[ip]; banned {
|
||||
fmt.Print("Banned IP: ", ip, " until ", banTime, "\n")
|
||||
if time.Now().Before(banTime) {
|
||||
bannedIPs[ip] = time.Now().Add(BanDuration)
|
||||
|
||||
http.Error(w, "You are banned", http.StatusForbidden)
|
||||
mu.Unlock()
|
||||
return
|
||||
}
|
||||
delete(bannedIPs, ip)
|
||||
}
|
||||
|
||||
mu.Unlock()
|
||||
|
||||
authHeader := r.Header.Get("Authorization")
|
||||
if !strings.HasPrefix(authHeader, "Bearer ") || strings.TrimPrefix(authHeader, "Bearer ") != os.Getenv("CCTV_BearerToken") {
|
||||
LogFailedAttempt(ip)
|
||||
|
||||
mu.Lock()
|
||||
failedAttempts[ip]++
|
||||
if failedAttempts[ip] >= MaxFailedAttempts {
|
||||
bannedIPs[ip] = time.Now().Add(BanDuration)
|
||||
delete(failedAttempts, ip)
|
||||
|
||||
LogBan(ip)
|
||||
}
|
||||
mu.Unlock()
|
||||
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
delete(failedAttempts, ip)
|
||||
mu.Unlock()
|
||||
|
||||
next(w, r)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func HealthHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
// Get client IP address from request
|
||||
func getClientIP(r *http.Request) string {
|
||||
ip, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
return r.RemoteAddr // Fallback
|
||||
}
|
||||
return ip
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user