Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ _Please note: you will need Docker installed on your local machine._

The application uses environment variables to configure all aspects.

### General Configuration

* `J_ARTICLES_PER_PAGE` - Articles to display per page, default `20`
* `J_CREATE` - Set to `0` to disable article creation
* `J_DB_PATH` - Path to SQLite DB - default is `$GOPATH/data/journal.db`
Expand All @@ -59,6 +61,21 @@ The application uses environment variables to configure all aspects.
* `J_THEME` - Theme to use from within the _web/themes_ folder, defaults to `default`
* `J_TITLE` - Set the title of the Journal

### SSL/TLS Configuration

* `J_SSL_CERT` - Path to SSL certificate file for HTTPS (enables SSL when set)
* `J_SSL_KEY` - Path to SSL private key file for HTTPS

### Session and Cookie Security

* `J_SESSION_KEY` - 32-byte encryption key for session data (AES-256). Must be exactly 32 printable ASCII characters. If not set, a random key is generated on startup (sessions won't persist across restarts).
* `J_SESSION_NAME` - Cookie name for sessions, default `journal-session`
* `J_COOKIE_DOMAIN` - Domain restriction for cookies, default is current domain only
* `J_COOKIE_MAX_AGE` - Cookie expiry time in seconds, default `2592000` (30 days)
* `J_COOKIE_HTTPONLY` - Set to `0` or `false` to allow JavaScript access to cookies (not recommended). Default is `true` for XSS protection.

**Note:** When `J_SSL_CERT` is configured, session cookies automatically use the `Secure` flag to prevent transmission over unencrypted connections.

## Layout

The project layout follows the standard set out in the following document:
Expand Down
56 changes: 56 additions & 0 deletions internal/app/app.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package app

import (
"crypto/rand"
"database/sql"
"encoding/hex"
"log"
"os"
"strconv"

Expand Down Expand Up @@ -46,6 +49,12 @@ type Configuration struct {
Theme string
ThemePath string
Title string
SessionKey string
SessionName string
CookieDomain string
CookieMaxAge int
CookieSecure bool
CookieHTTPOnly bool
}

// DefaultConfiguration returns the default settings for the app
Expand All @@ -65,6 +74,12 @@ func DefaultConfiguration() Configuration {
Theme: "default",
ThemePath: "web/themes",
Title: "Jamie's Journal",
SessionKey: "",
SessionName: "journal-session",
CookieDomain: "",
CookieMaxAge: 2592000,
CookieSecure: false,
CookieHTTPOnly: true,
}
}

Expand Down Expand Up @@ -101,6 +116,47 @@ func ApplyEnvConfiguration(config *Configuration) {
}
config.SSLCertificate = os.Getenv("J_SSL_CERT")
config.SSLKey = os.Getenv("J_SSL_KEY")

sessionKey := os.Getenv("J_SESSION_KEY")
if sessionKey != "" {
if len(sessionKey) != 32 {
log.Println("WARNING: J_SESSION_KEY must be exactly 32 bytes. Using auto-generated key instead.")
sessionKey = ""
}
}
if sessionKey == "" {
bytes := make([]byte, 16)
if _, err := rand.Read(bytes); err == nil {
sessionKey = hex.EncodeToString(bytes)
log.Println("WARNING: J_SESSION_KEY not set or invalid. Using auto-generated key. Sessions will not persist across restarts.")
}
}
config.SessionKey = sessionKey

sessionName := os.Getenv("J_SESSION_NAME")
if sessionName != "" {
config.SessionName = sessionName
}

cookieDomain := os.Getenv("J_COOKIE_DOMAIN")
if cookieDomain != "" {
config.CookieDomain = cookieDomain
}

cookieMaxAge, _ := strconv.Atoi(os.Getenv("J_COOKIE_MAX_AGE"))
if cookieMaxAge > 0 {
config.CookieMaxAge = cookieMaxAge
}

cookieHTTPOnly := os.Getenv("J_COOKIE_HTTPONLY")
if cookieHTTPOnly == "0" || cookieHTTPOnly == "false" {
config.CookieHTTPOnly = false
}

if config.SSLCertificate != "" {
config.CookieSecure = true
}

staticPath := os.Getenv("J_STATIC_PATH")
if staticPath != "" {
config.StaticPath = staticPath
Expand Down
Loading