Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to define secret key & move the secret key file to parent directory #358

Merged
merged 5 commits into from
Oct 31, 2024
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
3 changes: 3 additions & 0 deletions config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ external-url:
# Directory where Opengist will store its data. Default: ~/.opengist/
opengist-home:

# Secret key used for session store & encrypt MFA data on database. Default: <randomized 32 bytes>
secret-key:

# URI of the database. Default: opengist.db (SQLite)
# SQLite: file name
# PostgreSQL: postgres://user:password@host:port/database
Expand Down
1 change: 1 addition & 0 deletions docs/configuration/cheat-sheet.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ aside: false
| log-output | OG_LOG_OUTPUT | `stdout,file` | Set the log output to one or more of the following: `stdout`, `file`. |
| external-url | OG_EXTERNAL_URL | none | Public URL to access to Opengist. |
| opengist-home | OG_OPENGIST_HOME | home directory | Path to the directory where Opengist stores its data. |
| secret-key | OG_SECRET_KEY | randomized 32 bytes | Secret key used for session store & encrypt MFA data on database. |
| db-filename | OG_DB_FILENAME | `opengist.db` | Name of the SQLite database file. |
| index.enabled | OG_INDEX_ENABLED | `true` | Enable or disable the code search index (`true` or `false`) |
| index.dirname | OG_INDEX_DIRNAME | `opengist.index` | Name of the directory where the code search index is stored. |
Expand Down
2 changes: 2 additions & 0 deletions internal/cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ func Initialize(ctx *cli.Context) {
panic(err)
}

config.SetupSecretKey()

config.InitLog()

