Difference between revisions of "Mimir push reverse proxy in go"

From UVOO Tech Wiki
Jump to navigation Jump to search
Line 1: Line 1:
 +
```
 +
sqlite3 auth.db
 +
```
 +
 
```
 
```
 
CREATE TABLE users (
 
CREATE TABLE users (
Line 13: Line 17:
  
 
import (
 
import (
"database/sql"
+
    "database/sql"
"encoding/base64"
+
    "encoding/base64"
"fmt"
+
    "log"
"io"
+
    "net/http"
"log"
+
    "net/http/httputil"
"net/http"
+
    "net/url"
"net/http/httputil"
+
    "os"
"net/url"
+
    "strings"
"strings"
 
  
_ "github.com/mattn/go-sqlite3"
+
    _ "github.com/mattn/go-sqlite3"
 
)
 
)
  
Line 30: Line 33:
  
 
func main() {
 
func main() {
var err error
+
    var err error
db, err = sql.Open("sqlite3", "./auth.db")
+
    db, err = sql.Open("sqlite3", "./auth.db")
if err != nil {
+
    if err != nil {
log.Fatalf("Failed to open database: %v", err)
+
        log.Fatalf("Failed to open database: %v", err)
}
+
    }
defer db.Close()
+
    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)
 +
    }
  
targetURL := "http://mimir-server:9009" // Change this to your Mimir endpoint
+
    proxy := httputil.NewSingleHostReverseProxy(target)
target, err := url.Parse(targetURL)
+
    // Modify the proxy's director to add backend Basic Auth credentials
if err != nil {
+
    originalDirector := proxy.Director
log.Fatalf("Invalid target URL: %v", err)
+
    proxy.Director = func(req *http.Request) {
}
+
        originalDirector(req)
 +
        req.SetBasicAuth(backendUsername, backendPassword)
 +
    }
  
proxy := httputil.NewSingleHostReverseProxy(target)
+
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+
        if !authenticate(r) {
if !authenticate(r) {
+
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
http.Error(w, "Unauthorized", http.StatusUnauthorized)
+
            return
return
+
        }
}
+
        proxy.ServeHTTP(w, r)
proxy.ServeHTTP(w, r)
+
    })
})
 
  
log.Println("Reverse proxy running on :8080")
+
    log.Println("Reverse proxy running on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
+
    log.Fatal(http.ListenAndServe(":8080", nil))
 
}
 
}
  
 
// Authenticate using Basic Auth with SQLite
 
// Authenticate using Basic Auth with SQLite
 
func authenticate(r *http.Request) bool {
 
func authenticate(r *http.Request) bool {
authHeader := r.Header.Get("Authorization")
+
    authHeader := r.Header.Get("Authorization")
if authHeader == "" || !strings.HasPrefix(authHeader, "Basic ") {
+
    if authHeader == "" || !strings.HasPrefix(authHeader, "Basic ") {
return false
+
        return false
}
+
    }
  
decoded, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(authHeader, "Basic "))
+
    decoded, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(authHeader, "Basic "))
if err != nil {
+
    if err != nil {
return false
+
        return false
}
+
    }
  
parts := strings.SplitN(string(decoded), ":", 2)
+
    parts := strings.SplitN(string(decoded), ":", 2)
if len(parts) != 2 {
+
    if len(parts) != 2 {
return false
+
        return false
}
+
    }
  
username, password := parts[0], parts[1]
+
    username, password := parts[0], parts[1]
return validateUser(username, password)
+
    return validateUser(username, password)
 
}
 
}
  
 
// Validate user from SQLite database
 
// Validate user from SQLite database
 
func validateUser(username, password string) bool {
 
func validateUser(username, password string) bool {
var storedPassword string
+
    var storedPassword string
err := db.QueryRow("SELECT password FROM users WHERE username = ?", username).Scan(&storedPassword)
+
    err := db.QueryRow("SELECT password FROM users WHERE username = ?", username).Scan(&storedPassword)
if err != nil {
+
    if err != nil {
return false
+
        return false
}
+
    }
return password == stor9edPassword
+
    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
 +
```
 +
 +
  
 
```
 
```

Revision as of 16:27, 23 February 2025

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 '{}'