mirror of
https://github.com/thomiceli/opengist.git
synced 2024-12-22 20:42:40 +00:00
Allow to define secret key & move the secret key file to parent directory (#358)
This commit is contained in:
parent
20372f44e4
commit
4fd0832df9
11 changed files with 98 additions and 43 deletions
|
@ -14,6 +14,9 @@ external-url:
|
||||||
# Directory where Opengist will store its data. Default: ~/.opengist/
|
# Directory where Opengist will store its data. Default: ~/.opengist/
|
||||||
opengist-home:
|
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)
|
# URI of the database. Default: opengist.db (SQLite)
|
||||||
# SQLite: file name
|
# SQLite: file name
|
||||||
# PostgreSQL: postgres://user:password@host:port/database
|
# PostgreSQL: postgres://user:password@host:port/database
|
||||||
|
|
|
@ -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`. |
|
| 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. |
|
| 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. |
|
| 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. |
|
| 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.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. |
|
| index.dirname | OG_INDEX_DIRNAME | `opengist.index` | Name of the directory where the code search index is stored. |
|
||||||
|
|
|
@ -76,6 +76,8 @@ func Initialize(ctx *cli.Context) {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config.SetupSecretKey()
|
||||||
|
|
||||||
config.InitLog()
|
config.InitLog()
|
||||||
|
|
||||||
gitVersion, err := git.GetGitVersion()
|
gitVersion, err := git.GetGitVersion()
|
||||||
|
|
|
@ -27,6 +27,8 @@ var SecretKey []byte
|
||||||
// Not using nested structs because the library
|
// Not using nested structs because the library
|
||||||
// doesn't support dot notation in this case sadly
|
// doesn't support dot notation in this case sadly
|
||||||
type config struct {
|
type config struct {
|
||||||
|
SecretKey string `yaml:"secret-key" env:"OG_SECRET_KEY"`
|
||||||
|
|
||||||
LogLevel string `yaml:"log-level" env:"OG_LOG_LEVEL"`
|
LogLevel string `yaml:"log-level" env:"OG_LOG_LEVEL"`
|
||||||
LogOutput string `yaml:"log-output" env:"OG_LOG_OUTPUT"`
|
LogOutput string `yaml:"log-output" env:"OG_LOG_OUTPUT"`
|
||||||
ExternalUrl string `yaml:"external-url" env:"OG_EXTERNAL_URL"`
|
ExternalUrl string `yaml:"external-url" env:"OG_EXTERNAL_URL"`
|
||||||
|
@ -82,6 +84,8 @@ type StaticLink struct {
|
||||||
func configWithDefaults() (*config, error) {
|
func configWithDefaults() (*config, error) {
|
||||||
c := &config{}
|
c := &config{}
|
||||||
|
|
||||||
|
c.SecretKey = ""
|
||||||
|
|
||||||
c.LogLevel = "warn"
|
c.LogLevel = "warn"
|
||||||
c.LogOutput = "stdout,file"
|
c.LogOutput = "stdout,file"
|
||||||
c.OpengistHome = ""
|
c.OpengistHome = ""
|
||||||
|
@ -138,7 +142,9 @@ func InitConfig(configPath string, out io.Writer) error {
|
||||||
|
|
||||||
C = c
|
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 {
|
if err = os.Setenv("OG_OPENGIST_HOME_INTERNAL", GetHomeDir()); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -235,6 +241,15 @@ func GetHomeDir() string {
|
||||||
return filepath.Clean(absolutePath)
|
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 {
|
func loadConfigFromYaml(c *config, configPath string, out io.Writer) error {
|
||||||
if configPath != "" {
|
if configPath != "" {
|
||||||
absolutePath, _ := filepath.Abs(configPath)
|
absolutePath, _ := filepath.Abs(configPath)
|
||||||
|
|
42
internal/config/migrate.go
Normal file
42
internal/config/migrate.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
ogtotp "github.com/thomiceli/opengist/internal/auth/totp"
|
ogtotp "github.com/thomiceli/opengist/internal/auth/totp"
|
||||||
|
"github.com/thomiceli/opengist/internal/config"
|
||||||
"github.com/thomiceli/opengist/internal/utils"
|
"github.com/thomiceli/opengist/internal/utils"
|
||||||
"slices"
|
"slices"
|
||||||
)
|
)
|
||||||
|
@ -29,7 +30,7 @@ func GetTOTPByUserID(userID uint) (*TOTP, error) {
|
||||||
|
|
||||||
func (totp *TOTP) StoreSecret(secret string) error {
|
func (totp *TOTP) StoreSecret(secret string) error {
|
||||||
secretBytes := []byte(secret)
|
secretBytes := []byte(secret)
|
||||||
encrypted, err := utils.AESEncrypt([]byte("tmp"), secretBytes)
|
encrypted, err := utils.AESEncrypt(config.SecretKey, secretBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -44,7 +45,7 @@ func (totp *TOTP) ValidateCode(code string) (bool, error) {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
secretBytes, err := utils.AESDecrypt([]byte("tmp"), ciphertext)
|
secretBytes, err := utils.AESDecrypt(config.SecretKey, ciphertext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,12 @@ import (
|
||||||
"os"
|
"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)
|
key, err := os.ReadFile(filePath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return key
|
return key, false
|
||||||
}
|
}
|
||||||
|
|
||||||
key = securecookie.GenerateRandomKey(32)
|
key = securecookie.GenerateRandomKey(32)
|
||||||
|
@ -22,5 +24,5 @@ func GenerateSecretKey(filePath string) []byte {
|
||||||
log.Fatal().Err(err).Msgf("Failed to save the key to %s", filePath)
|
log.Fatal().Err(err).Msgf("Failed to save the key to %s", filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
return key
|
return key, true
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,10 +167,8 @@ type Server struct {
|
||||||
func NewServer(isDev bool, sessionsPath string) *Server {
|
func NewServer(isDev bool, sessionsPath string) *Server {
|
||||||
dev = isDev
|
dev = isDev
|
||||||
flashStore = sessions.NewCookieStore([]byte("opengist"))
|
flashStore = sessions.NewCookieStore([]byte("opengist"))
|
||||||
userStore = sessions.NewFilesystemStore(sessionsPath,
|
encryptKey, _ := utils.GenerateSecretKey(filepath.Join(sessionsPath, "session-encrypt.key"))
|
||||||
utils.GenerateSecretKey(path.Join(sessionsPath, "session-auth.key")),
|
userStore = sessions.NewFilesystemStore(sessionsPath, config.SecretKey, encryptKey)
|
||||||
utils.GenerateSecretKey(path.Join(sessionsPath, "session-encrypt.key")),
|
|
||||||
)
|
|
||||||
userStore.MaxLength(10 * 1024)
|
userStore.MaxLength(10 * 1024)
|
||||||
gothic.Store = userStore
|
gothic.Store = userStore
|
||||||
|
|
||||||
|
|
|
@ -12,12 +12,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRegister(t *testing.T) {
|
func TestRegister(t *testing.T) {
|
||||||
setup(t)
|
s := setup(t)
|
||||||
s, err := newTestServer()
|
|
||||||
require.NoError(t, err, "Failed to create test server")
|
|
||||||
defer teardown(t, s)
|
defer teardown(t, s)
|
||||||
|
|
||||||
err = s.request("GET", "/", nil, 302)
|
err := s.request("GET", "/", nil, 302)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = s.request("GET", "/register", nil, 200)
|
err = s.request("GET", "/register", nil, 200)
|
||||||
|
@ -55,12 +53,10 @@ func TestRegister(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLogin(t *testing.T) {
|
func TestLogin(t *testing.T) {
|
||||||
setup(t)
|
s := setup(t)
|
||||||
s, err := newTestServer()
|
|
||||||
require.NoError(t, err, "Failed to create test server")
|
|
||||||
defer teardown(t, s)
|
defer teardown(t, s)
|
||||||
|
|
||||||
err = s.request("GET", "/login", nil, 200)
|
err := s.request("GET", "/login", nil, 200)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
user1 := db.UserDTO{Username: "thomas", Password: "thomas"}
|
user1 := db.UserDTO{Username: "thomas", Password: "thomas"}
|
||||||
|
@ -101,15 +97,13 @@ type settingSet struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAnonymous(t *testing.T) {
|
func TestAnonymous(t *testing.T) {
|
||||||
setup(t)
|
s := setup(t)
|
||||||
s, err := newTestServer()
|
|
||||||
require.NoError(t, err, "Failed to create test server")
|
|
||||||
defer teardown(t, s)
|
defer teardown(t, s)
|
||||||
|
|
||||||
user := db.UserDTO{Username: "thomas", Password: "azeaze"}
|
user := db.UserDTO{Username: "thomas", Password: "azeaze"}
|
||||||
register(t, s, user)
|
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)
|
require.NoError(t, err)
|
||||||
|
|
||||||
gist1 := db.GistDTO{
|
gist1 := db.GistDTO{
|
||||||
|
@ -154,9 +148,7 @@ func TestAnonymous(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGitOperations(t *testing.T) {
|
func TestGitOperations(t *testing.T) {
|
||||||
setup(t)
|
s := setup(t)
|
||||||
s, err := newTestServer()
|
|
||||||
require.NoError(t, err, "Failed to create test server")
|
|
||||||
defer teardown(t, s)
|
defer teardown(t, s)
|
||||||
|
|
||||||
admin := db.UserDTO{Username: "thomas", Password: "thomas"}
|
admin := db.UserDTO{Username: "thomas", Password: "thomas"}
|
||||||
|
@ -178,7 +170,7 @@ func TestGitOperations(t *testing.T) {
|
||||||
"yeah",
|
"yeah",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err = s.request("POST", "/", gist1, 302)
|
err := s.request("POST", "/", gist1, 302)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
gist2 := db.GistDTO{
|
gist2 := db.GistDTO{
|
||||||
|
|
|
@ -9,12 +9,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGists(t *testing.T) {
|
func TestGists(t *testing.T) {
|
||||||
setup(t)
|
s := setup(t)
|
||||||
s, err := newTestServer()
|
|
||||||
require.NoError(t, err, "Failed to create test server")
|
|
||||||
defer teardown(t, s)
|
defer teardown(t, s)
|
||||||
|
|
||||||
err = s.request("GET", "/", nil, 302)
|
err := s.request("GET", "/", nil, 302)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
user1 := db.UserDTO{Username: "thomas", Password: "thomas"}
|
user1 := db.UserDTO{Username: "thomas", Password: "thomas"}
|
||||||
|
@ -106,9 +104,7 @@ func TestGists(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVisibility(t *testing.T) {
|
func TestVisibility(t *testing.T) {
|
||||||
setup(t)
|
s := setup(t)
|
||||||
s, err := newTestServer()
|
|
||||||
require.NoError(t, err, "Failed to create test server")
|
|
||||||
defer teardown(t, s)
|
defer teardown(t, s)
|
||||||
|
|
||||||
user1 := db.UserDTO{Username: "thomas", Password: "thomas"}
|
user1 := db.UserDTO{Username: "thomas", Password: "thomas"}
|
||||||
|
@ -123,7 +119,7 @@ func TestVisibility(t *testing.T) {
|
||||||
Name: []string{""},
|
Name: []string{""},
|
||||||
Content: []string{"yeah"},
|
Content: []string{"yeah"},
|
||||||
}
|
}
|
||||||
err = s.request("POST", "/", gist1, 302)
|
err := s.request("POST", "/", gist1, 302)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
gist1db, err := db.GetGistByID("1")
|
gist1db, err := db.GetGistByID("1")
|
||||||
|
@ -150,9 +146,7 @@ func TestVisibility(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLikeFork(t *testing.T) {
|
func TestLikeFork(t *testing.T) {
|
||||||
setup(t)
|
s := setup(t)
|
||||||
s, err := newTestServer()
|
|
||||||
require.NoError(t, err, "Failed to create test server")
|
|
||||||
defer teardown(t, s)
|
defer teardown(t, s)
|
||||||
|
|
||||||
user1 := db.UserDTO{Username: "thomas", Password: "thomas"}
|
user1 := db.UserDTO{Username: "thomas", Password: "thomas"}
|
||||||
|
@ -167,7 +161,7 @@ func TestLikeFork(t *testing.T) {
|
||||||
Name: []string{""},
|
Name: []string{""},
|
||||||
Content: []string{"yeah"},
|
Content: []string{"yeah"},
|
||||||
}
|
}
|
||||||
err = s.request("POST", "/", gist1, 302)
|
err := s.request("POST", "/", gist1, 302)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
s.sessionCookie = ""
|
s.sessionCookie = ""
|
||||||
|
@ -211,9 +205,7 @@ func TestLikeFork(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCustomUrl(t *testing.T) {
|
func TestCustomUrl(t *testing.T) {
|
||||||
setup(t)
|
s := setup(t)
|
||||||
s, err := newTestServer()
|
|
||||||
require.NoError(t, err, "Failed to create test server")
|
|
||||||
defer teardown(t, s)
|
defer teardown(t, s)
|
||||||
|
|
||||||
user1 := db.UserDTO{Username: "thomas", Password: "thomas"}
|
user1 := db.UserDTO{Username: "thomas", Password: "thomas"}
|
||||||
|
@ -229,7 +221,7 @@ func TestCustomUrl(t *testing.T) {
|
||||||
Name: []string{"gist1.txt", "gist2.txt", "gist3.txt"},
|
Name: []string{"gist1.txt", "gist2.txt", "gist3.txt"},
|
||||||
Content: []string{"yeah", "yeah\ncool", "yeah\ncool gist actually"},
|
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)
|
require.NoError(t, err)
|
||||||
|
|
||||||
gist1db, err := db.GetGistByID("1")
|
gist1db, err := db.GetGistByID("1")
|
||||||
|
|
|
@ -133,7 +133,7 @@ func structToURLValues(s interface{}) url.Values {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func setup(t *testing.T) {
|
func setup(t *testing.T) *testServer {
|
||||||
var databaseDsn string
|
var databaseDsn string
|
||||||
databaseType = os.Getenv("OPENGIST_TEST_DB")
|
databaseType = os.Getenv("OPENGIST_TEST_DB")
|
||||||
switch databaseType {
|
switch databaseType {
|
||||||
|
@ -153,6 +153,8 @@ func setup(t *testing.T) {
|
||||||
err = os.MkdirAll(filepath.Join(config.GetHomeDir()), 0755)
|
err = os.MkdirAll(filepath.Join(config.GetHomeDir()), 0755)
|
||||||
require.NoError(t, err, "Could not create Opengist home directory")
|
require.NoError(t, err, "Could not create Opengist home directory")
|
||||||
|
|
||||||
|
config.SetupSecretKey()
|
||||||
|
|
||||||
git.ReposDirectory = path.Join("tests")
|
git.ReposDirectory = path.Join("tests")
|
||||||
|
|
||||||
config.C.IndexEnabled = false
|
config.C.IndexEnabled = false
|
||||||
|
@ -180,6 +182,11 @@ func setup(t *testing.T) {
|
||||||
|
|
||||||
// err = index.Open(filepath.Join(homePath, "testsindex", "opengist.index"))
|
// err = index.Open(filepath.Join(homePath, "testsindex", "opengist.index"))
|
||||||
// require.NoError(t, err, "Could not open 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) {
|
func teardown(t *testing.T, s *testServer) {
|
||||||
|
|
Loading…
Reference in a new issue