Mimir push reverse proxy in go

From UVOO Tech Wiki
Revision as of 16:27, 23 February 2025 by Busk (talk | contribs)
Jump to navigation Jump to search
sqlite3 auth.db
CREATE TABLE users (
    username TEXT PRIMARY KEY,
    password TEXT NOT NULL
);

INSERT INTO users (username, password) VALUES ('admin', 'password123');

package main

import (
    "database/sql"
    "encoding/base64"
    "log"
    "net/http"
    "net/http/httputil"
    "net/url"
    "os"
    "strings"

    _ "github.com/mattn/go-sqlite3"
)

// Database connection
var db *sql.DB

func main() {
    var err error
    db, err = sql.Open("sqlite3", "./auth.db")
    if err != nil {
        log.Fatalf("Failed to open database: %v", err)
    }
    defer db.Close()

    // Read environment variables for backend credentials and target URL
    backendUsername := os.Getenv("BACKEND_USERNAME")
    if backendUsername == "" {
        log.Fatal("Environment variable BACKEND_USERNAME is required")
    }

    backendPassword := os.Getenv("BACKEND_PASSWORD")
    if backendPassword == "" {
        log.Fatal("Environment variable BACKEND_PASSWORD is required")
    }

    targetURL := os.Getenv("TARGET_URL")
    if targetURL == "" {
        log.Fatal("Environment variable TARGET_URL is required")
    }

    target, err := url.Parse(targetURL)
    if err != nil {
        log.Fatalf("Invalid target URL: %v", err)
    }

    proxy := httputil.NewSingleHostReverseProxy(target)
    // Modify the proxy's director to add backend Basic Auth credentials
    originalDirector := proxy.Director
    proxy.Director = func(req *http.Request) {
        originalDirector(req)
        req.SetBasicAuth(backendUsername, backendPassword)
    }

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        if !authenticate(r) {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }
        proxy.ServeHTTP(w, r)
    })

    log.Println("Reverse proxy running on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

// Authenticate using Basic Auth with SQLite
func authenticate(r *http.Request) bool {
    authHeader := r.Header.Get("Authorization")
    if authHeader == "" || !strings.HasPrefix(authHeader, "Basic ") {
        return false
    }

    decoded, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(authHeader, "Basic "))
    if err != nil {
        return false
    }

    parts := strings.SplitN(string(decoded), ":", 2)
    if len(parts) != 2 {
        return false
    }

    username, password := parts[0], parts[1]
    return validateUser(username, password)
}

// Validate user from SQLite database
func validateUser(username, password string) bool {
    var storedPassword string
    err := db.QueryRow("SELECT password FROM users WHERE username = ?", username).Scan(&storedPassword)
    if err != nil {
        return false
    }
    return password == storedPassword
}
mkdir push-proxy
cd push-proxy
go mod init
go mod tidy
go build
set -a
TARGET_URL=https://myprompush.example.com
BACKEND_USERNAME=foo
BACKEND_PASSWORD=bar
curl -u admin:password123 http://localhost:8080/api/v1/push -d '{}'