Auto session storage key (#1767)
This commit is contained in:
@@ -105,11 +105,8 @@ Dozzle follows the [12-factor](https://12factor.net/) model. Configurations can
|
|||||||
| `--filter` | `DOZZLE_FILTER` | `""` |
|
| `--filter` | `DOZZLE_FILTER` | `""` |
|
||||||
| `--username` | `DOZZLE_USERNAME` | `""` |
|
| `--username` | `DOZZLE_USERNAME` | `""` |
|
||||||
| `--password` | `DOZZLE_PASSWORD` | `""` |
|
| `--password` | `DOZZLE_PASSWORD` | `""` |
|
||||||
| `--key` | `DOZZLE_KEY` | `""` |
|
|
||||||
| `--no-analytics` | `DOZZLE_NO_ANALYTICS` | false |
|
| `--no-analytics` | `DOZZLE_NO_ANALYTICS` | false |
|
||||||
|
|
||||||
Note: When using username and password `DOZZLE_KEY` is required for session management.
|
|
||||||
|
|
||||||
## Troubleshooting and FAQs
|
## Troubleshooting and FAQs
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|||||||
6
main.go
6
main.go
@@ -29,7 +29,6 @@ type args struct {
|
|||||||
Base string `arg:"env:DOZZLE_BASE" default:"/" help:"sets the base for http router."`
|
Base string `arg:"env:DOZZLE_BASE" default:"/" help:"sets the base for http router."`
|
||||||
Level string `arg:"env:DOZZLE_LEVEL" default:"info" help:"set Dozzle log level. Use debug for more logging."`
|
Level string `arg:"env:DOZZLE_LEVEL" default:"info" help:"set Dozzle log level. Use debug for more logging."`
|
||||||
TailSize int `arg:"env:DOZZLE_TAILSIZE" default:"300" help:"update the initial tail size when fetching logs."`
|
TailSize int `arg:"env:DOZZLE_TAILSIZE" default:"300" help:"update the initial tail size when fetching logs."`
|
||||||
Key string `arg:"env:DOZZLE_KEY" help:"set a random key for username and password. This is required for auth."`
|
|
||||||
Username string `arg:"env:DOZZLE_USERNAME" help:"sets the username for auth."`
|
Username string `arg:"env:DOZZLE_USERNAME" help:"sets the username for auth."`
|
||||||
Password string `arg:"env:DOZZLE_PASSWORD" help:"sets password for auth"`
|
Password string `arg:"env:DOZZLE_PASSWORD" help:"sets password for auth"`
|
||||||
NoAnalytics bool `arg:"--no-analytics,env:DOZZLE_NO_ANALYTICS" help:"disables anonymous analytics"`
|
NoAnalytics bool `arg:"--no-analytics,env:DOZZLE_NO_ANALYTICS" help:"disables anonymous analytics"`
|
||||||
@@ -88,10 +87,6 @@ func main() {
|
|||||||
if args.Username == "" || args.Password == "" {
|
if args.Username == "" || args.Password == "" {
|
||||||
log.Fatalf("Username AND password are required for authentication")
|
log.Fatalf("Username AND password are required for authentication")
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.Key == "" {
|
|
||||||
log.Fatalf("Key is required for authentication")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config := web.Config{
|
config := web.Config{
|
||||||
@@ -99,7 +94,6 @@ func main() {
|
|||||||
Base: args.Base,
|
Base: args.Base,
|
||||||
Version: version,
|
Version: version,
|
||||||
TailSize: args.TailSize,
|
TailSize: args.TailSize,
|
||||||
Key: args.Key,
|
|
||||||
Username: args.Username,
|
Username: args.Username,
|
||||||
Password: args.Password,
|
Password: args.Password,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -17,7 +19,7 @@ const sessionName = "session"
|
|||||||
func initializeAuth(h *handler) {
|
func initializeAuth(h *handler) {
|
||||||
secured = false
|
secured = false
|
||||||
if h.config.Username != "" && h.config.Password != "" {
|
if h.config.Username != "" && h.config.Password != "" {
|
||||||
store = sessions.NewCookieStore([]byte(h.config.Key))
|
store = sessions.NewCookieStore(generateSessionStorageKey(h.config.Username, h.config.Password))
|
||||||
store.Options.HttpOnly = true
|
store.Options.HttpOnly = true
|
||||||
store.Options.SameSite = http.SameSiteLaxMode
|
store.Options.SameSite = http.SameSiteLaxMode
|
||||||
store.Options.MaxAge = 0
|
store.Options.MaxAge = 0
|
||||||
@@ -115,3 +117,8 @@ func (h *handler) clearSession(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
http.Redirect(w, r, h.config.Base, http.StatusTemporaryRedirect)
|
http.Redirect(w, r, h.config.Base, http.StatusTemporaryRedirect)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generateSessionStorageKey(username string, password string) []byte {
|
||||||
|
key := sha256.Sum256([]byte(fmt.Sprintf("%s:%s", username, password)))
|
||||||
|
return key[:]
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ type Config struct {
|
|||||||
Addr string
|
Addr string
|
||||||
Version string
|
Version string
|
||||||
TailSize int
|
TailSize int
|
||||||
Key string
|
|
||||||
Username string
|
Username string
|
||||||
Password string
|
Password string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ func Test_createRoutes_redirect_with_auth(t *testing.T) {
|
|||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
require.NoError(t, afero.WriteFile(fs, "index.html", []byte("index page"), 0644), "WriteFile should have no error.")
|
require.NoError(t, afero.WriteFile(fs, "index.html", []byte("index page"), 0644), "WriteFile should have no error.")
|
||||||
|
|
||||||
handler := createHandler(nil, afero.NewIOFS(fs), Config{Base: "/foobar", Username: "amir", Password: "password", Key: "key"})
|
handler := createHandler(nil, afero.NewIOFS(fs), Config{Base: "/foobar", Username: "amir", Password: "password"})
|
||||||
req, err := http.NewRequest("GET", "/foobar/", nil)
|
req, err := http.NewRequest("GET", "/foobar/", nil)
|
||||||
require.NoError(t, err, "NewRequest should not return an error.")
|
require.NoError(t, err, "NewRequest should not return an error.")
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
@@ -320,7 +320,7 @@ func Test_createRoutes_version(t *testing.T) {
|
|||||||
|
|
||||||
func Test_createRoutes_username_password(t *testing.T) {
|
func Test_createRoutes_username_password(t *testing.T) {
|
||||||
|
|
||||||
handler := createHandler(nil, nil, Config{Base: "/", Username: "amir", Password: "password", Key: "key"})
|
handler := createHandler(nil, nil, Config{Base: "/", Username: "amir", Password: "password"})
|
||||||
req, err := http.NewRequest("GET", "/", nil)
|
req, err := http.NewRequest("GET", "/", nil)
|
||||||
require.NoError(t, err, "NewRequest should not return an error.")
|
require.NoError(t, err, "NewRequest should not return an error.")
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
@@ -329,7 +329,7 @@ func Test_createRoutes_username_password(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_createRoutes_username_password_invalid(t *testing.T) {
|
func Test_createRoutes_username_password_invalid(t *testing.T) {
|
||||||
handler := createHandler(nil, nil, Config{Base: "/", Username: "amir", Password: "password", Key: "key"})
|
handler := createHandler(nil, nil, Config{Base: "/", Username: "amir", Password: "password"})
|
||||||
req, err := http.NewRequest("GET", "/api/logs/stream?id=123", nil)
|
req, err := http.NewRequest("GET", "/api/logs/stream?id=123", nil)
|
||||||
require.NoError(t, err, "NewRequest should not return an error.")
|
require.NoError(t, err, "NewRequest should not return an error.")
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
@@ -338,7 +338,7 @@ func Test_createRoutes_username_password_invalid(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_createRoutes_username_password_login_happy(t *testing.T) {
|
func Test_createRoutes_username_password_login_happy(t *testing.T) {
|
||||||
handler := createHandler(nil, nil, Config{Base: "/", Username: "amir", Password: "password", Key: "key"})
|
handler := createHandler(nil, nil, Config{Base: "/", Username: "amir", Password: "password"})
|
||||||
|
|
||||||
body := &bytes.Buffer{}
|
body := &bytes.Buffer{}
|
||||||
writer := multipart.NewWriter(body)
|
writer := multipart.NewWriter(body)
|
||||||
@@ -368,7 +368,7 @@ func Test_createRoutes_username_password_login_happy(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_createRoutes_username_password_login_failed(t *testing.T) {
|
func Test_createRoutes_username_password_login_failed(t *testing.T) {
|
||||||
handler := createHandler(nil, nil, Config{Base: "/", Username: "amir", Password: "password", Key: "key"})
|
handler := createHandler(nil, nil, Config{Base: "/", Username: "amir", Password: "password"})
|
||||||
|
|
||||||
body := &bytes.Buffer{}
|
body := &bytes.Buffer{}
|
||||||
writer := multipart.NewWriter(body)
|
writer := multipart.NewWriter(body)
|
||||||
@@ -398,7 +398,7 @@ func Test_createRoutes_username_password_valid_session(t *testing.T) {
|
|||||||
mockedClient := new(MockedClient)
|
mockedClient := new(MockedClient)
|
||||||
mockedClient.On("FindContainer", "123").Return(docker.Container{ID: "123"}, nil)
|
mockedClient.On("FindContainer", "123").Return(docker.Container{ID: "123"}, nil)
|
||||||
mockedClient.On("ContainerLogs", mock.Anything, "123", 0).Return(ioutil.NopCloser(strings.NewReader("test data")), io.EOF)
|
mockedClient.On("ContainerLogs", mock.Anything, "123", 0).Return(ioutil.NopCloser(strings.NewReader("test data")), io.EOF)
|
||||||
handler := createHandler(mockedClient, nil, Config{Base: "/", Username: "amir", Password: "password", Key: "key"})
|
handler := createHandler(mockedClient, nil, Config{Base: "/", Username: "amir", Password: "password"})
|
||||||
|
|
||||||
// Get cookie first
|
// Get cookie first
|
||||||
req, err := http.NewRequest("GET", "/api/logs/stream?id=123", nil)
|
req, err := http.NewRequest("GET", "/api/logs/stream?id=123", nil)
|
||||||
@@ -422,7 +422,7 @@ func Test_createRoutes_username_password_invalid_session(t *testing.T) {
|
|||||||
mockedClient := new(MockedClient)
|
mockedClient := new(MockedClient)
|
||||||
mockedClient.On("FindContainer", "123").Return(docker.Container{ID: "123"}, nil)
|
mockedClient.On("FindContainer", "123").Return(docker.Container{ID: "123"}, nil)
|
||||||
mockedClient.On("ContainerLogs", mock.Anything, "123", 0).Return(ioutil.NopCloser(strings.NewReader("test data")), io.EOF)
|
mockedClient.On("ContainerLogs", mock.Anything, "123", 0).Return(ioutil.NopCloser(strings.NewReader("test data")), io.EOF)
|
||||||
handler := createHandler(mockedClient, nil, Config{Base: "/", Username: "amir", Password: "password", Key: "key"})
|
handler := createHandler(mockedClient, nil, Config{Base: "/", Username: "amir", Password: "password"})
|
||||||
req, err := http.NewRequest("GET", "/api/logs/stream?id=123", nil)
|
req, err := http.NewRequest("GET", "/api/logs/stream?id=123", nil)
|
||||||
require.NoError(t, err, "NewRequest should not return an error.")
|
require.NoError(t, err, "NewRequest should not return an error.")
|
||||||
req.AddCookie(&http.Cookie{Name: "session", Value: "baddata"})
|
req.AddCookie(&http.Cookie{Name: "session", Value: "baddata"})
|
||||||
|
|||||||
Reference in New Issue
Block a user