diff --git a/.env b/.env index 0301d4b..a33da3e 100644 --- a/.env +++ b/.env @@ -1,4 +1,10 @@ -export JWT_PUBLIC_KEY="kzfYrYy+TzpanWZHJ5qSdMj5uKUWgq74BWhQG6copP0=" -export JWT_PRIVATE_KEY="dtPC/6dWJFKZK7KZ78CvWuynylOmjBFyMsUWArwmodOTN9itjL5POlqdZkcnmpJ0yPm4pRaCrvgFaFAbpyik/Q==" -export SESSION_KEY="67d829bf61dc5f87a73fd814e2c9f629" -export LDAP_ADMIN_PASSWORD="mashup" +# Base64 encoded Ed25519 keys (DO NOT USE THESE TWO IN PRODUCTION!) +# You can generate your own keypair using `go run utils/gen-keypair.go` +JWT_PUBLIC_KEY="kzfYrYy+TzpanWZHJ5qSdMj5uKUWgq74BWhQG6copP0=" +JWT_PRIVATE_KEY="dtPC/6dWJFKZK7KZ78CvWuynylOmjBFyMsUWArwmodOTN9itjL5POlqdZkcnmpJ0yPm4pRaCrvgFaFAbpyik/Q==" + +# Some random bytes used as secret for cookie-based sessions (DO NOT USE THIS ONE IN PRODUCTION) +SESSION_KEY="67d829bf61dc5f87a73fd814e2c9f629" + +# Password for the ldap server (optional) +LDAP_ADMIN_PASSWORD="mashup" diff --git a/.gitignore b/.gitignore index 5877af3..aca72bb 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,8 @@ cc-jobarchive /var/job-archive /var/*.db +/var/machine-state + +/.env +/config.json diff --git a/README.md b/README.md index c92c171..e8e7a42 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # ClusterCockpit with a Golang backend -__*DOES NOT WORK WITH CURRENT FRONTEND*__ - [![Build](https://github.com/ClusterCockpit/cc-jobarchive/actions/workflows/test.yml/badge.svg)](https://github.com/ClusterCockpit/cc-jobarchive/actions/workflows/test.yml) Create your job-archive accoring to [this specification](https://github.com/ClusterCockpit/cc-specifications). At least one cluster with a valid `cluster.json` file is required. Having no jobs in the job-archive at all is fine. You may use the sample job-archive available for download [in cc-docker/develop](https://github.com/ClusterCockpit/cc-docker/tree/develop). @@ -30,7 +28,7 @@ touch ./var/job.db # EDIT THE .env FILE BEFORE YOU DEPLOY (Change the secrets)! # If authentication is disabled, it can be empty. -source .env +vim ./.env # This will first initialize the job.db database by traversing all # `meta.json` files in the job-archive and add a new user. `--no-server` will cause the diff --git a/auth/auth.go b/auth/auth.go index e463fd0..6282d94 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -22,6 +22,8 @@ import ( "golang.org/x/crypto/bcrypt" ) +// TODO: Properly do this "roles" stuff. +// Add a roles array and `user.HasRole(...)` functions. type User struct { Username string Password string @@ -191,6 +193,7 @@ func Login(db *sqlx.DB) http.Handler { return } + session.Options.MaxAge = 30 * 24 * 60 * 60 session.Values["username"] = user.Username session.Values["is_admin"] = user.IsAdmin if err := sessionStore.Save(r, rw, session); err != nil { @@ -239,6 +242,8 @@ func authViaToken(r *http.Request) (*User, error) { sub, _ := claims["sub"].(string) isAdmin, _ := claims["is_admin"].(bool) isAPIUser, _ := claims["is_api"].(bool) + + // TODO: Check if sub is still a valid user! return &User{ Username: sub, IsAdmin: isAdmin, @@ -258,6 +263,7 @@ func Auth(next http.Handler) http.Handler { return } if user != nil { + // Successfull authentication using a token ctx := context.WithValue(r.Context(), ContextUserKey, user) next.ServeHTTP(rw, r.WithContext(ctx)) return @@ -265,6 +271,7 @@ func Auth(next http.Handler) http.Handler { session, err := sessionStore.Get(r, "session") if err != nil { + // sessionStore.Get will return a new session if no current one is attached to this request. http.Error(rw, err.Error(), http.StatusInternalServerError) return } diff --git a/utils/gen-keypair.go b/utils/gen-keypair.go new file mode 100644 index 0000000..905817d --- /dev/null +++ b/utils/gen-keypair.go @@ -0,0 +1,22 @@ +package main + +import ( + "crypto/ed25519" + "crypto/rand" + "encoding/base64" + "fmt" + "os" +) + +func main() { + // rand.Reader uses /dev/urandom on Linux + pub, priv, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + fmt.Fprintf(os.Stderr, "error: %s\n", err.Error()) + os.Exit(1) + } + + fmt.Fprintf(os.Stdout, "JWT_PUBLIC_KEY=%#v\nJWT_PRIVATE_KEY=%#v\n", + base64.StdEncoding.EncodeToString(pub), + base64.StdEncoding.EncodeToString(priv)) +}