diff --git a/.gitignore b/.gitignore
index 4be5802a..e94d0693 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
*.exe
/*-poc
/*.sqlite
+
+/flamenco-worker.yaml
+/flamenco-worker-credentials.yaml
diff --git a/cmd/flamenco-manager-poc/main.go b/cmd/flamenco-manager-poc/main.go
index 30048b90..d6e1275c 100644
--- a/cmd/flamenco-manager-poc/main.go
+++ b/cmd/flamenco-manager-poc/main.go
@@ -71,14 +71,14 @@ func main() {
log.Fatal().Err(err).Msg("error loading job compilers")
}
flamenco := api_impl.NewFlamenco(compiler, persist)
- e := buildWebService(flamenco)
+ e := buildWebService(flamenco, persist)
// Start the web server.
finalErr := e.Start(listen)
log.Warn().Err(finalErr).Msg("shutting down")
}
-func buildWebService(flamenco api.ServerInterface) *echo.Echo {
+func buildWebService(flamenco api.ServerInterface, persist api_impl.PersistenceService) *echo.Echo {
e := echo.New()
e.HideBanner = true
@@ -99,7 +99,9 @@ func buildWebService(flamenco api.ServerInterface) *echo.Echo {
validator := oapi_middle.OapiRequestValidatorWithOptions(swagger,
&oapi_middle.Options{
Options: openapi3filter.Options{
- AuthenticationFunc: authenticator,
+ AuthenticationFunc: func(ctx context.Context, authInfo *openapi3filter.AuthenticationInput) error {
+ return api_impl.WorkerAuth(ctx, authInfo, persist)
+ },
},
// Skip OAPI validation when the request is not for the OAPI interface.
diff --git a/flamenco-worker-credentials.yaml b/flamenco-worker-credentials.yaml
deleted file mode 100644
index f829954f..00000000
--- a/flamenco-worker-credentials.yaml
+++ /dev/null
@@ -1,9 +0,0 @@
-# Credentials file for Flamenco Worker.
-# For an explanation of the fields, refer to flamenco-worker-example.yaml
-#
-# NOTE: this file can be overwritten by Flamenco Worker.
-#
-# This file was written on 2022-01-31 14:49:14 +01:00
-
-worker_id: 9b41b767-74de-4cac-9604-f3521821d68d
-worker_secret: 9b4d6b672181d29dfc65ceb59ff7aba3aab3e5bd9cac5d50342eb3a7217b0085
diff --git a/flamenco-worker.yaml b/flamenco-worker.yaml
deleted file mode 100644
index 68be88d9..00000000
--- a/flamenco-worker.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
-# Configuration file for Flamenco Worker.
-# For an explanation of the fields, refer to flamenco-worker-example.yaml
-#
-# NOTE: this file can be overwritten by Flamenco Worker.
-#
-# This file was written on 2022-01-31 14:45:36 +01:00
-
-manager_url: "http://localhost:8080/"
-task_types:
-- sleep
-- blender-render
-- file-management
-- exr-merge
-- debug
diff --git a/go.mod b/go.mod
index b79c3e43..96ef5a82 100644
--- a/go.mod
+++ b/go.mod
@@ -19,6 +19,7 @@ require (
github.com/shopspring/decimal v1.2.0 // indirect
github.com/stretchr/testify v1.7.0
github.com/ziflex/lecho/v3 v3.1.0
+ golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e
golang.org/x/net v0.0.0-20211013171255-e13a2654a71e
gopkg.in/yaml.v2 v2.4.0
gorm.io/driver/postgres v1.0.8
diff --git a/internal/manager/api_impl/logging.go b/internal/manager/api_impl/logging.go
index e6d2e246..0e59d8cd 100644
--- a/internal/manager/api_impl/logging.go
+++ b/internal/manager/api_impl/logging.go
@@ -1,5 +1,25 @@
package api_impl
+/* ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * Original Code Copyright (C) 2022 Blender Foundation.
+ *
+ * This file is part of Flamenco.
+ *
+ * Flamenco is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * Flamenco is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * Flamenco. If not, see .
+ *
+ * ***** END GPL LICENSE BLOCK ***** */
+
import (
"context"
diff --git a/internal/manager/api_impl/worker_auth.go b/internal/manager/api_impl/worker_auth.go
new file mode 100644
index 00000000..54a5375a
--- /dev/null
+++ b/internal/manager/api_impl/worker_auth.go
@@ -0,0 +1,79 @@
+package api_impl
+
+/* ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * Original Code Copyright (C) 2022 Blender Foundation.
+ *
+ * This file is part of Flamenco.
+ *
+ * Flamenco is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * Flamenco is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * Flamenco. If not, see .
+ *
+ * ***** END GPL LICENSE BLOCK ***** */
+
+import (
+ "context"
+ "errors"
+
+ oapi_middle "github.com/deepmap/oapi-codegen/pkg/middleware"
+ "github.com/getkin/kin-openapi/openapi3filter"
+ "github.com/labstack/echo/v4"
+ "golang.org/x/crypto/bcrypt"
+)
+
+type workerContextKey string
+
+const (
+ workerKey = workerContextKey("worker")
+)
+
+var (
+ errAuthBad = errors.New("no such worker known")
+)
+
+// OpenAPI authentication function for authing workers.
+// The worker will be fetched from the database and stored in the request context.
+func WorkerAuth(ctx context.Context, authInfo *openapi3filter.AuthenticationInput, persist PersistenceService) error {
+ echo := ctx.Value(oapi_middle.EchoContextKey).(echo.Context)
+ req := echo.Request()
+ logger := requestLogger(echo)
+
+ // Fetch username & password from the HTTP header.
+ u, p, ok := req.BasicAuth()
+ logger.Debug().Interface("scheme", authInfo.SecuritySchemeName).Str("user", u).Msg("authenticator")
+ if !ok {
+ return authInfo.NewError(errors.New("no auth header found"))
+ }
+
+ // Fetch the Worker that has this username, making sure there is always _some_
+ // secret to check. This helps in making this a constant-time operation.
+ var hashedSecret string
+ w, err := persist.FetchWorker(ctx, u)
+ if err == nil {
+ hashedSecret = w.Secret
+ } else {
+ hashedSecret = "this is not a BCrypt hash, so it'll fail"
+ }
+
+ // Check the password.
+ err = bcrypt.CompareHashAndPassword([]byte(hashedSecret), []byte(p))
+ if err != nil {
+ logger.Warn().Str("username", u).Msg("authentication error")
+ return authInfo.NewError(errAuthBad)
+ }
+
+ // Store the Worker in the request context, so that it doesn't need to be fetched again later.
+ reqCtx := context.WithValue(req.Context(), workerKey, w)
+ echo.SetRequest(req.WithContext(reqCtx))
+
+ return nil
+}
diff --git a/internal/manager/api_impl/workers.go b/internal/manager/api_impl/workers.go
index 82e22dda..e11cbfcc 100644
--- a/internal/manager/api_impl/workers.go
+++ b/internal/manager/api_impl/workers.go
@@ -26,6 +26,7 @@ import (
"github.com/google/uuid"
"github.com/labstack/echo/v4"
+ "golang.org/x/crypto/bcrypt"
"gitlab.com/blender/flamenco-ng-poc/internal/manager/persistence"
"gitlab.com/blender/flamenco-ng-poc/pkg/api"
@@ -46,10 +47,16 @@ func (f *Flamenco) RegisterWorker(e echo.Context) error {
logger.Info().Str("nickname", req.Nickname).Msg("registering new worker")
+ hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Secret), bcrypt.DefaultCost)
+ if err != nil {
+ logger.Warn().Err(err).Msg("error hashing worker password")
+ return sendAPIError(e, http.StatusBadRequest, "error hashing password")
+ }
+
dbWorker := persistence.Worker{
UUID: uuid.New().String(),
Name: req.Nickname,
- Secret: req.Secret,
+ Secret: string(hashedPassword),
Platform: req.Platform,
Address: e.RealIP(),
SupportedTaskTypes: strings.Join(req.SupportedTaskTypes, ","),