Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(auth): server side changes for new worker auth flow #790

Merged
merged 25 commits into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b339c4c
initial work
ecrupper Feb 28, 2023
746b399
Merge branch 'main' into worker-auth/complete
ecrupper Mar 9, 2023
8bf613c
adding symmetric token handling
ecrupper Mar 14, 2023
c1f1463
incorporating constants
ecrupper Mar 15, 2023
1da6bdd
convert admin register token endpoint to be POST
ecrupper Mar 15, 2023
4d7b735
pulling in types update
ecrupper Mar 21, 2023
e925301
Merge branch 'main' into worker-auth/complete
ecrupper Mar 21, 2023
e620fa3
add server mocks and json tags to check in resp
ecrupper Mar 21, 2023
febb17d
handle symmetric token in worker endpoints
ecrupper Mar 21, 2023
4a12e25
appease linter overlord
ecrupper Mar 21, 2023
25118c9
return properly when met with server worker token
ecrupper Mar 22, 2023
ad7db54
add proper return to update worker too
ecrupper Mar 22, 2023
467b47f
add back actually updating the worker
ecrupper Mar 22, 2023
f6dea85
add comments to create and update worker api func
ecrupper Mar 22, 2023
85c0efd
add new endpoint for refresh, remove plat admin access to worker auth…
ecrupper Mar 23, 2023
75d4c1f
mocks mocks mocks
ecrupper Mar 23, 2023
fa8ba2c
Merge branch 'main' into worker-auth/complete
ecrupper Mar 23, 2023
bbdcfc5
fix mock, fix status returns, move check in update to refresh
ecrupper Mar 23, 2023
2f49e8e
Merge branch 'worker-auth/complete' of github.com:go-vela/server into…
ecrupper Mar 23, 2023
7bc1b5c
update mock validate http method
ecrupper Mar 23, 2023
0014ccd
use authorization not token in mock
ecrupper Mar 23, 2023
077c267
retrieve plat admin for logging in register token + swagger updates
ecrupper Mar 23, 2023
66e3642
change return type of validate token
ecrupper Mar 23, 2023
b140651
getting mocked by mock
ecrupper Mar 23, 2023
05b7cd2
update validate token mock comment
ecrupper Mar 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions api/admin/worker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (c) 2023 Target Brands, Inc. All rights reserved.
//
// Use of this source code is governed by the LICENSE file in this repository.

package admin