gitVersion, err := git.GetGitVersion()
Expand Down
17 changes: 16 additions & 1 deletion internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ var SecretKey []byte
// Not using nested structs because the library
// doesn't support dot notation in this case sadly
type config struct {
SecretKey string `yaml:"secret-key" env:"OG_SECRET_KEY"`

LogLevel string `yaml:"log-level" env:"OG_LOG_LEVEL"`
LogOutput string `yaml:"log-output" env:"OG_LOG_OUTPUT"`
ExternalUrl string `yaml:"external-url" env:"OG_EXTERNAL_URL"`
Expand Down Expand Up @@ -82,6 +84,8 @@ type StaticLink struct {
func configWithDefaults() (*config, error) {
c := &config{}

c.SecretKey = ""

c.LogLevel = "warn"
c.LogOutput = "stdout,file"
c.OpengistHome = ""
Expand Down Expand Up @@ -138,7 +142,9 @@ func InitConfig(configPath string, out io.Writer) error {

C = c

// SecretKey = utils.GenerateSecretKey(filepath.Join(GetHomeDir(), "opengist-secret.key"))
if err = migrateConfig(); err != nil {
return err
}

if err = os.Setenv("OG_OPENGIST_HOME_INTERNAL", GetHomeDir()); err != nil {
return err
Expand Down Expand Up @@ -235,6 +241,15 @@ func GetHomeDir() string {
return filepath.Clean(absolutePath)
}

func SetupSecretKey() {
if C.SecretKey == "" {
path := filepath.Join(GetHomeDir(), "opengist-secret.key")
SecretKey, _ = utils.GenerateSecretKey(path)
} else {
SecretKey = []byte(C.SecretKey)
}
}

func loadConfigFromYaml(c *config, configPath string, out io.Writer) error {
if configPath != "" {
absolutePath, _ := filepath.Abs(configPath)
Expand Down
42 changes: 42 additions & 0 deletions internal/config/migrate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package config

import (
"fmt"
"os"
"path/filepath"
)

// auto migration for newer versions of Opengist
func migrateConfig() error {
configMigrations := []struct {
Version string
Func func() error
}{
{"1.8.0", v1_8_0},
}

for _, fn := range configMigrations {
err := fn.Func()
if err != nil {
return err
}
}

return nil
}

func v1_8_0() error {
homeDir := GetHomeDir()
moveFile(filepath.Join(filepath.Join(homeDir, "sessions"), "session-auth.key"), filepath.Join(homeDir, "opengist-secret.key"))
return nil
}

func moveFile(oldPath, newPath string) {
if _, err := os.Stat(oldPath); err != nil {
return
}

if err := os.Rename(oldPath, newPath); err == nil {
fmt.Printf("Automatically moved %s to %s\n", oldPath, newPath)
}
}
5 changes: 3 additions & 2 deletions internal/db/totp.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"encoding/json"
"fmt"
ogtotp "github.com/thomiceli/opengist/internal/auth/totp"
"github.com/thomiceli/opengist/internal/config"
"github.com/thomiceli/opengist/internal/utils"
"slices"
)
Expand All @@ -29,7 +30,7 @@ func GetTOTPByUserID(userID uint) (*TOTP, error) {

func (totp *TOTP) StoreSecret(secret string) error {
secretBytes := []byte(secret)
encrypted, err := utils.AESEncrypt([]byte("tmp"), secretBytes)
encrypted, err := utils.AESEncrypt(config.SecretKey, secretBytes)
if err != nil {
return err
}
Expand All @@ -44,7 +45,7 @@ func (totp *TOTP) ValidateCode(code string) (bool, error) {
return false, err
}

secretBytes, err := utils.AESDecrypt([]byte("tmp"), ciphertext)
secretBytes, err := utils.AESDecrypt(config.SecretKey, ciphertext)
if err != nil {
return false, err
}
Expand Down
8 changes: 5 additions & 3 deletions internal/utils/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import (
"os"
)

func GenerateSecretKey(filePath string) []byte {
// GenerateSecretKey generates a new secret key for sessions
// Returns the key and a boolean indicating if the key was generated
func GenerateSecretKey(filePath string) ([]byte, bool) {
key, err := os.ReadFile(filePath)
if err == nil {
return key
return key, false
}

key = securecookie.GenerateRandomKey(32)
Expand All @@ -22,5 +24,5 @@ func GenerateSecretKey(filePath string) []byte {
log.Fatal().Err(err).Msgf("Failed to save the key to %s", filePath)
}

return key
return key, true
}
6 changes: 2 additions & 4 deletions internal/web/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,10 +167,8 @@ type Server struct {
func NewServer(isDev bool, sessionsPath string) *Server {
dev = isDev
flashStore = sessions.NewCookieStore([]byte("opengist"))
userStore = sessions.NewFilesystemStore(sessionsPath,
utils.GenerateSecretKey(path.Join(sessionsPath, "session-auth.key")),
utils.GenerateSecretKey(path.Join(sessionsPath, "session-encrypt.key")),
)
encryptKey, _ := utils.GenerateSecretKey(filepath.Join(sessionsPath, "session-encrypt.key"))
userStore = sessions.NewFilesystemStore(sessionsPath, config.SecretKey, encryptKey)
userStore.MaxLength(10 * 1024)
gothic.Store = userStore

Expand Down
24 changes: 8 additions & 16 deletions internal/web/test/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,10 @@ import (
)

func TestRegister(t *testing.T) {
setup(t)
s, err := newTestServer()
require.NoError(t, err, "Failed to create test server")
s := setup(t)
defer teardown(t, s)

err = s.request("GET", "/", nil, 302)
err := s.request("GET", "/", nil, 302)
require.NoError(t, err)

err = s.request("GET", "/register", nil, 200)
Expand Down Expand Up @@ -55,12 +53,10 @@ func TestRegister(t *testing.T) {
}

func TestLogin(t *testing.T) {
setup(t)
s, err := newTestServer()
require.NoError(t, err, "Failed to create test server")
s := setup(t)
defer teardown(t, s)

err = s.request("GET", "/login", nil, 200)
err := s.request("GET", "/login", nil, 200)
require.NoError(t, err)

user1 := db.UserDTO{Username: "thomas", Password: "thomas"}
Expand Down Expand Up @@ -101,15 +97,13 @@ type settingSet struct {
}

func TestAnonymous(t *testing.T) {
setup(t)
s, err := newTestServer()
require.NoError(t, err, "Failed to create test server")
s := setup(t)
defer teardown(t, s)

user := db.UserDTO{Username: "thomas", Password: "azeaze"}
register(t, s, user)

err = s.request("PUT", "/admin-panel/set-config", settingSet{"require-login", "1"}, 200)
err := s.request("PUT", "/admin-panel/set-config", settingSet{"require-login", "1"}, 200)
require.NoError(t, err)

gist1 := db.GistDTO{
Expand Down Expand Up @@ -154,9 +148,7 @@ func TestAnonymous(t *testing.T) {
}

func TestGitOperations(t *testing.T) {
setup(t)
s, err := newTestServer()
require.NoError(t, err, "Failed to create test server")
s := setup(t)
defer teardown(t, s)

admin := db.UserDTO{Username: "thomas", Password: "thomas"}
Expand All @@ -178,7 +170,7 @@ func TestGitOperations(t *testing.T) {
"yeah",
},
}
err = s.request("POST", "/", gist1, 302)
err := s.request("POST", "/", gist1, 302)
require.NoError(t, err)

gist2 := db.GistDTO{
Expand Down
24 changes: 8 additions & 16 deletions internal/web/test/gist_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,10 @@ import (
)

func TestGists(t *testing.T) {
setup(t)
s, err := newTestServer()
require.NoError(t, err, "Failed to create test server")
s := setup(t)
defer teardown(t, s)

err = s.request("GET", "/", nil, 302)
err := s.request("GET", "/", nil, 302)
require.NoError(t, err)

user1 := db.UserDTO{Username: "thomas", Password: "thomas"}
Expand Down Expand Up @@ -106,9 +104,7 @@ func TestGists(t *testing.T) {
}

func TestVisibility(t *testing.T) {
setup(t)
s, err := newTestServer()
require.NoError(t, err, "Failed to create test server")
s := setup(t)
defer teardown(t, s)

user1 := db.UserDTO{Username: "thomas", Password: "thomas"}
Expand All @@ -123,7 +119,7 @@ func TestVisibility(t *testing.T) {
Name: []string{""},
Content: []string{"yeah"},
}
err = s.request("POST", "/", gist1, 302)
err := s.request("POST", "/", gist1, 302)
require.NoError(t, err)

gist1db, err := db.GetGistByID("1")
Expand All @@ -150,9 +146,7 @@ func TestVisibility(t *testing.T) {
}

func TestLikeFork(t *testing.T) {
setup(t)
s, err := newTestServer()
require.NoError(t, err, "Failed to create test server")
s := setup(t)
defer teardown(t, s)

user1 := db.UserDTO{Username: "thomas", Password: "thomas"}
Expand All @@ -167,7 +161,7 @@ func TestLikeFork(t *testing.T) {
Name: []string{""},
Content: []string{"yeah"},
}
err = s.request("POST", "/", gist1, 302)
err := s.request("POST", "/", gist1, 302)
require.NoError(t, err)

s.sessionCookie = ""
Expand Down Expand Up @@ -211,9 +205,7 @@ func TestLikeFork(t *testing.T) {
}

func TestCustomUrl(t *testing.T) {
setup(t)
s, err := newTestServer()
require.NoError(t, err, "Failed to create test server")
s := setup(t)
defer teardown(t, s)

user1 := db.UserDTO{Username: "thomas", Password: "thomas"}
Expand All @@ -229,7 +221,7 @@ func TestCustomUrl(t *testing.T) {
Name: []string{"gist1.txt", "gist2.txt", "gist3.txt"},
Content: []string{"yeah", "yeah\ncool", "yeah\ncool gist actually"},
}
err = s.request("POST", "/", gist1, 302)
err := s.request("POST", "/", gist1, 302)
require.NoError(t, err)

gist1db, err := db.GetGistByID("1")
Expand Down
9 changes: 8 additions & 1 deletion internal/web/test/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func structToURLValues(s interface{}) url.Values {
return v
}

func setup(t *testing.T) {
func setup(t *testing.T) *testServer {
var databaseDsn string
databaseType = os.Getenv("OPENGIST_TEST_DB")
switch databaseType {
Expand All @@ -153,6 +153,8 @@ func setup(t *testing.T) {
err = os.MkdirAll(filepath.Join(config.GetHomeDir()), 0755)
require.NoError(t, err, "Could not create Opengist home directory")

config.SetupSecretKey()

git.ReposDirectory = path.Join("tests")

config.C.IndexEnabled = false
Expand Down Expand Up @@ -180,6 +182,11 @@ func setup(t *testing.T) {

// err = index.Open(filepath.Join(homePath, "testsindex", "opengist.index"))
// require.NoError(t, err, "Could not open index")

s, err := newTestServer()
require.NoError(t, err, "Failed to create test server")

return s
}

func teardown(t *testing.T, s *testServer) {
Expand Down
Loading