Updated the README

Added new security layers
This commit is contained in:
2025-08-22 12:39:51 -04:00
parent fb7428064f
commit 720c7e0b52
7 changed files with 695 additions and 552 deletions

View File

@@ -26,7 +26,7 @@ func getenvBool(key string, def bool) bool {
func staticHeaders(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Same security posture as API
// Security posture for static client
w.Header().Set("Referrer-Policy", "no-referrer")
w.Header().Set("Cross-Origin-Opener-Policy", "same-origin")
w.Header().Set("Cross-Origin-Resource-Policy", "same-site")
@@ -35,7 +35,10 @@ func staticHeaders(next http.Handler) http.Handler {
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("Strict-Transport-Security", "max-age=15552000; includeSubDomains; preload")
// Basic CORS for client assets
// Strong CSP to block XSS/token theft (enumerate your API host)
w.Header().Set("Content-Security-Policy", "default-src 'self'; base-uri 'none'; object-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self' https://api-gc.fullmooncyberworks.com; frame-ancestors 'none'")
// CORS for assets
w.Header().Set("Access-Control-Allow-Origin", "*")
if r.Method == http.MethodOptions {
w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS")
@@ -48,14 +51,11 @@ func staticHeaders(next http.Handler) http.Handler {
}
func main() {
// ---- Config via env ----
httpAddr := os.Getenv("GC_HTTP_ADDR")
if httpAddr == "" {
httpAddr = ":9080" // API
httpAddr = ":9080"
}
// Optional TLS for API
httpsAddr := os.Getenv("GC_HTTPS_ADDR") // leave empty for HTTP
httpsAddr := os.Getenv("GC_HTTPS_ADDR")
certFile := os.Getenv("GC_TLS_CERT")
keyFile := os.Getenv("GC_TLS_KEY")
@@ -64,7 +64,6 @@ func main() {
dataDir = "/var/lib/greencoast"
}
// Static dir + port (frontend)
staticDir := os.Getenv("GC_STATIC_DIR")
if staticDir == "" {
staticDir = "/opt/greencoast/client"
@@ -78,21 +77,18 @@ func main() {
zeroTrust := getenvBool("GC_ZERO_TRUST", true)
signingSecretHex := os.Getenv("GC_SIGNING_SECRET_HEX")
// Discord SSO
discID := os.Getenv("GC_DISCORD_CLIENT_ID")
discSecret := os.Getenv("GC_DISCORD_CLIENT_SECRET")
discRedirect := os.Getenv("GC_DISCORD_REDIRECT_URI")
// ---- Storage ----
store, err := storage.NewFS(dataDir)
if err != nil {
log.Fatalf("storage init: %v", err)
}
// ---- Index ----
ix := index.New()
// Optional: auto-reindex from disk on boot
// Auto-reindex on boot if possible
if w, ok := any(store).(interface {
Walk(func(hash string, size int64, mod time.Time) error) error
}); ok {
@@ -108,7 +104,6 @@ func main() {
}
}
// ---- Auth/Providers ----
ap := api.AuthProviders{
SigningSecretHex: signingSecretHex,
Discord: api.DiscordProvider{
@@ -119,15 +114,20 @@ func main() {
},
}
// ---- API server (9080/HTTPS optional) ----
srv := api.New(store, ix, coarseTS, zeroTrust, ap)
// Serve the static client in a goroutine on 9082
// Static client server (9082)
go func() {
if st, err := os.Stat(staticDir); err != nil || !st.IsDir() {
log.Printf("WARN: GC_STATIC_DIR %q not found or not a dir; client may 404", staticDir)
}
mux := http.NewServeMux()
// Optional: forward API paths to API host to avoid 404 if user hits wrong host
mux.Handle("/v1/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "https://api-gc.fullmooncyberworks.com"+r.URL.Path, http.StatusTemporaryRedirect)
}))
mux.Handle("/", http.FileServer(http.Dir(staticDir)))
log.Printf("static listening on %s (dir=%s)", staticAddr, staticDir)
if err := http.ListenAndServe(staticAddr, staticHeaders(mux)); err != nil {
@@ -135,7 +135,6 @@ func main() {
}
}()
// Prefer HTTPS if configured
if httpsAddr != "" && certFile != "" && keyFile != "" {
log.Printf("starting HTTPS API on %s", httpsAddr)
if err := srv.ListenHTTPS(httpsAddr, certFile, keyFile); err != nil {
@@ -143,12 +142,8 @@ func main() {
}
return
}
// Otherwise HTTP
log.Printf("starting HTTP API on %s", httpAddr)
if err := srv.ListenHTTP(httpAddr); err != nil {
log.Fatal(err)
}
_ = time.Second
}