import (
"fmt"
"net/http"

"github.com/go-vela/server/internal/token"
ecrupper marked this conversation as resolved.
Show resolved Hide resolved
"github.com/go-vela/server/util"
"github.com/go-vela/types/constants"
"github.com/go-vela/types/library"

"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)

// swagger:operation POST /api/v1/admin/workers/{worker}/register-token admin RegisterToken
//
// Get a worker registration token
//
// ---
// produces:
// - application/json
// parameters:
// - in: path
// name: worker
// description: Hostname of the worker
// required: true
// type: string
// security:
// - ApiKeyAuth: []
// responses:
// '200':
// description: Successfully generated registration token
// schema:
// "$ref": "#/definitions/Token"
// '401':
// description: Unauthorized
// schema:
// "$ref": "#/definitions/Error"

// RegisterToken represents the API handler to
// generate a registration token for onboarding a worker.
func RegisterToken(c *gin.Context) {
logrus.Info("Admin: generating registration token")
ecrupper marked this conversation as resolved.
Show resolved Hide resolved

host := util.PathParameter(c, "worker")

tm := c.MustGet("token-manager").(*token.Manager)
rmto := &token.MintTokenOpts{
Hostname: host,
TokenType: constants.WorkerRegisterTokenType,
ecrupper marked this conversation as resolved.
Show resolved Hide resolved
TokenDuration: tm.WorkerRegisterTokenDuration,
}

rt, err := tm.MintToken(rmto)
if err != nil {
retErr := fmt.Errorf("unable to generate registration token: %w", err)

util.HandleError(c, http.StatusInternalServerError, retErr)

return
}

c.JSON(http.StatusOK, library.Token{Token: &rt})
}
20 changes: 19 additions & 1 deletion api/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -1764,8 +1764,26 @@ func CancelBuild(c *gin.Context) {
return
}

tm := c.MustGet("token-manager").(*token.Manager)

// set mint token options
mto := &token.MintTokenOpts{
Hostname: "vela-server",
TokenType: constants.WorkerAuthTokenType,
ecrupper marked this conversation as resolved.
Show resolved Hide resolved
TokenDuration: time.Minute * 1,
}

// mint token
tkn, err := tm.MintToken(mto)
if err != nil {
retErr := fmt.Errorf("unable to generate auth token: %w", err)
util.HandleError(c, http.StatusInternalServerError, retErr)

return
}

// add the token to authenticate to the worker
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", c.MustGet("secret").(string)))
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", tkn))

// perform the request to the worker
resp, err := client.Do(req)
Expand Down
37 changes: 37 additions & 0 deletions api/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ package api
import (
"fmt"
"net/http"
"strings"

"github.com/go-vela/server/internal/token"
"github.com/go-vela/server/router/middleware/auth"
"github.com/go-vela/server/router/middleware/claims"
"github.com/go-vela/server/util"

"github.com/go-vela/types/library"
Expand Down Expand Up @@ -65,3 +67,38 @@ func RefreshAccessToken(c *gin.Context) {

c.JSON(http.StatusOK, library.Token{Token: &newAccessToken})
}

// swagger:operation GET /validate-token authenticate ValidateServerToken
//
// Validate a server token
//
// ---
// produces:
// - application/json
// security:
// - CookieAuth: []
// responses:
// '200':
// description: Successfully validated a token
// schema:
// type: string
ecrupper marked this conversation as resolved.
Show resolved Hide resolved
// '401':
// description: Unauthorized
// schema:
// "$ref": "#/definitions/Error"

// ValidateServerToken will return the claims of a valid server token
// if it is provided in the auth header.
func ValidateServerToken(c *gin.Context) {
cl := claims.Retrieve(c)

if !strings.EqualFold(cl.Subject, "vela-server") {
retErr := fmt.Errorf("token is not a valid server token")

util.HandleError(c, http.StatusUnauthorized, retErr)

return
}

c.JSON(http.StatusOK, cl)
}
140 changes: 136 additions & 4 deletions api/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import (
"fmt"
"net/http"

"github.com/go-vela/server/internal/token"
"github.com/go-vela/server/router/middleware/claims"
"github.com/go-vela/server/router/middleware/user"
"github.com/go-vela/types/constants"

"github.com/go-vela/server/database"
"github.com/go-vela/server/router/middleware/worker"
Expand Down Expand Up @@ -38,9 +41,9 @@ import (
// - ApiKeyAuth: []
// responses:
// '201':
// description: Successfully created the worker
// description: Successfully created the worker and retrieved auth token
// schema:
// type: string
// "$ref": "#definitions/Token"
// '400':
// description: Unable to create the worker
// schema:
Expand All @@ -55,6 +58,7 @@ import (
func CreateWorker(c *gin.Context) {
// capture middleware values
u := user.Retrieve(c)
cl := claims.Retrieve(c)

// capture body from API request
input := new(library.Worker)
Expand Down Expand Up @@ -85,7 +89,46 @@ func CreateWorker(c *gin.Context) {
return
}

c.JSON(http.StatusCreated, fmt.Sprintf("worker %s created", input.GetHostname()))
switch cl.TokenType {
// if symmetric token configured, send back symmetric token
case constants.ServerWorkerTokenType:
if secret, ok := c.Value("secret").(string); ok {
tkn := new(library.Token)
tkn.SetToken(secret)
c.JSON(http.StatusOK, tkn)

return
}

retErr := fmt.Errorf("symmetric token provided but not configured in server")
util.HandleError(c, http.StatusBadRequest, retErr)

return
// if worker register token, send back auth token
default:
tm := c.MustGet("token-manager").(*token.Manager)

wmto := &token.MintTokenOpts{
TokenType: constants.WorkerAuthTokenType,
TokenDuration: tm.WorkerAuthTokenDuration,
Hostname: cl.Subject,
}

tkn := new(library.Token)

wt, err := tm.MintToken(wmto)
if err != nil {
retErr := fmt.Errorf("unable to generate auth token for worker %s: %w", input.GetHostname(), err)

util.HandleError(c, http.StatusInternalServerError, retErr)

return
}

tkn.SetToken(wt)

c.JSON(http.StatusCreated, tkn)
}
}

// swagger:operation GET /api/v1/workers workers GetWorkers
Expand Down Expand Up @@ -226,7 +269,7 @@ func GetWorker(c *gin.Context) {
// "$ref": "#/definitions/Error"

// UpdateWorker represents the API handler to
// create a worker in the configured backend.
// update a worker in the configured backend.
func UpdateWorker(c *gin.Context) {
// capture middleware values
u := user.Retrieve(c)
Expand Down Expand Up @@ -288,6 +331,95 @@ func UpdateWorker(c *gin.Context) {
c.JSON(http.StatusOK, w)
}

// swagger:operation POST /api/v1/workers/{worker}/refresh workers RefreshWorkerAuth
//
// Refresh authorization token for worker
//
// ---
// produces:
// - application/json
// parameters:
// - in: path
// name: worker
// description: Name of the worker
// required: true
// type: string
// security:
// - ApiKeyAuth: []
// responses:
// '200':
// description: Successfully refreshed auth
// schema:
// "$ref": "#/definitions/Token"
// '400':
// description: Unable to refresh worker auth
// schema:
// "$ref": "#/definitions/Error"
// '404':
// description: Unable to refresh worker auth
// schema:
// "$ref": "#/definitions/Error"
// '500':
// description: Unable to refresh worker auth
// schema:
// "$ref": "#/definitions/Error"

// RefreshWorkerAuth represents the API handler to
// refresh the auth token for a worker.
func RefreshWorkerAuth(c *gin.Context) {
ecrupper marked this conversation as resolved.
Show resolved Hide resolved
// capture middleware values
w := worker.Retrieve(c)
cl := claims.Retrieve(c)

// update engine logger with API metadata
//
// https://pkg.go.dev/github.com/sirupsen/logrus?tab=doc#Entry.WithFields
logrus.WithFields(logrus.Fields{
"worker": w.GetHostname(),
}).Infof("refreshing worker %s authentication", w.GetHostname())

switch cl.TokenType {
ecrupper marked this conversation as resolved.
Show resolved Hide resolved
// if symmetric token configured, send back symmetric token
case constants.ServerWorkerTokenType:
if secret, ok := c.Value("secret").(string); ok {
tkn := new(library.Token)
tkn.SetToken(secret)
c.JSON(http.StatusOK, tkn)

return
}

retErr := fmt.Errorf("symmetric token provided but not configured in server")
util.HandleError(c, http.StatusBadRequest, retErr)

return
// if worker auth / register token, send back auth token
case constants.WorkerAuthTokenType, constants.WorkerRegisterTokenType:
tm := c.MustGet("token-manager").(*token.Manager)

wmto := &token.MintTokenOpts{
TokenType: constants.WorkerAuthTokenType,
ecrupper marked this conversation as resolved.
Show resolved Hide resolved
TokenDuration: tm.WorkerAuthTokenDuration,
Hostname: cl.Subject,
}

tkn := new(library.Token)

wt, err := tm.MintToken(wmto)
if err != nil {
retErr := fmt.Errorf("unable to generate auth token for worker %s: %w", w.GetHostname(), err)

util.HandleError(c, http.StatusInternalServerError, retErr)

return
}

tkn.SetToken(wt)

c.JSON(http.StatusCreated, tkn)
ecrupper marked this conversation as resolved.
Show resolved Hide resolved
}
}

// swagger:operation DELETE /api/v1/workers/{worker} workers DeleteWorker
//
// Delete a worker for the configured backend
Expand Down
12 changes: 12 additions & 0 deletions cmd/vela-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,18 @@ func main() {
Usage: "sets the duration of the buffer for build token expiration based on repo build timeout",
Value: 5 * time.Minute,
},
&cli.DurationFlag{
EnvVars: []string{"VELA_WORKER_AUTH_TOKEN_DURATION", "WORKER_AUTH_TOKEN_DURATION"},
Name: "worker-auth-token-duration",
Usage: "sets the duration of the worker auth token",
Value: 20 * time.Minute,
},
&cli.DurationFlag{
EnvVars: []string{"VELA_WORKER_REGISTER_TOKEN_DURATION", "WORKER_REGISTER_TOKEN_DURATION"},
Name: "worker-register-token-duration",
Usage: "sets the duration of the worker register token",
Value: 1 * time.Minute,
},
// Compiler Flags
&cli.BoolFlag{
EnvVars: []string{"VELA_COMPILER_GITHUB", "COMPILER_GITHUB"},
Expand Down
12 changes: 7 additions & 5 deletions cmd/vela-server/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ func setupTokenManager(c *cli.Context) *token.Manager {
logrus.Debug("Creating token manager from CLI configuration")

tm := &token.Manager{
PrivateKey: c.String("vela-server-private-key"),
SignMethod: jwt.SigningMethodHS256,
UserAccessTokenDuration: c.Duration("user-access-token-duration"),
UserRefreshTokenDuration: c.Duration("user-refresh-token-duration"),
BuildTokenBufferDuration: c.Duration("build-token-buffer-duration"),
PrivateKey: c.String("vela-server-private-key"),
SignMethod: jwt.SigningMethodHS256,
UserAccessTokenDuration: c.Duration("user-access-token-duration"),
UserRefreshTokenDuration: c.Duration("user-refresh-token-duration"),
BuildTokenBufferDuration: c.Duration("build-token-buffer-duration"),
WorkerAuthTokenDuration: c.Duration("worker-auth-token-duration"),
WorkerRegisterTokenDuration: c.Duration("worker-register-token-duration"),
}

return tm
Expand Down
4 changes: 0 additions & 4 deletions cmd/vela-server/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,6 @@ func validateCore(c *cli.Context) error {
return fmt.Errorf("clone-image (VELA_CLONE_IMAGE) flag is not properly configured")
}

if len(c.String("vela-secret")) == 0 {
return fmt.Errorf("vela-secret (VELA_SECRET) flag is not properly configured")
}

if len(c.String("vela-server-private-key")) == 0 {
return fmt.Errorf("vela-server-private-key (VELA_SERVER_PRIVATE_KEY) flag is not properly configured")
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ require (
github.com/drone/envsubst v1.0.3
github.com/gin-gonic/gin v1.9.0
github.com/go-playground/assert/v2 v2.2.0
github.com/go-vela/types v0.18.1
github.com/go-vela/types v0.18.2-0.20230321015315-6c723879639c
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/google/go-cmp v0.5.9
github.com/google/go-github/v50 v50.1.0
Expand Down
Loading