forked from Ivasoft/opds-proxy
feat: secure cookie keys config
If keys aren't specified they will be generated. The downside of this is that the keys will change every server restart which will invalidate previously generated cookies. If the keys are specified in the config.yml, they are used between restarts and all previously generated cookies remain valid.
This commit is contained in:
41
main.go
41
main.go
@@ -1,10 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/gorilla/securecookie"
|
||||
"github.com/knadh/koanf/parsers/yaml"
|
||||
"github.com/knadh/koanf/providers/basicflag"
|
||||
"github.com/knadh/koanf/providers/file"
|
||||
@@ -13,9 +16,15 @@ import (
|
||||
|
||||
type config struct {
|
||||
Port string `koanf:"port"`
|
||||
Auth auth `koanf:"auth"`
|
||||
Feeds []feedConfig `koanf:"feeds" `
|
||||
}
|
||||
|
||||
type auth struct {
|
||||
HashKey string `koanf:"hash_key"`
|
||||
BlockKey string `koanf:"block_key"`
|
||||
}
|
||||
|
||||
type feedConfig struct {
|
||||
Name string `koanf:"name"`
|
||||
Url string `koanf:"url"`
|
||||
@@ -27,7 +36,13 @@ func main() {
|
||||
fs := flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fs.String("port", "8080", "port to listen on")
|
||||
configPath := fs.String("config", "config.yml", "config file to load")
|
||||
generateKeys := fs.Bool("generate-keys", false, "generate cookie signing keys and exit")
|
||||
if err := fs.Parse(os.Args[1:]); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if *generateKeys {
|
||||
displayKeys()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
@@ -49,6 +64,30 @@ func main() {
|
||||
log.Fatal("No feeds defined in config")
|
||||
}
|
||||
|
||||
server := NewServer(&config)
|
||||
if config.Auth.HashKey == "" || config.Auth.BlockKey == "" {
|
||||
log.Println("Generating new cookie signing credentials")
|
||||
hashKey, blockKey := displayKeys()
|
||||
|
||||
config.Auth.HashKey = hashKey
|
||||
config.Auth.BlockKey = blockKey
|
||||
|
||||
}
|
||||
|
||||
server, err := NewServer(&config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
server.Serve()
|
||||
}
|
||||
|
||||
func displayKeys() (string, string) {
|
||||
hashKey := hex.EncodeToString(securecookie.GenerateRandomKey(32))
|
||||
blockKey := hex.EncodeToString(securecookie.GenerateRandomKey(32))
|
||||
|
||||
log.Println("Set these values in your config file to persist authentication between server restarts.")
|
||||
fmt.Println("auth:")
|
||||
fmt.Printf(" hash_key: %s\n", hashKey)
|
||||
fmt.Printf(" block_key: %s\n", blockKey)
|
||||
|
||||
return hashKey, blockKey
|
||||
}
|
||||
|
||||
30
server.go
30
server.go
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
@@ -34,6 +35,7 @@ var (
|
||||
type Server struct {
|
||||
addr string
|
||||
router *http.ServeMux
|
||||
s *securecookie.SecureCookie
|
||||
}
|
||||
|
||||
type Credentials struct {
|
||||
@@ -41,19 +43,29 @@ type Credentials struct {
|
||||
Password string
|
||||
}
|
||||
|
||||
var s = securecookie.New(securecookie.GenerateRandomKey(32), securecookie.GenerateRandomKey(32))
|
||||
func NewServer(config *config) (*Server, error) {
|
||||
hashKey, err := hex.DecodeString(config.Auth.HashKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockKey, err := hex.DecodeString(config.Auth.BlockKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := securecookie.New(hashKey, blockKey)
|
||||
|
||||
func NewServer(config *config) *Server {
|
||||
router := http.NewServeMux()
|
||||
router.HandleFunc("GET /{$}", handleHome(config.Feeds))
|
||||
router.HandleFunc("GET /feed", handleFeed("tmp/"))
|
||||
router.HandleFunc("/auth", handleAuth())
|
||||
router.HandleFunc("GET /feed", handleFeed("tmp/", s))
|
||||
router.HandleFunc("/auth", handleAuth(s))
|
||||
router.Handle("GET /static/", http.FileServer(http.FS(html.StaticFiles())))
|
||||
|
||||
return &Server{
|
||||
addr: ":" + config.Port,
|
||||
router: router,
|
||||
}
|
||||
s: s,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) Serve() {
|
||||
@@ -75,7 +87,7 @@ func handleHome(feeds []feedConfig) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func handleFeed(outputDir string) http.HandlerFunc {
|
||||
func handleFeed(outputDir string, s *securecookie.SecureCookie) http.HandlerFunc {
|
||||
kepubConverter := &convert.KepubConverter{}
|
||||
mobiConverter := &convert.MobiConverter{}
|
||||
|
||||
@@ -98,7 +110,7 @@ func handleFeed(outputDir string) http.HandlerFunc {
|
||||
queryURL = replaceSearchPlaceHolder(queryURL, searchTerm)
|
||||
}
|
||||
|
||||
resp, err := fetchFromUrl(queryURL, getCredentials(r))
|
||||
resp, err := fetchFromUrl(queryURL, getCredentials(r, s))
|
||||
if err != nil {
|
||||
handleError(r, w, "Failed to fetch", err)
|
||||
return
|
||||
@@ -166,7 +178,7 @@ func handleFeed(outputDir string) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func handleAuth() http.HandlerFunc {
|
||||
func handleAuth(s *securecookie.SecureCookie) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
returnUrl := r.URL.Query().Get("return")
|
||||
if returnUrl == "" {
|
||||
@@ -219,7 +231,7 @@ func handleAuth() http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func getCredentials(r *http.Request) *Credentials {
|
||||
func getCredentials(r *http.Request, s *securecookie.SecureCookie) *Credentials {
|
||||
cookie, err := r.Cookie("auth-creds")
|
||||
if err != nil {
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user