Worker authentication is working

This commit is contained in:
Sybren A. Stüvel 2022-01-31 15:27:13 +01:00
parent 7c14b2648d
commit d880f7e7f0
8 changed files with 116 additions and 27 deletions

3
.gitignore vendored

@ -1,3 +1,6 @@
*.exe *.exe
/*-poc /*-poc
/*.sqlite /*.sqlite
/flamenco-worker.yaml
/flamenco-worker-credentials.yaml

@ -71,14 +71,14 @@ func main() {
log.Fatal().Err(err).Msg("error loading job compilers") log.Fatal().Err(err).Msg("error loading job compilers")
} }
flamenco := api_impl.NewFlamenco(compiler, persist) flamenco := api_impl.NewFlamenco(compiler, persist)
e := buildWebService(flamenco) e := buildWebService(flamenco, persist)
// Start the web server. // Start the web server.
finalErr := e.Start(listen) finalErr := e.Start(listen)
log.Warn().Err(finalErr).Msg("shutting down") 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 := echo.New()
e.HideBanner = true e.HideBanner = true
@ -99,7 +99,9 @@ func buildWebService(flamenco api.ServerInterface) *echo.Echo {
validator := oapi_middle.OapiRequestValidatorWithOptions(swagger, validator := oapi_middle.OapiRequestValidatorWithOptions(swagger,
&oapi_middle.Options{ &oapi_middle.Options{
Options: openapi3filter.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. // Skip OAPI validation when the request is not for the OAPI interface.

@ -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

@ -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

1
go.mod

@ -19,6 +19,7 @@ require (
github.com/shopspring/decimal v1.2.0 // indirect github.com/shopspring/decimal v1.2.0 // indirect
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
github.com/ziflex/lecho/v3 v3.1.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 golang.org/x/net v0.0.0-20211013171255-e13a2654a71e
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
gorm.io/driver/postgres v1.0.8 gorm.io/driver/postgres v1.0.8

@ -1,5 +1,25 @@
package api_impl 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 <https://www.gnu.org/licenses/>.
*
* ***** END GPL LICENSE BLOCK ***** */
import ( import (
"context" "context"

@ -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 <https://www.gnu.org/licenses/>.
*
* ***** 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
}

@ -26,6 +26,7 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
"github.com/labstack/echo/v4" "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/internal/manager/persistence"
"gitlab.com/blender/flamenco-ng-poc/pkg/api" "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") 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{ dbWorker := persistence.Worker{
UUID: uuid.New().String(), UUID: uuid.New().String(),
Name: req.Nickname, Name: req.Nickname,
Secret: req.Secret, Secret: string(hashedPassword),
Platform: req.Platform, Platform: req.Platform,
Address: e.RealIP(), Address: e.RealIP(),
SupportedTaskTypes: strings.Join(req.SupportedTaskTypes, ","), SupportedTaskTypes: strings.Join(req.SupportedTaskTypes, ","),